mirror of
https://github.com/lubosz/overte.git
synced 2025-04-15 15:19:36 +02:00
Merge branch 'master' into fix/spinbox
This commit is contained in:
commit
7088fc9d6c
357 changed files with 10444 additions and 4447 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
|
6
.github/workflows/master_build.yml
vendored
6
.github/workflows/master_build.yml
vendored
|
@ -83,15 +83,15 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo "ARTIFACT_PATTERN=Vircadia-Alpha-*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
echo "ARTIFACT_PATTERN=Vircadia-*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
# Build type variables
|
||||
echo "GIT_COMMIT_SHORT=${{ steps.buildenv1.outputs.github_sha_short }}" >> $GITHUB_ENV
|
||||
if [ "${{ matrix.build_type }}" = "full" ]; then
|
||||
echo "CLIENT_ONLY=FALSE" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-Alpha-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
else
|
||||
echo "CLIENT_ONLY=TRUE" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-Alpha-Interface-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-Interface-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Clear working directory
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
|
|
4
.github/workflows/pr_build.yml
vendored
4
.github/workflows/pr_build.yml
vendored
|
@ -86,9 +86,9 @@ jobs:
|
|||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo "GIT_COMMIT_SHORT=${{ steps.buildenv1.outputs.github_sha_short }}" >> $GITHUB_ENV
|
||||
if [[ "${{ matrix.build_type }}" != "android" ]]; then
|
||||
echo "ARTIFACT_PATTERN=Vircadia-Alpha-PR${{ github.event.number }}-*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
echo "ARTIFACT_PATTERN=Vircadia-PR${{ github.event.number }}-*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
# Build type variables
|
||||
echo "INSTALLER=Vircadia-Alpha-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
else
|
||||
echo "ARTIFACT_PATTERN=*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
fi
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -111,3 +111,4 @@ tools/unity-avatar-exporter
|
|||
server-console/package-lock.json
|
||||
vcpkg/
|
||||
/tools/nitpick/compiledResources
|
||||
qt/
|
101
BUILD.md
101
BUILD.md
|
@ -1,6 +1,6 @@
|
|||
# General Build Information
|
||||
|
||||
*Last Updated on August 26, 2020*
|
||||
*Last Updated on December 21, 2020*
|
||||
|
||||
### OS Specific Build Guides
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
|||
|
||||
### Dependencies
|
||||
- [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
|
||||
- [Node.JS](https://nodejs.org/en/): >= 12.13.1 LTS
|
||||
- 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.
|
||||
|
||||
#### 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.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.12.3/gcc_64/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/clang_64/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
|
||||
```bash
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.12.3/gcc_64/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/clang_64/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
|
||||
|
||||
|
@ -59,56 +62,68 @@ 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.
|
||||
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:
|
||||
|
||||
```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.
|
||||
|
||||
#### Generating Build Files
|
||||
|
||||
##### Possible Environment Variables
|
||||
|
||||
// The URL to post the dump to.
|
||||
CMAKE_BACKTRACE_URL
|
||||
// The identifying tag of the release.
|
||||
CMAKE_BACKTRACE_TOKEN
|
||||
|
||||
// The release version.
|
||||
RELEASE_NUMBER
|
||||
// The build commit.
|
||||
BUILD_NUMBER
|
||||
```text
|
||||
// The URL to post the dump to.
|
||||
CMAKE_BACKTRACE_URL
|
||||
// The identifying tag of the release.
|
||||
CMAKE_BACKTRACE_TOKEN
|
||||
|
||||
// The release version, e.g., 2021.3.2.
|
||||
RELEASE_NUMBER
|
||||
// The release name, e.g., Eos.
|
||||
RELEASE_NAME
|
||||
// The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b.
|
||||
|
||||
BUILD_NUMBER
|
||||
|
||||
// The type of release.
|
||||
RELEASE_TYPE=PRODUCTION|PR|DEV
|
||||
|
||||
// The Interface will have a custom default home and startup location.
|
||||
PRELOADED_STARTUP_LOCATION=Location/IP/URL
|
||||
// The Interface will have a custom default script whitelist, comma separated, no spaces.
|
||||
// This will also activate the whitelist on Interface's first run.
|
||||
PRELOADED_SCRIPT_WHITELIST=ListOfEntries
|
||||
|
||||
// Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place.
|
||||
HF_PFX_FILE=Path to certificate
|
||||
HF_PFX_PASSPHRASE=Passphrase for certificate
|
||||
|
||||
// Determine the build type
|
||||
PRODUCTION_BUILD=0|1
|
||||
PR_BUILD=0|1
|
||||
STABLE_BUILD=0|1
|
||||
|
||||
// Determine if to utilize testing or stable Metaverse URLs
|
||||
USE_STABLE_GLOBAL_SERVICES=1
|
||||
BUILD_GLOBAL_SERVICES=STABLE
|
||||
```
|
||||
|
||||
// The type of release.
|
||||
RELEASE_TYPE=PRODUCTION|PR|DEV
|
||||
|
||||
// The Interface will have a custom default home and startup location.
|
||||
INITIAL_STARTUP_LOCATION=Location/IP/URL
|
||||
|
||||
// Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place.
|
||||
HF_PFX_FILE=Path to certificate
|
||||
HF_PFX_PASSPHRASE=Passphrase for certificate
|
||||
|
||||
// Determine the build type
|
||||
PRODUCTION_BUILD=0|1
|
||||
PR_BUILD=0|1
|
||||
STABLE_BUILD=0|1
|
||||
|
||||
// Determine if to utilize testing or stable Metaverse URLs
|
||||
USE_STABLE_GLOBAL_SERVICES=1
|
||||
BUILD_GLOBAL_SERVICES=STABLE
|
||||
|
||||
##### Generate Files
|
||||
|
||||
Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
```
|
||||
|
||||
If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`.
|
||||
|
||||
##### Generating a release/debug only vcpkg build
|
||||
|
||||
In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your cmake generate command. Building a release only vcpkg can drastically decrease the total build time.
|
||||
In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your CMake generate command. Building a release only vcpkg can drastically decrease the total build time.
|
||||
|
||||
For release only vcpkg:
|
||||
|
||||
|
@ -124,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:
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Build Android
|
||||
|
||||
*Last Updated on December 21, 2019*
|
||||
*Last Updated on December 15, 2020*
|
||||
|
||||
Please read the [general build guide](BUILD.md) for information on building other platforms. Only Android specific instructions are found in this file. **Note that these instructions apply to building for Oculus Quest.**
|
||||
Please read the [general build guide](BUILD.md) for information on building other platforms. Only Android specific instructions are found in this file. **Note that these instructions apply to building for the Oculus Quest 1.**
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
@ -14,7 +14,7 @@ Please install the dependencies for your OS using the [Windows](BUILD_WIN.md), [
|
|||
|
||||
### Android Studio
|
||||
|
||||
Download the [Android Studio](https://developer.android.com/studio/index.html) installer and run it. Once installed, at the welcome screen, click _Configure_ in the lower right corner and select _SDK Manager_.
|
||||
Download the [Android Studio](https://developer.android.com/studio/index.html) installer and run it. Once installed, click _File_ then _Settings_, expand _Appearance & Behavior_ then expand _System Settings_ and select _Android SDK_.
|
||||
|
||||
From the _SDK Platforms_ tab, select API levels 26 and 28.
|
||||
|
||||
|
@ -27,24 +27,28 @@ From the _SDK Tools_ tab, select the following
|
|||
* Android SDK Tools
|
||||
* NDK (even if you have the NDK installed separately)
|
||||
|
||||
Still in the _SDK Tools_ tab, click _Show Package Details_. Select CMake 3.6.4. Do this even if you have a separate CMake installation.
|
||||
Still in the _SDK Tools_ tab, check off _Show Package Details_ at the bottom. Select CMake 3.6.4. Do this even if you have a separate CMake installation. Also, make sure the NDK installed version is 18 (or higher).
|
||||
|
||||
Also, make sure the NDK installed version is 18 (or higher).
|
||||
Now go back to _File_ then _Project Structure_ then under _Project_ set the Android Gradle Plugin Version to `3.2.1` and Gradle Version to `4.10.1`.
|
||||
|
||||
If Android Studio pops open the "Plugin Update Recommeded" dialog, do not click update, just click X on the top right to close. Later versions of the Gradle plugin have known issues with cz.malohlava.
|
||||
|
||||
## Environment
|
||||
|
||||
### Create a keystore in Android Studio
|
||||
Follow the directions [here](https://developer.android.com/studio/publish/app-signing#generate-key) to create a keystore file. You can save it anywhere (preferably not in the `hifi` folder).
|
||||
Follow the directions [here](https://developer.android.com/studio/publish/app-signing#generate-key) to create a keystore file. You can save it anywhere (preferably not in the `vircadia` folder).
|
||||
|
||||
### Set up machine specific Gradle properties
|
||||
|
||||
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
|
||||
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
|
||||
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
|
||||
HIFI_ANDROID_KEY_ALIAS=<key_alias>
|
||||
HIFI_ANDROID_KEY_PASSWORD=<key_passwords>
|
||||
```properties
|
||||
HIFI_ANDROID_PRECOMPILED=<your_home_directory>/Android/hifi_externals
|
||||
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
|
||||
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
|
||||
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.
|
||||
|
||||
|
@ -52,17 +56,26 @@ Note, do not use $HOME for the path. It must be a fully qualified path name. Als
|
|||
|
||||
Add these lines to `gradle.properties`
|
||||
|
||||
SUPPRESS_QUEST_INTERFACE
|
||||
SUPPRESS_QUEST_FRAME_PLAYER
|
||||
```properties
|
||||
SUPPRESS_QUEST_INTERFACE
|
||||
SUPPRESS_QUEST_FRAME_PLAYER
|
||||
```
|
||||
|
||||
#### If you are building for an Oculus Quest
|
||||
|
||||
Add these lines to `gradle.properties`
|
||||
|
||||
SUPPRESS_INTERFACE
|
||||
SUPPRESS_FRAME_PLAYER
|
||||
```properties
|
||||
SUPPRESS_INTERFACE
|
||||
SUPPRESS_FRAME_PLAYER
|
||||
```
|
||||
|
||||
The above code to suppress modules is not necessary, but will speed up the build process.
|
||||
#### 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`
|
||||
|
||||
```properties
|
||||
SUPPRESS_FRAME_PLAYER
|
||||
SUPPRESS_QUEST_FRAME_PLAYER
|
||||
```
|
||||
|
||||
### Clone the repository
|
||||
|
||||
|
@ -74,12 +87,17 @@ The above code to suppress modules is not necessary, but will speed up the build
|
|||
|
||||
* Open Android Studio
|
||||
* Choose _Open an existing Android Studio project_
|
||||
* Navigate to the `hifi` repository and choose the `android` folder and select _OK_
|
||||
* Navigate to the `vircadia` repository that had you cloned and choose the `android` folder and select _OK_
|
||||
* Wait for Gradle to sync (this should take around 20 minutes the first time)
|
||||
* From the _Build_ menu select _Make Project_
|
||||
* If a dialog pops open saying "Plugin Update Recommeded" dialog, do not click update, just click X on the top right to close.
|
||||
* In the _Project_ window click on the project you wish to build (i.e. "questInterface") then click _Build_ in the top menu and choose _Make Module 'questInterface'_
|
||||
* By default this will build the "debug" apk, you can change this by opening the _Build Variants_ window along the left side and select other build types such as "release".
|
||||
* Your newly build APK should reside in `vircadia\android\apps\questInterface\release` (if you chose release).
|
||||
|
||||
### Running a Module
|
||||
|
||||
You are free to use the "adb" command line or other development tools to install (sideload on Quest) your newly built APK, or you can follow the instructions below to load the APK via Android Studio.
|
||||
|
||||
* In the toolbar at the top of Android Studio, next to the green hammer icon, you should see a dropdown menu.
|
||||
* You may already see a configuration for the module you are trying to build. If so, select it.
|
||||
* Otherwise, select _Edit Configurations_.
|
||||
|
@ -112,9 +130,17 @@ To view a more complete debug log,
|
|||
* Click the icon with the two overlapping squares in the upper left corner of the tab where the sync is running (hover text says _Toggle view_)
|
||||
* To change verbosity, click _File > Settings_. Under _Build, Execution, Deployment > Compiler_ you can add command-line flags, as per Gradle documentation
|
||||
|
||||
If you encounter CMake issues, try adding the following system environment variable:
|
||||
|
||||
With your start menu, search for 'Edit the System Environment Variables' and open it.
|
||||
* Click on 'Advanced' tab, then 'Environment Variables'
|
||||
* Select 'New' under System variables
|
||||
* Set "Variable name" to QT_CMAKE_PREFIX_PATH
|
||||
* Set "Variable value" to the directory that your android build placed the CMake 3.6.4 library CMake directory (i.e. android\qt\lib\cmake).
|
||||
|
||||
Some things you can try if you want to do a clean build
|
||||
|
||||
* Delete the `build` and `.externalNativeBuild` folders from the folder for each module you're building (for example, `hifi/android/apps/interface`)
|
||||
* Delete the `build` and `.externalNativeBuild` folders from the folder for each module you're building (for example, `vircadia/android/apps/interface`)
|
||||
* If you have set your `HIFI_VCPKG_ROOT` environment variable, delete the contents of that directory; otherwise, delete `AppData/Local/Temp/hifi`
|
||||
* In Android Studio, click _File > Invalidate Caches / Restart_ and select _Invalidate and Restart_
|
||||
|
||||
|
|
|
@ -168,39 +168,3 @@ If your goal is to set up a development environment, it is desirable to set the
|
|||
directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable.
|
||||
For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`.
|
||||
By default, vcpkg will build in the system `/tmp` directory.
|
||||
|
||||
##### Ubuntu 18.04 only
|
||||
|
||||
In Ubuntu 18.04 there is a problem related with NVidia driver library version.
|
||||
|
||||
It can be worked around following these steps:
|
||||
|
||||
1. Uninstall incompatible nvtt libraries:
|
||||
`sudo apt-get remove libnvtt2 libnvtt-dev`
|
||||
|
||||
1. Install libssl1.0-dev:
|
||||
`sudo apt-get -y install libssl1.0-dev`
|
||||
|
||||
1. Clone castano nvidia-texture-tools:
|
||||
`git clone https://github.com/castano/nvidia-texture-tools`
|
||||
`cd nvidia-texture-tools/`
|
||||
|
||||
1. Make these changes in repo:
|
||||
* In file **VERSION** set `2.2.1`
|
||||
* In file **configure**:
|
||||
* set `build="release"`
|
||||
* set `-DNVTT_SHARED=1`
|
||||
|
||||
1. Configure, build and install:
|
||||
`./configure`
|
||||
`make`
|
||||
`sudo make install`
|
||||
|
||||
1. Link compiled files:
|
||||
`sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so`
|
||||
`sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so`
|
||||
`sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so`
|
||||
`sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so`
|
||||
|
||||
1. After running these steps you can run interface:
|
||||
`interface/interface`
|
||||
|
|
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.
|
||||
|
||||
brew install cmake openssl npm
|
||||
```bash
|
||||
brew install cmake openssl npm
|
||||
```
|
||||
|
||||
### Python 3
|
||||
|
||||
Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/).
|
||||
Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/).
|
||||
Execute the `Update Shell Profile.command` script that is provided with the installer.
|
||||
|
||||
### OSX SDK
|
||||
|
||||
You will need version `10.12` of the OSX SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on OSX. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g.
|
||||
|
||||
cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
```bash
|
||||
cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
```
|
||||
|
||||
### OpenSSL
|
||||
|
||||
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
|
||||
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
|
||||
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
|
||||
`export OPENSSL_ROOT_DIR=/usr/local/opt/openssl`
|
||||
or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake`
|
||||
|
@ -31,12 +35,14 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
|
|||
### Xcode
|
||||
|
||||
You can ask CMake to generate Xcode project files instead of Unix Makefiles using the `-G Xcode` parameter after CMake. You will need to select the Xcode installation in the terminal first if you have not done so already.
|
||||
|
||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 ..
|
||||
|
||||
If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally instaled CMake
|
||||
```bash
|
||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 ..
|
||||
```
|
||||
|
||||
If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally installed CMake.
|
||||
|
||||
After running CMake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run.
|
||||
|
||||
|
|
41
BUILD_WIN.md
41
BUILD_WIN.md
|
@ -7,38 +7,41 @@ This is a stand-alone guide for creating your first Vircadia build for Windows 6
|
|||
Note: We are now using Visual Studio 2017 or 2019 and Qt 5.12.3.
|
||||
If you are upgrading from previous versions, do a clean uninstall of those versions before going through this guide.
|
||||
|
||||
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
|
||||
**Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.**
|
||||
|
||||
### Step 1. Visual Studio & Python 3.x
|
||||
|
||||
If you don’t have Community or Professional edition of Visual Studio, download [Visual Studio Community 2019](https://visualstudio.microsoft.com/vs/). If you have Visual Studio 2017, you are not required to download Visual Studio 2019.
|
||||
|
||||
When selecting components, check "Desktop development with C++". On the right on the Summary toolbar, select the following components.
|
||||
When selecting components, check "Desktop development with C++".
|
||||
|
||||
#### If you're installing Visual Studio 2017,
|
||||
If you do not already have a Python 3.x development environment installed and want to install it with Visual Studio, check "Python Development". If you already have Visual Studio installed and need to add Python, open the "Add or remove programs" control panel and find the "Microsoft Visual Studio Installer". Select it and click "Modify". In the installer, select "Modify" again, then check "Python Development" and allow the installer to apply the changes.
|
||||
|
||||
On the right on the Summary toolbar, select the following components based on your Visual Studio version.
|
||||
|
||||
#### If you're installing Visual Studio 2017
|
||||
|
||||
* Windows 8.1 SDK and UCRT SDK
|
||||
* VC++ 2015.3 v14.00 (v140) toolset for desktop
|
||||
|
||||
#### If you're installing Visual Studio 2019,
|
||||
#### If you're installing Visual Studio 2019
|
||||
|
||||
* MSVC v142 - VS 2019 C++ X64/x86 build tools
|
||||
* MSVC v141 - VS 2017 C++ x64/x86 build tools
|
||||
* MSVC v140 - VS 2015 C++ build tools (v14.00)
|
||||
|
||||
If you do not already have a Python 3.x development environment installed, also check "Python Development" in this screen.
|
||||
### Step 1a. Alternate Python
|
||||
|
||||
If you already have Visual Studio installed and need to add Python, open the "Add or remove programs" control panel and find the "Microsoft Visual Studio Installer". Select it and click "Modify". In the installer, select "Modify" again, then check "Python Development" and allow the installer to apply the changes.
|
||||
|
||||
### Step 1a. Alternate Python
|
||||
|
||||
If you do not wish to use the Python installation bundled with Visual Studio, you can download the installer from [here](https://www.python.org/downloads/). Ensure you get version 3.6.6 or higher.
|
||||
If you do not wish to use the Python installation bundled with Visual Studio, you can download the installer from [here](https://www.python.org/downloads/). Ensure that you get version 3.6.6 or higher.
|
||||
|
||||
### Step 2. Python Dependencies
|
||||
|
||||
In a command-line that can access Python's pip you will need to run the following command:
|
||||
In an administrator command-line that can access Python's pip you will need to run the following command:
|
||||
|
||||
`pip install distro`
|
||||
|
||||
If you do not use an administrator command-line, you will get errors.
|
||||
|
||||
### Step 3. Installing CMake
|
||||
|
||||
Download and install the latest version of CMake 3.15.
|
||||
|
@ -46,7 +49,11 @@ Download and install the latest version of CMake 3.15.
|
|||
|
||||
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.15 Version page](https://cmake.org/files/v3.15/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
|
||||
|
||||
### Step 4. Create VCPKG environment variable
|
||||
### Step 4. Node.JS and NPM
|
||||
|
||||
Install version 10.15.0 LTS (or greater) of [Node.JS and NPM](<https://nodejs.org/en/download/>).
|
||||
|
||||
### Step 5. Create VCPKG environment variable
|
||||
In the next step, you will use CMake to build Vircadia. By default, the CMake process builds dependency files in Windows' `%TEMP%` directory, which is periodically cleared by the operating system. To prevent you from having to re-build the dependencies in the event that Windows clears that directory, we recommend that you create a `HIFI_VCPKG_BASE` environment variable linked to a directory somewhere on your machine. That directory will contain all dependency files until you manually remove them.
|
||||
|
||||
To create this variable:
|
||||
|
@ -65,7 +72,7 @@ To create this variable:
|
|||
* Set "Variable name" to `HIFI_VCPKG_BOOTSTRAP`
|
||||
* Set "Variable value" to `1`
|
||||
|
||||
### Step 5. Running CMake to Generate Build Files
|
||||
### Step 6. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
`cd "%VIRCADIA_DIR%"`
|
||||
|
@ -80,7 +87,7 @@ Run `cmake .. -G "Visual Studio 16 2019" -A x64`.
|
|||
|
||||
Where `%VIRCADIA_DIR%` is the directory for the Vircadia repository.
|
||||
|
||||
### Step 6. Making a Build
|
||||
### Step 7. Making a Build
|
||||
|
||||
Open `%VIRCADIA_DIR%\build\vircadia.sln` using Visual Studio.
|
||||
|
||||
|
@ -88,7 +95,7 @@ Change the Solution Configuration (menu ribbon under the menu bar, next to the g
|
|||
|
||||
Run from the menu bar `Build > Build Solution`.
|
||||
|
||||
### Step 7. Testing Interface
|
||||
### Step 8. Testing Interface
|
||||
|
||||
Create another environment variable (see Step #3)
|
||||
* Set "Variable name": `_NO_DEBUG_HEAP`
|
||||
|
@ -104,11 +111,11 @@ Note: You can also run Interface by launching it from command line or File Explo
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
For any problems after Step #6, first try this:
|
||||
For any problems after Step #7, first try this:
|
||||
* Delete your locally cloned copy of the Vircadia repository
|
||||
* Restart your computer
|
||||
* Redownload the [repository](https://github.com/vircadia/vircadia)
|
||||
* Restart directions from Step #6
|
||||
* Restart directions from Step #7
|
||||
|
||||
#### CMake gives you the same error message repeatedly after the build fails
|
||||
|
||||
|
|
181
INSTALLER.md
181
INSTALLER.md
|
@ -1,6 +1,6 @@
|
|||
# Creating an Installer
|
||||
|
||||
*Last Updated on August 24, 2020*
|
||||
*Last Updated on March 4, 2021*
|
||||
|
||||
Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform.
|
||||
|
||||
|
@ -9,13 +9,13 @@ During generation, CMake should produce an `install` target and a `package` targ
|
|||
The `install` target will copy the Vircadia targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
|
||||
This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt`
|
||||
|
||||
### Packaging
|
||||
## Packaging
|
||||
|
||||
To produce an installer, run the `package` target. However you will want to follow the steps specific to your platform below.
|
||||
|
||||
#### Windows
|
||||
### Windows
|
||||
|
||||
##### Prerequisites
|
||||
#### Prerequisites
|
||||
|
||||
To produce an executable installer on Windows, the following are required:
|
||||
|
||||
|
@ -60,14 +60,14 @@ To produce an executable installer on Windows, the following are required:
|
|||
1. Copy `Release\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
|
||||
1. Copy `ReleaseUnicode\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
|
||||
|
||||
1. [Node.JS and NPM](<https://www.npmjs.com/get-npm>)
|
||||
1. Install version 10.15.0 LTS
|
||||
1. [Node.JS and NPM](<https://nodejs.org/en/download/>)
|
||||
1. Install version 10.15.0 LTS (or greater)
|
||||
|
||||
##### Code Signing (optional)
|
||||
#### Code Signing (optional)
|
||||
|
||||
For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PASSPHRASE` environment variables to be present during CMake runtime and globally as we proceed to package the installer.
|
||||
|
||||
##### Creating the Installer
|
||||
#### Creating the Installer
|
||||
|
||||
1. Perform a clean cmake from a new terminal.
|
||||
1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration.
|
||||
|
@ -79,7 +79,14 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS
|
|||
1. Build CMakeTargets->PACKAGE
|
||||
The installer is now available in `build\_CPack_Packages\win64\NSIS`
|
||||
|
||||
#### OS X
|
||||
#### FAQ
|
||||
|
||||
1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process```
|
||||
1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files.
|
||||
1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage.
|
||||
|
||||
### OS X
|
||||
|
||||
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||
Install version 12.16.3 LTS
|
||||
|
||||
|
@ -91,8 +98,156 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS
|
|||
1. Perform a Release build of `package`
|
||||
Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop
|
||||
|
||||
### FAQ
|
||||
### Linux
|
||||
|
||||
1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process```
|
||||
1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files.
|
||||
1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage.
|
||||
#### Server
|
||||
|
||||
##### Ubuntu 18.04 | .deb
|
||||
|
||||
1. Ensure you are using an Ubuntu 18.04 system. There is no required minimum to the amount of CPU cores needed, however it's recommended that you use as many as you have available in order to have an efficient experience.
|
||||
```text
|
||||
Recommended CPU Cores: 16
|
||||
Minimum Disk Space: 40GB
|
||||
```
|
||||
3. Get and bootstrap Vircadia Builder.
|
||||
```bash
|
||||
git clone https://github.com/vircadia/vircadia-builder.git
|
||||
cd vircadia-builder
|
||||
```
|
||||
3. Run Vircadia Builder.
|
||||
```bash
|
||||
./vircadia-builder --build server
|
||||
```
|
||||
4. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step.
|
||||
```bash
|
||||
./vircadia-builder --build server
|
||||
```
|
||||
5. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.*
|
||||
6. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to.
|
||||
```text
|
||||
Git repository: https://github.com/vircadia/vircadia/
|
||||
# OR, for example
|
||||
Git repository: https://github.com/digisomni/vircadia/
|
||||
```
|
||||
7. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc"
|
||||
```text
|
||||
Git tag: master
|
||||
# OR, for example
|
||||
Git tag: v2021.1.0-rc
|
||||
```
|
||||
8. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`.
|
||||
```text
|
||||
Release type: DEV
|
||||
# OR, for example we recommend you use
|
||||
Release type: PRODUCTION
|
||||
```
|
||||
9. This value is the release version. Release numbers should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`.
|
||||
```text
|
||||
Release number: 2021.1.0
|
||||
```
|
||||
10. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`.
|
||||
```text
|
||||
Build number: fd6973b
|
||||
```
|
||||
11. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user.
|
||||
```text
|
||||
Installation dir: /home/ubuntu/Vircadia
|
||||
```
|
||||
12. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server.
|
||||
```text
|
||||
CPU cores to use for Vircadia: 16
|
||||
```
|
||||
13. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server.
|
||||
```text
|
||||
CPU cores to use for Qt5: 16
|
||||
```
|
||||
14. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit.
|
||||
15. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times.
|
||||
16. Navigate to the `pkg-scripts` directory.
|
||||
```bash
|
||||
cd ../Vircadia/source/pkg-scripts/
|
||||
```
|
||||
17. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.*
|
||||
```bash
|
||||
DEBVERSION="2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server
|
||||
```
|
||||
18. If successful, the generated .deb package will be in the `pkg-scripts` folder.
|
||||
|
||||
##### Amazon Linux 2 | .rpm
|
||||
|
||||
1. Ensure you are using an Amazon Linux 2 system. You will need many CPU cores to complete this process within a reasonable time. As an alternative to AWS EC2, you may use a [virtual machine](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-2-virtual-machine.html). Here are the recommended specs:
|
||||
```text
|
||||
AWS EC2 Instance Type: C5a.4xlarge
|
||||
Recommended CPU Cores: 16
|
||||
Minimum Disk Space: 40GB
|
||||
```
|
||||
2. Update the system and install dependencies.
|
||||
```bash
|
||||
sudo yum update -y
|
||||
sudo yum install git -y
|
||||
sudo yum install rpm-build
|
||||
```
|
||||
3. Get and bootstrap Vircadia Builder.
|
||||
```bash
|
||||
git clone https://github.com/vircadia/vircadia-builder.git
|
||||
cd vircadia-builder
|
||||
sudo ./install_amazon_linux_deps.sh
|
||||
```
|
||||
4. Run Vircadia Builder.
|
||||
```bash
|
||||
./vircadia-builder --build server
|
||||
```
|
||||
5. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step.
|
||||
```bash
|
||||
./vircadia-builder --build server
|
||||
```
|
||||
6. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.*
|
||||
7. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to.
|
||||
```text
|
||||
Git repository: https://github.com/vircadia/vircadia/
|
||||
# OR, for example
|
||||
Git repository: https://github.com/digisomni/vircadia/
|
||||
```
|
||||
8. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc".
|
||||
```text
|
||||
Git tag: master
|
||||
# OR, for example
|
||||
Git tag: v2021.1.0-rc
|
||||
```
|
||||
9. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`.
|
||||
```text
|
||||
Release type: DEV
|
||||
# OR, for example we recommend you use
|
||||
Release type: PRODUCTION
|
||||
```
|
||||
10. This value is the release version. Release numbers typically should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`.
|
||||
```text
|
||||
Release number: 2021.1.0
|
||||
```
|
||||
11. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`.
|
||||
```text
|
||||
Build number: fd6973b
|
||||
```
|
||||
12. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user.
|
||||
```text
|
||||
Installation dir: /root/Vircadia
|
||||
```
|
||||
13. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server.
|
||||
```text
|
||||
CPU cores to use for Vircadia: 16
|
||||
```
|
||||
14. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server.
|
||||
```text
|
||||
CPU cores to use for Qt5: 16
|
||||
```
|
||||
15. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit.
|
||||
16. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times.
|
||||
17. Navigate to the `pkg-scripts` directory.
|
||||
```bash
|
||||
cd ../Vircadia/source/pkg-scripts/
|
||||
```
|
||||
18. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.*
|
||||
```bash
|
||||
RPMVERSION="2021.1.0" ./make-rpm-server
|
||||
```
|
||||
19. If successful, the generated .rpm package will be in the `pkg-scripts` folder of the Vircadia source files.
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,5 +1,5 @@
|
|||
Copyright (c) 2013-2019, High Fidelity, Inc.
|
||||
Copyright (c) 2019-2020, Vircadia contributors.
|
||||
Copyright (c) 2019-2021, Vircadia contributors.
|
||||
All rights reserved.
|
||||
https://vircadia.com
|
||||
|
||||
|
|
58
README.md
58
README.md
|
@ -1,10 +1,10 @@
|
|||
# Vircadia
|
||||
# Vircadia (Codename Athena)
|
||||
|
||||
### What is this?
|
||||
|
||||
Vircadia is a 3D social software project seeking to incrementally bring about a truly free and open metaverse, in desktop and XR.
|
||||
Vircadia™ is a 3D social software project seeking to incrementally bring about a truly free and open metaverse, in desktop and XR.
|
||||
|
||||
### [Download](https://vircadia.com/download-vircadia/)
|
||||
### [Website](https://vircadia.com/) | [Discord](https://discordapp.com/invite/Pvx2vke) | [Download](https://vircadia.com/download-vircadia/)
|
||||
|
||||
### Releases
|
||||
|
||||
|
@ -12,50 +12,48 @@ Vircadia is a 3D social software project seeking to incrementally bring about a
|
|||
|
||||
### How to build the Interface
|
||||
|
||||
[For Windows](https://github.com/vircadia/vircadia/blob/master/BUILD_WIN.md)
|
||||
|
||||
[For Mac](https://github.com/vircadia/vircadia/blob/master/BUILD_OSX.md)
|
||||
|
||||
[For Linux](https://github.com/vircadia/vircadia/blob/master/BUILD_LINUX.md)
|
||||
|
||||
[For Linux - Vircadia Builder](https://github.com/vircadia/vircadia-builder)
|
||||
- [For Windows](https://github.com/vircadia/vircadia/blob/master/BUILD_WIN.md)
|
||||
- [For Mac](https://github.com/vircadia/vircadia/blob/master/BUILD_OSX.md)
|
||||
- [For Linux](https://github.com/vircadia/vircadia/blob/master/BUILD_LINUX.md)
|
||||
- [For Linux - Vircadia Builder](https://github.com/vircadia/vircadia-builder)
|
||||
|
||||
### How to deploy a Server
|
||||
|
||||
[For Windows and Linux](https://vircadia.com/deploy-a-server/)
|
||||
- [For Windows and Linux](https://vircadia.com/deploy-a-server/)
|
||||
|
||||
### How to build a Server
|
||||
|
||||
[For Linux - Vircadia Builder](https://github.com/vircadia/vircadia-builder)
|
||||
- [For Windows](https://github.com/vircadia/vircadia/blob/master/BUILD_WIN.md)
|
||||
- [For Linux](https://github.com/vircadia/vircadia/blob/master/BUILD_LINUX.md)
|
||||
- [For Linux - Vircadia Builder](https://github.com/vircadia/vircadia-builder)
|
||||
|
||||
### 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 - Interface](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x)
|
||||
- [For Linux - Server .deb - Vircadia Builder](INSTALLER.md#ubuntu-1804--deb)
|
||||
- [For Linux - Server .rpm - Vircadia Builder](INSTALLER.md#amazon-linux-2--rpm)
|
||||
- [For Linux - Interface AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages)
|
||||
|
||||
[For Linux - AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages)
|
||||
|
||||
### Boot to Metaverse: The Goal
|
||||
### Boot to Metaverse: [The Goal](https://vircadia.com/vision/)
|
||||
|
||||
Having a place to experience adventure, a place to relax with calm breath, that's a world to live in. An engine to support infinite combinations and possibilities of worlds without censorship and interruption, that's a metaverse. Finding a way to make infinite realities our reality is the dream.
|
||||
|
||||
### Boot to Metaverse: The Technicals
|
||||
|
||||
Many developers have had personal combinations of High Fidelity from C++ modifications to different default scripts, all of which are lost to time as their fullest potential is never truly shared and propagated through the system.
|
||||
Vircadia consists of many projects and codebases with its unifying structure's goal being a decentralized metaverse.
|
||||
|
||||
The goal of this project is to achieve the metaverse dream through shared contribution and building. Setting goals that are achievable yet meaningful is key to making proper forward progress on the technical front whilst maintaining morale.
|
||||
- The Interface (Codename Athena) - You are here!
|
||||
- The Server (Codename Athena) - You are also here!
|
||||
- The UI Framework (Codename Nyx) - Codebase coming soon.
|
||||
- [The Metaverse (Codename Iamus)](https://github.com/vircadia/Iamus/)
|
||||
- [The Metaverse Dashboard (Codename Iamus)](https://github.com/vircadia/project-iamus-dashboard/)
|
||||
- [The Launcher (Codename Pantheon)](https://github.com/vircadia/pantheon-launcher/)
|
||||
|
||||
### Why High Fidelity's Virtual Reality Platform?
|
||||
|
||||
Because of all the options, it is the only starting point that is open-source, cross-platform, fully VR integrated + fully desktop integrated with an aim for quality visuals and performance. It also provides a foundation to build from including components like entity management, full body IK, etc.
|
||||
|
||||
WebXR offers the open-source and decentralized aspect but does not have any of the full featured starting points such as avatars, IK, etc. which means that a lot of ground work will have to be laid to make something functional. Far more work will need to be done to create a truly seamless and extensive experience as well.
|
||||
|
||||
Platforms like NeosVR or VRChat are not viable from go due to their fundamental closed-source and centralized nature. A metaverse to live in cannot have the keys handed over to any singular entity, if any at all.
|
||||
|
||||
We need to do the best we can with what we've got and our best bet as open source developers is to not redesign the wheel if we can help it!
|
||||
#### Child Projects
|
||||
- [Vircadia Builder for Linux](https://github.com/vircadia/vircadia-builder/)
|
||||
- [General Documentation](https://github.com/vircadia/vircadia-docs-sphinx/)
|
||||
|
||||
### Contribution
|
||||
|
||||
A special thanks to the contributors of Vircadia.
|
||||
|
||||
[Contribution](CONTRIBUTING.md)
|
||||
There are many contributors to Vircadia. Code writers, reviewers, testers, documentation writers, modelers, and general supporters of the project are all integral to its development and success towards its goals. Find out how you can [contribute](CONTRIBUTING.md)!
|
||||
|
|
|
@ -17,7 +17,7 @@ fi
|
|||
ANDROID_APP=interface
|
||||
ANDROID_OUTPUT_DIR=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_TYPE}
|
||||
ANDROID_OUTPUT_FILE=${ANDROID_APP}-${ANDROID_BUILD_TYPE}.apk
|
||||
ANDROID_APK_NAME=Vircadia-Alpha-${ANDROID_APK_SUFFIX}
|
||||
ANDROID_APK_NAME=Vircadia-${ANDROID_APK_SUFFIX}
|
||||
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} ${ANDROID_APP}:${ANDROID_BUILD_TARGET}
|
||||
cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <Trace.h>
|
||||
#include <StatTracker.h>
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
#include "AssignmentClientLogging.h"
|
||||
#include "AssignmentFactory.h"
|
||||
|
@ -235,10 +236,13 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
|
|||
qCDebug(assignment_client) << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||
|
||||
// start the deployed assignment
|
||||
QThread* workerThread = new QThread;
|
||||
QThread* workerThread = new QThread();
|
||||
workerThread->setObjectName("ThreadedAssignment Worker");
|
||||
|
||||
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
|
||||
connect(workerThread, &QThread::started, _currentAssignment.data(), [this] {
|
||||
setThreadName("ThreadedAssignment Worker");
|
||||
_currentAssignment->run();
|
||||
});
|
||||
|
||||
// Once the ThreadedAssignment says it is finished - we ask it to deleteLater
|
||||
// This is a queued connection so that it is put into the event loop to be processed by the worker
|
||||
|
|
|
@ -11,9 +11,13 @@
|
|||
|
||||
#include "AudioMixerSlavePool.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
void AudioMixerSlaveThread::run() {
|
||||
while (true) {
|
||||
wait();
|
||||
|
@ -157,6 +161,7 @@ void AudioMixerSlavePool::resize(int numThreads) {
|
|||
// start new slaves
|
||||
for (int i = 0; i < numThreads - _numThreads; ++i) {
|
||||
auto slave = new AudioMixerSlaveThread(*this, _workerSharedData);
|
||||
QObject::connect(slave, &QThread::started, [] { setThreadName("AudioMixerSlaveThread"); });
|
||||
slave->start();
|
||||
_slaves.emplace_back(slave);
|
||||
}
|
||||
|
|
|
@ -269,7 +269,13 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
// the avatar mixer uses the negative value of the sent version
|
||||
instanceVersionRef = -packetTraitVersion;
|
||||
} else {
|
||||
_avatar->processTraitInstance(traitType, instanceID, message.read(traitSize));
|
||||
// Don't accept avatar entity data for distribution unless sender has rez permissions on the domain.
|
||||
// The sender shouldn't be sending avatar entity data, however this provides a back-up.
|
||||
auto trait = message.read(traitSize);
|
||||
if (sendingNode.getCanRezAvatarEntities()) {
|
||||
_avatar->processTraitInstance(traitType, instanceID, trait);
|
||||
}
|
||||
|
||||
instanceVersionRef = packetTraitVersion;
|
||||
}
|
||||
|
||||
|
@ -290,6 +296,29 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::emulateDeleteEntitiesTraitsMessage(const QList<QUuid>& avatarEntityIDs) {
|
||||
// Emulates processSetTraitsMessage() actions on behalf of an avatar whose canRezAvatarEntities permission has been removed.
|
||||
// The source avatar should be removing its avatar entities. However, using this method provides a back-up.
|
||||
|
||||
auto traitType = AvatarTraits::AvatarEntity;
|
||||
for (const auto& entityID : avatarEntityIDs) {
|
||||
auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, entityID);
|
||||
|
||||
_avatar->processDeletedTraitInstance(traitType, entityID);
|
||||
// Mixer doesn't need deleted IDs.
|
||||
_avatar->getAndClearRecentlyRemovedIDs();
|
||||
|
||||
// to track a deleted instance but keep version information
|
||||
// the avatar mixer uses the negative value of the sent version
|
||||
// Because there is no originating message from an avatar we enlarge the magnitude by 1.
|
||||
// If a user subsequently has canRezAvatarEntities permission granted, they will have to relog in order for their
|
||||
// avatar entities to be visible to others.
|
||||
instanceVersionRef = -instanceVersionRef - 1;
|
||||
}
|
||||
|
||||
_lastReceivedTraitsChange = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& message) {
|
||||
// Avatar Traits flow control marks each outgoing avatar traits packet with a
|
||||
// sequence number. The mixer caches the traits sent in the traits packet.
|
||||
|
|
|
@ -132,6 +132,7 @@ public:
|
|||
int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed
|
||||
|
||||
void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode);
|
||||
void emulateDeleteEntitiesTraitsMessage(const QList<QUuid>& avatarEntityIDs);
|
||||
void processBulkAvatarTraitsAckMessage(ReceivedMessage& message);
|
||||
void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode,
|
||||
AvatarTraits::TraitVersion traitVersion);
|
||||
|
|
|
@ -432,6 +432,17 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
}
|
||||
}
|
||||
|
||||
// The source avatar should be removing its avatar entities. However, provide a back-up.
|
||||
if (sendAvatar) {
|
||||
if (!sourceAvatarNode->getCanRezAvatarEntities()) {
|
||||
auto sourceAvatarNodeData = reinterpret_cast<AvatarMixerClientData*>(sourceAvatarNode->getLinkedData());
|
||||
auto avatarEntityIDs = sourceAvatarNodeData->getAvatar().getAvatarEntityIDs();
|
||||
if (avatarEntityIDs.count() > 0) {
|
||||
sourceAvatarNodeData->emulateDeleteEntitiesTraitsMessage(avatarEntityIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sendAvatar) {
|
||||
AvatarDataSequenceNumber lastSeqToReceiver = destinationNodeData->getLastBroadcastSequenceNumber(sourceAvatarNode->getLocalID());
|
||||
AvatarDataSequenceNumber lastSeqFromSender = sourceAvatarNodeData->getLastReceivedSequenceNumber();
|
||||
|
|
|
@ -398,7 +398,7 @@ void ScriptableAvatar::setAvatarEntityData(const AvatarEntityMap& avatarEntityDa
|
|||
|
||||
// clear deleted traits
|
||||
for (const auto& id : idsToClear) {
|
||||
clearAvatarEntity(id);
|
||||
clearAvatarEntityInternal(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,7 +408,7 @@ void ScriptableAvatar::updateAvatarEntity(const QUuid& entityID, const QByteArra
|
|||
std::map<QUuid, EntityItemPointer>::iterator itr = _entities.find(entityID);
|
||||
if (itr != _entities.end()) {
|
||||
_entities.erase(itr);
|
||||
clearAvatarEntity(entityID);
|
||||
clearAvatarEntityInternal(entityID);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <udt/PacketHeaders.h>
|
||||
|
||||
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
|
||||
const int MESSAGES_MIXER_RATE_LIMITER_INTERVAL = 1000; // 1 second
|
||||
|
||||
MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message)
|
||||
{
|
||||
|
@ -44,10 +45,20 @@ void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> receivedMessa
|
|||
QByteArray data;
|
||||
QUuid senderID;
|
||||
bool isText;
|
||||
auto senderUUID = senderNode->getUUID();
|
||||
MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID);
|
||||
|
||||
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(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
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) {
|
||||
auto senderUUID = senderNode->getUUID();
|
||||
QString channel = QString::fromUtf8(message->getMessage());
|
||||
_channelSubscribers[channel] << senderNode->getUUID();
|
||||
|
||||
_channelSubscribers[channel] << senderUUID;
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
auto senderUUID = senderNode->getUUID();
|
||||
QString channel = QString::fromUtf8(message->getMessage());
|
||||
|
||||
if (_channelSubscribers.contains(channel)) {
|
||||
_channelSubscribers[channel].remove(senderNode->getUUID());
|
||||
_channelSubscribers[channel].remove(senderUUID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +103,48 @@ void MessagesMixer::sendStatsPacket() {
|
|||
}
|
||||
|
||||
void MessagesMixer::run() {
|
||||
ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
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 handleMessagesSubscribe(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:
|
||||
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
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <QtCore/QDir>
|
||||
|
||||
#include <OctreeDataUtils.h>
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(octree_server, "hifi.octree-server")
|
||||
|
||||
|
@ -1192,7 +1193,10 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
_persistAsFileType);
|
||||
_persistManager->moveToThread(&_persistThread);
|
||||
connect(&_persistThread, &QThread::finished, _persistManager, &QObject::deleteLater);
|
||||
connect(&_persistThread, &QThread::started, _persistManager, &OctreePersistThread::start);
|
||||
connect(&_persistThread, &QThread::started, _persistManager, [this] {
|
||||
setThreadName("OctreePersistThread");
|
||||
_persistManager->start();
|
||||
});
|
||||
connect(_persistManager, &OctreePersistThread::loadCompleted, this, [this]() {
|
||||
beginRunning();
|
||||
});
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
# Created by Leonardo Murillo on 12/16/2015.
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Copyright 2021 Vircadia contributors.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -20,9 +21,9 @@ macro(GENERATE_INSTALLERS)
|
|||
set(INSTALLER_TYPE "client_only")
|
||||
string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
elseif (SERVER_ONLY)
|
||||
set(_PACKAGE_NAME_EXTRA "-Sandbox")
|
||||
set(_PACKAGE_NAME_EXTRA "-Server")
|
||||
set(INSTALLER_TYPE "server_only")
|
||||
string(REGEX REPLACE "Vircadia" "Vircadia Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
string(REGEX REPLACE "Vircadia" "Vircadia Server" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
else ()
|
||||
set(_DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||
set(INSTALLER_TYPE "full")
|
||||
|
@ -31,7 +32,7 @@ macro(GENERATE_INSTALLERS)
|
|||
set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME})
|
||||
set(CPACK_PACKAGE_VENDOR "Vircadia")
|
||||
set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
|
||||
set(CPACK_PACKAGE_FILE_NAME "Vircadia-Alpha${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "Vircadia${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}-${RELEASE_NAME}")
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
|
||||
if (PR_BUILD)
|
||||
|
@ -122,7 +123,7 @@ macro(GENERATE_INSTALLERS)
|
|||
endif ()
|
||||
|
||||
if (BUILD_SERVER)
|
||||
cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Sandbox")
|
||||
cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Server")
|
||||
endif ()
|
||||
|
||||
include(CPack)
|
||||
|
|
|
@ -8,23 +8,30 @@
|
|||
#
|
||||
|
||||
macro(SETUP_MEMORY_DEBUGGER)
|
||||
if (DEFINED ENV{HIFI_MEMORY_DEBUGGING})
|
||||
SET( HIFI_MEMORY_DEBUGGING true )
|
||||
if ("$ENV{VIRCADIA_MEMORY_DEBUGGING}")
|
||||
SET( VIRCADIA_MEMORY_DEBUGGING true )
|
||||
endif ()
|
||||
|
||||
if (HIFI_MEMORY_DEBUGGING)
|
||||
if (VIRCADIA_MEMORY_DEBUGGING)
|
||||
if (UNIX)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# for clang on Linux
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -shared-libasan -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -shared-libasan -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -shared-libasan -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address -fsanitize=leak -fsanitize-recover=address")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -fsanitize-recover=address")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -fsanitize-recover=address")
|
||||
else ()
|
||||
# for gcc on Linux
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -U_FORTIFY_SOURCE -fno-stack-protector -fno-omit-frame-pointer")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=undefined -fsanitize=address")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=undefined -fsanitize=address")
|
||||
# For some reason, using -fstack-protector results in this error:
|
||||
# usr/bin/ld: ../../libraries/audio/libaudio.so: undefined reference to `FIR_1x4_AVX512(float*, float*, float*, float*, float*, float (*) [64], int)'
|
||||
# The '-DSTACK_PROTECTOR' argument below disables the usage of this function in the code. This should be fine as it only works on the latest Intel hardware,
|
||||
# and is an optimization that should make no functional difference.
|
||||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak -U_FORTIFY_SOURCE -DSTACK_PROTECTOR -fstack-protector-strong -fno-omit-frame-pointer")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak ")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize=leak")
|
||||
endif()
|
||||
endif (UNIX)
|
||||
else()
|
||||
message(FATAL_ERROR "Memory debugging is not supported on this platform." )
|
||||
endif()
|
||||
endif ()
|
||||
endmacro(SETUP_MEMORY_DEBUGGER)
|
||||
|
|
|
@ -23,8 +23,12 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
|
||||
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
|
||||
set_from_env(RELEASE_NAME RELEASE_NAME "")
|
||||
set_from_env(STABLE_BUILD STABLE_BUILD 0)
|
||||
set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "")
|
||||
|
||||
set_from_env(PRELOADED_STARTUP_LOCATION PRELOADED_STARTUP_LOCATION "")
|
||||
set_from_env(PRELOADED_SCRIPT_WHITELIST PRELOADED_SCRIPT_WHITELIST "")
|
||||
|
||||
set_from_env(BYPASS_SIGNING BYPASS_SIGNING 0)
|
||||
|
||||
message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}")
|
||||
|
@ -176,12 +180,12 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
if (PRODUCTION_BUILD)
|
||||
set(INTERFACE_SHORTCUT_NAME "Vircadia")
|
||||
set(CONSOLE_SHORTCUT_NAME "Console")
|
||||
set(SANDBOX_SHORTCUT_NAME "Sandbox")
|
||||
set(SANDBOX_SHORTCUT_NAME "Server")
|
||||
set(APP_USER_MODEL_ID "com.vircadia.console")
|
||||
else ()
|
||||
set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}")
|
||||
set(CONSOLE_SHORTCUT_NAME "Console - ${BUILD_VERSION_NO_SHA}")
|
||||
set(SANDBOX_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}")
|
||||
set(SANDBOX_SHORTCUT_NAME "Server - ${BUILD_VERSION_NO_SHA}")
|
||||
endif ()
|
||||
|
||||
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 1/14/16.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -24,10 +25,12 @@ namespace BuildInfo {
|
|||
const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@";
|
||||
const QString ORGANIZATION_DOMAIN = "vircadia.com";
|
||||
const QString VERSION = "@BUILD_VERSION@";
|
||||
const QString RELEASE_NAME = "@RELEASE_NAME@";
|
||||
const QString BUILD_NUMBER = "@BUILD_NUMBER@";
|
||||
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
|
||||
const QString BUILD_TIME = "@BUILD_TIME@";
|
||||
const QString INITIAL_STARTUP_LOCATION = "@INITIAL_STARTUP_LOCATION@";
|
||||
const QString PRELOADED_STARTUP_LOCATION = "@PRELOADED_STARTUP_LOCATION@";
|
||||
const QString PRELOADED_SCRIPT_WHITELIST = "@PRELOADED_SCRIPT_WHITELIST@";
|
||||
|
||||
enum BuildType {
|
||||
Dev,
|
||||
|
|
|
@ -251,8 +251,8 @@ Var substringResult
|
|||
|
||||
!macro InitSection SecName
|
||||
; This macro reads component installed flag from the registry and
|
||||
;changes checked state of the section on the components page.
|
||||
;Input: section index constant name specified in Section command.
|
||||
; changes checked state of the section on the components page.
|
||||
; Input: section index constant name specified in Section command.
|
||||
|
||||
ClearErrors
|
||||
;Reading component status from registry
|
||||
|
@ -718,7 +718,7 @@ Function InstallTypesPage
|
|||
StrCpy $OffsetUnits u
|
||||
StrCpy $Express "0"
|
||||
|
||||
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface and Vircadia Sandbox"
|
||||
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface"
|
||||
pop $ExpressInstallRadioButton
|
||||
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
@ -1346,6 +1346,7 @@ Section "-Core installation"
|
|||
${EndIf}
|
||||
|
||||
${If} @SERVER_COMPONENT_CONDITIONAL@
|
||||
${AndIf} $Express != "1"
|
||||
; handling for server console shortcut
|
||||
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 2.4,
|
||||
"version": 2.5,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
|
@ -295,10 +295,10 @@
|
|||
},
|
||||
{
|
||||
"name": "approved_safe_urls",
|
||||
"label": "Approved Script and QML URLs",
|
||||
"label": "Approved Script and QML URLs (Not Enabled)",
|
||||
"help": "These URLs will be sent to the Interface as safe URLs to allow through the whitelist if the Interface has this security option enabled.",
|
||||
"placeholder": "0",
|
||||
"default": "1",
|
||||
"placeholder": "",
|
||||
"default": "",
|
||||
"advanced": false
|
||||
},
|
||||
{
|
||||
|
@ -338,7 +338,7 @@
|
|||
"name": "standard_permissions",
|
||||
"type": "table",
|
||||
"label": "Domain-Wide User Permissions",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether a user can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"caption": "Standard Permissions",
|
||||
"can_add_new_rows": false,
|
||||
"groups": [
|
||||
|
@ -347,8 +347,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether a user can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -363,6 +363,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -439,17 +446,20 @@
|
|||
"default": [
|
||||
{
|
||||
"id_can_connect": true,
|
||||
"id_can_rez_avatar_entities": true,
|
||||
"id_can_rez_tmp_certified": true,
|
||||
"permissions_id": "anonymous"
|
||||
},
|
||||
{
|
||||
"id_can_connect": true,
|
||||
"id_can_rez_avatar_entities": true,
|
||||
"id_can_rez_tmp_certified": true,
|
||||
"permissions_id": "friends"
|
||||
},
|
||||
{
|
||||
"id_can_adjust_locks": true,
|
||||
"id_can_connect": true,
|
||||
"id_can_rez_avatar_entities": true,
|
||||
"id_can_adjust_locks": true,
|
||||
"id_can_connect_past_max_capacity": true,
|
||||
"id_can_kick": true,
|
||||
"id_can_replace_content": true,
|
||||
|
@ -463,6 +473,7 @@
|
|||
},
|
||||
{
|
||||
"id_can_connect": true,
|
||||
"id_can_rez_avatar_entities": true,
|
||||
"id_can_rez_tmp_certified": true,
|
||||
"permissions_id": "logged-in"
|
||||
}
|
||||
|
@ -484,8 +495,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether users in specific groups can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users in specific groups can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -525,6 +536,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -613,8 +631,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether users in specific groups can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users in specific groups can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -651,6 +669,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -734,8 +759,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether a user can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -750,6 +775,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -833,8 +865,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether users from specific IPs can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -849,6 +881,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -932,8 +971,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether users with specific MACs can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -948,6 +987,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -1031,8 +1077,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 11
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Avatar Entities</strong><br />Sets whether users with specific Machine Fingerprints can use avatar entities on the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li><li><strong>Get and Set Private User Data</strong><br>Sets whether a user can get and set the privateUserData entity property.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -1047,6 +1093,13 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_avatar_entities",
|
||||
"label": "Avatar Entities",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_adjust_locks",
|
||||
"label": "Lock / Unlock",
|
||||
|
@ -1488,6 +1541,24 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "messages_mixer",
|
||||
"label": "Messages Mixer",
|
||||
"assignment-types": [
|
||||
4
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "max_node_messages_per_second",
|
||||
"type": "int",
|
||||
"label": "Maximum Message Rate",
|
||||
"help": "Maximum message send rate (messages per second) per node",
|
||||
"placeholder": 1000,
|
||||
"default": 1000,
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "entity_server_settings",
|
||||
"label": "Entities",
|
||||
|
@ -2013,6 +2084,23 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "domain_server",
|
||||
"label": "Setup Domain Server",
|
||||
"restart": false,
|
||||
"hidden": true,
|
||||
"settings": [
|
||||
{
|
||||
"name": "network_address",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "network_port",
|
||||
"type": "int",
|
||||
"default": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "installed_content",
|
||||
"label": "Installed Content",
|
||||
|
|
|
@ -129,9 +129,10 @@ function getCurrentDomainIDType() {
|
|||
return DOMAIN_ID_TYPE_UNKNOWN;
|
||||
}
|
||||
if (DomainInfo !== null) {
|
||||
if (DomainInfo.name !== undefined) {
|
||||
return DOMAIN_ID_TYPE_TEMP;
|
||||
}
|
||||
// Disabled because detecting as temp domain... and we're not even using temp domains right now.
|
||||
// if (DomainInfo.name !== undefined) {
|
||||
// return DOMAIN_ID_TYPE_TEMP;
|
||||
// }
|
||||
return DOMAIN_ID_TYPE_FULL;
|
||||
}
|
||||
return DOMAIN_ID_TYPE_UNKNOWN;
|
||||
|
@ -504,7 +505,7 @@ function createDomainIDPrompt(callback) {
|
|||
swal({
|
||||
title: 'Finish Registering Domain',
|
||||
type: 'input',
|
||||
text: 'Enter a label for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>This is a required step for registration.</br></br>',
|
||||
text: 'Enter a label for this Domain Server.</br></br>This will help you identify which domain ID belongs to which server.</br></br>This is a required step for registration.</br></br>Acceptable characters are [A-Z][a-z0-9]+-_.</br',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Create",
|
||||
closeOnConfirm: false,
|
||||
|
|
|
@ -193,7 +193,11 @@ function promptToCreateDomainID() {
|
|||
|
||||
var formJSON = {
|
||||
"metaverse": {
|
||||
"automatic_networking": "full",
|
||||
"id": domainID
|
||||
},
|
||||
"descriptors": {
|
||||
"world_name": label
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -461,6 +465,7 @@ function savePermissions() {
|
|||
"standard_permissions": [
|
||||
{
|
||||
"id_can_connect": anonymousCanConnect,
|
||||
"id_can_rez_avatar_entities": anonymousCanConnect,
|
||||
"id_can_rez": anonymousCanRez,
|
||||
"id_can_rez_certified": anonymousCanRez,
|
||||
"id_can_rez_tmp": anonymousCanRez,
|
||||
|
@ -469,6 +474,7 @@ function savePermissions() {
|
|||
},
|
||||
{
|
||||
"id_can_connect": friendsCanConnect,
|
||||
"id_can_rez_avatar_entities": friendsCanConnect,
|
||||
"id_can_rez": friendsCanRez,
|
||||
"id_can_rez_certified": friendsCanRez,
|
||||
"id_can_rez_tmp": friendsCanRez,
|
||||
|
@ -477,6 +483,7 @@ function savePermissions() {
|
|||
},
|
||||
{
|
||||
"id_can_connect": loggedInCanConnect,
|
||||
"id_can_rez_avatar_entities": loggedInCanConnect,
|
||||
"id_can_rez": loggedInCanRez,
|
||||
"id_can_rez_certified": loggedInCanRez,
|
||||
"id_can_rez_tmp": loggedInCanRez,
|
||||
|
@ -486,6 +493,7 @@ function savePermissions() {
|
|||
{
|
||||
"id_can_adjust_locks": localhostPermissions,
|
||||
"id_can_connect": localhostPermissions,
|
||||
"id_can_rez_avatar_entities": localhostPermissions,
|
||||
"id_can_connect_past_max_capacity": localhostPermissions,
|
||||
"id_can_kick": localhostPermissions,
|
||||
"id_can_replace_content": localhostPermissions,
|
||||
|
|
|
@ -353,6 +353,7 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData;
|
||||
userPerms.permissions |= NodePermissions::Permission::canRezAvatarEntities;
|
||||
} else {
|
||||
// at this point we don't have a sending socket for packets from this node - assume it is the active socket
|
||||
// or the public socket if we haven't activated a socket for the node yet
|
||||
|
@ -448,6 +449,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
|||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData;
|
||||
userPerms.permissions |= NodePermissions::Permission::canRezAvatarEntities;
|
||||
newNode->setPermissions(userPerms);
|
||||
return newNode;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
#include <Gzip.h>
|
||||
|
||||
#include <OctreeDataUtils.h>
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
|
@ -68,6 +69,9 @@ Q_LOGGING_CATEGORY(domain_server_ice, "hifi.domain_server.ice")
|
|||
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
const QString DomainServer::REPLACEMENT_FILE_EXTENSION = ".replace";
|
||||
const QString PUBLIC_SOCKET_ADDRESS_KEY = "network_address";
|
||||
const QString PUBLIC_SOCKET_PORT_KEY = "network_port";
|
||||
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
const int MIN_PORT = 1;
|
||||
const int MAX_PORT = 65535;
|
||||
|
||||
|
@ -827,9 +831,11 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
||||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||
|
||||
_assetClientThread.setObjectName("AssetClient Thread");
|
||||
QString name = "AssetClient Thread";
|
||||
_assetClientThread.setObjectName(name);
|
||||
auto assetClient = DependencyManager::set<AssetClient>();
|
||||
assetClient->moveToThread(&_assetClientThread);
|
||||
connect(&_assetClientThread, &QThread::started, [name] { setThreadName(name.toStdString()); });
|
||||
_assetClientThread.start();
|
||||
// add whatever static assignments that have been parsed to the queue
|
||||
addStaticAssignmentsToQueue();
|
||||
|
@ -901,14 +907,13 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
|
||||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// send any public socket changes to the data server so nodes can find us at our new IP
|
||||
connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged, this,
|
||||
&DomainServer::performIPAddressPortUpdate);
|
||||
|
||||
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// send any public socket changes to the data server so nodes can find us at our new IP
|
||||
connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
|
||||
this, &DomainServer::performIPAddressUpdate);
|
||||
|
||||
// have the LNL enable public socket updating via STUN
|
||||
nodeList->startSTUNPublicSocketUpdate();
|
||||
}
|
||||
|
@ -1504,13 +1509,23 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
|||
return socketObject;
|
||||
}
|
||||
|
||||
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
void DomainServer::performIPAddressPortUpdate(const HifiSockAddr& newPublicSockAddr) {
|
||||
const QString& DOMAIN_SERVER_SETTINGS_KEY = "domain_server";
|
||||
const QString& publicSocketAddress = newPublicSockAddr.getAddress().toString();
|
||||
const int publicSocketPort = newPublicSockAddr.getPort();
|
||||
|
||||
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
||||
sendHeartbeatToMetaverse(newPublicSockAddr.getAddress().toString());
|
||||
sendHeartbeatToMetaverse(publicSocketAddress, publicSocketPort);
|
||||
|
||||
QJsonObject rootObject;
|
||||
QJsonObject domainServerObject;
|
||||
domainServerObject.insert(PUBLIC_SOCKET_ADDRESS_KEY, publicSocketAddress);
|
||||
domainServerObject.insert(PUBLIC_SOCKET_PORT_KEY, publicSocketPort);
|
||||
rootObject.insert(DOMAIN_SERVER_SETTINGS_KEY, domainServerObject);
|
||||
QJsonDocument doc(rootObject);
|
||||
_settingsManager.recurseJSONObjectAndOverwriteSettings(rootObject, DomainSettings);
|
||||
}
|
||||
|
||||
void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
|
||||
void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress, const int port) {
|
||||
// Setup the domain object to send to the data server
|
||||
QJsonObject domainObject;
|
||||
|
||||
|
@ -1520,10 +1535,20 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
|
|||
static const QString PROTOCOL_VERSION_KEY = "protocol";
|
||||
domainObject[PROTOCOL_VERSION_KEY] = protocolVersionsSignatureBase64();
|
||||
|
||||
// add networking
|
||||
static const QString NETWORK_ADDRESS_SETTINGS_KEY = "domain_server." + PUBLIC_SOCKET_ADDRESS_KEY;
|
||||
const QString networkAddressFromSettings = _settingsManager.valueForKeyPath(NETWORK_ADDRESS_SETTINGS_KEY).toString();
|
||||
if (!networkAddress.isEmpty()) {
|
||||
static const QString PUBLIC_NETWORK_ADDRESS_KEY = "network_address";
|
||||
domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
|
||||
domainObject[PUBLIC_SOCKET_ADDRESS_KEY] = networkAddress;
|
||||
} else if (!networkAddressFromSettings.isEmpty()) {
|
||||
domainObject[PUBLIC_SOCKET_ADDRESS_KEY] = networkAddressFromSettings;
|
||||
}
|
||||
|
||||
static const QString PORT_SETTINGS_KEY = "domain_server." + PUBLIC_SOCKET_PORT_KEY;
|
||||
const int portFromSettings = _settingsManager.valueForKeyPath(PORT_SETTINGS_KEY).toInt();
|
||||
if (port != 0) {
|
||||
domainObject[PUBLIC_SOCKET_PORT_KEY] = port;
|
||||
} else if (portFromSettings != 0) {
|
||||
domainObject[PUBLIC_SOCKET_PORT_KEY] = portFromSettings;
|
||||
}
|
||||
|
||||
static const QString AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
|
|
|
@ -112,8 +112,8 @@ private slots:
|
|||
void setupPendingAssignmentCredits();
|
||||
void sendPendingTransactionsToServer();
|
||||
|
||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void sendHeartbeatToMetaverse() { sendHeartbeatToMetaverse(QString()); }
|
||||
void performIPAddressPortUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void sendHeartbeatToMetaverse() { sendHeartbeatToMetaverse(QString(), int()); }
|
||||
void sendHeartbeatToIceServer();
|
||||
void nodePingMonitor();
|
||||
|
||||
|
@ -176,7 +176,7 @@ private:
|
|||
void setupAutomaticNetworking();
|
||||
void setupICEHeartbeatForFullNetworking();
|
||||
void setupHeartbeatToMetaverse();
|
||||
void sendHeartbeatToMetaverse(const QString& networkAddress);
|
||||
void sendHeartbeatToMetaverse(const QString& networkAddress, const int port);
|
||||
|
||||
void randomizeICEServerAddress(bool shouldTriggerHostLookup);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <SettingHandle.h>
|
||||
#include <SettingHelpers.h>
|
||||
#include <FingerprintUtils.h>
|
||||
#include <ModerationFlags.h>
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
|
||||
|
@ -527,6 +528,28 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena
|
|||
*newAdminRoles = adminRoles;
|
||||
}
|
||||
|
||||
if (oldVersion < 2.5) {
|
||||
// Default values for new canRezAvatarEntities permission.
|
||||
unpackPermissions();
|
||||
std::list<std::unordered_map<NodePermissionsKey, NodePermissionsPointer>> permissionsSets{
|
||||
_standardAgentPermissions.get(),
|
||||
_agentPermissions.get(),
|
||||
_ipPermissions.get(),
|
||||
_macPermissions.get(),
|
||||
_machineFingerprintPermissions.get(),
|
||||
_groupPermissions.get(),
|
||||
_groupForbiddens.get()
|
||||
};
|
||||
foreach (auto permissionsSet, permissionsSets) {
|
||||
for (auto entry : permissionsSet) {
|
||||
const auto& userKey = entry.first;
|
||||
if (permissionsSet[userKey]->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
permissionsSet[userKey]->set(NodePermissions::Permission::canRezAvatarEntities);
|
||||
}
|
||||
}
|
||||
}
|
||||
packPermissions();
|
||||
}
|
||||
|
||||
// write the current description version to our settings
|
||||
*versionVariant = _descriptionVersion;
|
||||
|
@ -863,6 +886,20 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
// pull the UUID being kicked from the packet
|
||||
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()) {
|
||||
// make sure we actually have a node with this UUID
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
@ -881,16 +918,20 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
if (!verifiedUsername.isEmpty()) {
|
||||
// if we have a verified user name for this user, we first apply the kick to the username
|
||||
|
||||
// check if there were already permissions
|
||||
bool hadPermissions = havePermissionsForName(verifiedUsername);
|
||||
// if we have optional ban parameters, we should ban the username based on the parameter
|
||||
if (!hasOptionalBanParameters || banByUsername) {
|
||||
// check if there were already permissions
|
||||
bool hadPermissions = havePermissionsForName(verifiedUsername);
|
||||
|
||||
// grab or create permissions for the given username
|
||||
auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()];
|
||||
// grab or create permissions for the given username
|
||||
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
|
||||
userPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
// ensure that the connect permission is clear
|
||||
userPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't have a username, or this domain-server uses the "multi-kick" setting to
|
||||
|
@ -898,7 +939,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
// then we remove connect permissions for the machine fingerprint (or IP as fallback)
|
||||
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
|
||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||
if (nodeData) {
|
||||
|
@ -923,36 +964,39 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
|||
fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
}
|
||||
} else {
|
||||
// if no node data, all we can do is IP address
|
||||
auto& kickAddress = matchingNode->getActiveSocket()
|
||||
? matchingNode->getActiveSocket()->getAddress()
|
||||
: matchingNode->getPublicSocket().getAddress();
|
||||
// if no node data, all we can do is ban by IP address
|
||||
banByIP = true;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
// 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() ||
|
||||
kickAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||
kickAddress.isLoopback() ) {
|
||||
qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest";
|
||||
return;
|
||||
}
|
||||
// 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
|
||||
// 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.
|
||||
if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() ||
|
||||
kickAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||
kickAddress.isLoopback() ) {
|
||||
qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest";
|
||||
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
|
||||
bool hadIPPermissions = hasPermissionsForIP(kickAddress);
|
||||
// grab or create permissions for the given IP address
|
||||
auto ipPermissions = _ipPermissions[ipAddressKey];
|
||||
|
||||
// grab or create permissions for the given IP address
|
||||
auto ipPermissions = _ipPermissions[ipAddressKey];
|
||||
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
newPermissions = true;
|
||||
|
||||
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
newPermissions = true;
|
||||
|
||||
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
}
|
||||
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1448,6 +1492,8 @@ QJsonObject DomainServerSettingsManager::settingsResponseObjectForType(const QSt
|
|||
SettingsBackupFlag settingsBackupFlag) {
|
||||
QJsonObject responseObject;
|
||||
|
||||
responseObject["version"] = _descriptionVersion; // Domain settings version number.
|
||||
|
||||
if (!typeValue.isEmpty() || authentication == Authenticated) {
|
||||
// convert the string type value to a QJsonValue
|
||||
QJsonValue queryType = typeValue.isEmpty() ? QJsonValue() : QJsonValue(typeValue.toInt());
|
||||
|
|
|
@ -90,8 +90,8 @@ endif()
|
|||
if 'Windows' == system:
|
||||
self.exe = os.path.join(self.path, 'vcpkg.exe')
|
||||
self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.bat'), '-disableMetrics' ]
|
||||
self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/builds/vcpkg-win32-client.zip%3FversionId=tSFzbw01VkkVFeRQ6YuAY4dro2HxJR9U'
|
||||
self.vcpkgHash = 'a650db47a63ccdc9904b68ddd16af74772e7e78170b513ea8de5a3b47d032751a3b73dcc7526d88bcb500753ea3dd9880639ca842bb176e2bddb1710f9a58cd3'
|
||||
self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-win32-client-20210122.zip'
|
||||
self.vcpkgHash = '3df86b7d58c827bf08b3b7744f456f414b86a6d9bd58a507924103bc5a88f01ee495ce1f0fbf2f5b27f1ef6bfb1526e580ec13d3b9f87a89a462b3c50589fd6a'
|
||||
self.hostTriplet = 'x64-windows'
|
||||
if usePrebuilt:
|
||||
self.prebuiltArchive = self.assets_url + "/dependencies/vcpkg/builds/vcpkg-win32.zip%3FversionId=3SF3mDC8dkQH1JP041m88xnYmWNzZflx"
|
||||
|
|
Binary file not shown.
|
@ -19,16 +19,17 @@ Item {
|
|||
property string url: ""
|
||||
property string scriptUrl: null
|
||||
property bool useBackground: true
|
||||
property string userAgent: ""
|
||||
|
||||
onUrlChanged: {
|
||||
load(root.url, root.scriptUrl, root.useBackground);
|
||||
load(root.url, root.scriptUrl, root.useBackground, root.userAgent);
|
||||
}
|
||||
|
||||
onScriptUrlChanged: {
|
||||
if (root.item) {
|
||||
root.item.scriptUrl = root.scriptUrl;
|
||||
} else {
|
||||
load(root.url, root.scriptUrl, root.useBackground);
|
||||
load(root.url, root.scriptUrl, root.useBackground, root.userAgent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,13 +37,21 @@ Item {
|
|||
if (root.item) {
|
||||
root.item.useBackground = root.useBackground;
|
||||
} else {
|
||||
load(root.url, root.scriptUrl, root.useBackground);
|
||||
load(root.url, root.scriptUrl, root.useBackground, root.userAgent);
|
||||
}
|
||||
}
|
||||
|
||||
onUserAgentChanged: {
|
||||
if (root.item) {
|
||||
root.item.userAgent = root.userAgent;
|
||||
} else {
|
||||
load(root.url, root.scriptUrl, root.useBackground, root.userAgent);
|
||||
}
|
||||
}
|
||||
|
||||
property var item: null
|
||||
|
||||
function load(url, scriptUrl, useBackground) {
|
||||
function load(url, scriptUrl, useBackground, userAgent) {
|
||||
// Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375
|
||||
if (root.item != null) {
|
||||
root.item.url = "about:blank"
|
||||
|
@ -54,11 +63,12 @@ Item {
|
|||
root.item.url = url
|
||||
root.item.scriptUrl = scriptUrl
|
||||
root.item.useBackground = useBackground
|
||||
root.item.userAgent = userAgent
|
||||
})
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
load(root.url, root.scriptUrl, root.useBackground);
|
||||
load(root.url, root.scriptUrl, root.useBackground, root.userAgent);
|
||||
}
|
||||
|
||||
signal sendToScript(var message);
|
||||
|
|
|
@ -13,10 +13,13 @@ Item {
|
|||
property alias url: webViewCore.url
|
||||
property alias canGoBack: webViewCore.canGoBack
|
||||
property alias webViewCore: webViewCore
|
||||
property alias webViewCoreProfile: webViewCore.profile
|
||||
// FIXME - This was commented out to allow for manual setting of the userAgent.
|
||||
//
|
||||
// property alias webViewCoreProfile: webViewCore.profile
|
||||
property string webViewCoreUserAgent
|
||||
|
||||
property bool useBackground: webViewCore.useBackground
|
||||
property string userAgent: webViewCore.profile.httpUserAgent
|
||||
property string userScriptUrl: ""
|
||||
property string urlTag: "noDownload=false";
|
||||
|
||||
|
@ -34,6 +37,10 @@ Item {
|
|||
permissionPopupBackground.visible = false;
|
||||
}
|
||||
|
||||
onUserAgentChanged: {
|
||||
webViewCore.profile.httpUserAgent = flick.userAgent;
|
||||
}
|
||||
|
||||
StylesUIt.HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
@ -74,7 +81,7 @@ Item {
|
|||
|
||||
function onLoadingChanged(loadRequest) {
|
||||
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
|
||||
|
||||
webViewCore.profile.httpUserAgent = flick.userAgent;
|
||||
// Required to support clicking on "hifi://" links
|
||||
var url = loadRequest.url.toString();
|
||||
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
|
||||
|
@ -101,7 +108,6 @@ Item {
|
|||
height: parent.height
|
||||
backgroundColor: (flick.useBackground) ? "white" : "transparent"
|
||||
|
||||
profile: HFWebEngineProfile;
|
||||
settings.pluginsEnabled: true
|
||||
settings.touchIconsEnabled: true
|
||||
settings.allowRunningInsecureContent: true
|
||||
|
@ -136,8 +142,10 @@ Item {
|
|||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
|
||||
if (webViewCoreUserAgent !== undefined) {
|
||||
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
|
||||
if (flick.userAgent !== undefined) {
|
||||
webViewCore.profile.httpUserAgent = flick.userAgent;
|
||||
webViewCore.profile.offTheRecord = false;
|
||||
webViewCore.profile.storageName = "qmlWebEngine";
|
||||
} else {
|
||||
webViewCore.profile.httpUserAgent += " (VircadiaInterface)";
|
||||
}
|
||||
|
|
|
@ -11,12 +11,14 @@ Item {
|
|||
property alias url: webViewCore.url
|
||||
property alias canGoBack: webViewCore.canGoBack
|
||||
property alias webViewCore: webViewCore
|
||||
property alias webViewCoreProfile: webViewCore.profile
|
||||
property string webViewCoreUserAgent
|
||||
// FIXME - This was commented out to allow for manual setting of the userAgent.
|
||||
//
|
||||
// property alias webViewCoreProfile: webViewCore.profile
|
||||
|
||||
property bool useBackground: webViewCore.useBackground
|
||||
property alias useBackground: webViewCore.useBackground
|
||||
property alias userAgent: webViewCore.userAgent
|
||||
property string userScriptUrl: ""
|
||||
property string urlTag: "noDownload=false";
|
||||
property string urlTag: "noDownload=false"
|
||||
|
||||
signal newViewRequestedCallback(var request)
|
||||
signal loadingChangedCallback(var loadRequest)
|
||||
|
|
|
@ -27,7 +27,11 @@ Item {
|
|||
}
|
||||
*/
|
||||
|
||||
property alias viewProfile: webroot.webViewCoreProfile
|
||||
// FIXME - Reimplement profiles for... why? Was it so that new windows opened share the same profile?
|
||||
// Are profiles written to by the webengine during the session?
|
||||
// Removed in PR Feature/web entity user agent #988
|
||||
//
|
||||
// property alias viewProfile: webroot.webViewCoreProfile
|
||||
|
||||
FlickableWebViewCore {
|
||||
id: webroot
|
||||
|
|
|
@ -25,7 +25,11 @@ Item {
|
|||
property bool isDesktop: false
|
||||
property alias url: web.url
|
||||
property alias webView: web.webViewCore
|
||||
property alias profile: web.webViewCoreProfile
|
||||
// FIXME - Reimplement profiles for... why? Was it so that new windows opened share the same profile?
|
||||
// Are profiles written to by the webengine during the session?
|
||||
// Removed in PR Feature/web entity user agent #988
|
||||
//
|
||||
// property alias profile: web.webViewCoreProfile
|
||||
property bool remove: false
|
||||
property bool closeButtonVisible: true
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Item {
|
|||
property alias flickable: webroot.interactive
|
||||
property alias blurOnCtrlShift: webroot.blurOnCtrlShift
|
||||
property alias useBackground: webroot.useBackground
|
||||
property alias userAgent: webroot.userAgent
|
||||
|
||||
function stop() {
|
||||
webroot.stop();
|
||||
|
@ -37,7 +38,11 @@ Item {
|
|||
}
|
||||
*/
|
||||
|
||||
property alias viewProfile: webroot.webViewCoreProfile
|
||||
// FIXME - Reimplement profiles for... why? Was it so that new windows opened share the same profile?
|
||||
// Are profiles written to by the webengine during the session?
|
||||
// Removed in PR Feature/web entity user agent #988
|
||||
//
|
||||
// property alias viewProfile: webroot.webViewCoreProfile
|
||||
|
||||
FlickableWebViewCore {
|
||||
id: webroot
|
||||
|
|
|
@ -518,7 +518,12 @@ Rectangle {
|
|||
glyphText: "\ue02e"
|
||||
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
if (!AddressManager.isConnected || Entities.canRezAvatarEntities()) {
|
||||
adjustWearables.open(currentAvatar);
|
||||
} else {
|
||||
Window.alert("You cannot use wearables on this domain.")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,7 +534,11 @@ Rectangle {
|
|||
glyphText: wearablesFrozen ? hifi.glyphs.lock : hifi.glyphs.unlock;
|
||||
|
||||
onClicked: {
|
||||
emitSendToScript({'method' : 'toggleWearablesFrozen'});
|
||||
if (!AddressManager.isConnected || Entities.canRezAvatarEntities()) {
|
||||
emitSendToScript({'method' : 'toggleWearablesFrozen'});
|
||||
} else {
|
||||
Window.alert("You cannot use wearables on this domain.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ Item {
|
|||
color: hifi.colors.darkGray;
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: selected && pal.activeTab == "nearbyTab" && thisNameCard.userName !== "" && isPresent;
|
||||
enabled: selected && pal.activeTab == "nearbyTab" && isPresent;
|
||||
hoverEnabled: enabled
|
||||
onClicked: {
|
||||
goToUserInDomain(thisNameCard.uuid);
|
||||
|
|
|
@ -506,6 +506,7 @@ Rectangle {
|
|||
id: itemCell;
|
||||
property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore";
|
||||
property bool isButton: styleData.role === "mute" || styleData.role === "kick";
|
||||
property bool isBan: styleData.role === "kick";
|
||||
property bool isAvgAudio: styleData.role === "avgAudioLevel";
|
||||
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
|
||||
visible: isButton;
|
||||
enabled: !nameCard.isReplicated;
|
||||
anchors.centerIn: parent;
|
||||
anchors.verticalCenter: itemCell.verticalCenter;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: styleData.role === "kick" ? 4 : 18;
|
||||
width: 32;
|
||||
height: 32;
|
||||
onClicked: {
|
||||
|
@ -620,7 +623,39 @@ Rectangle {
|
|||
HiFiGlyphs {
|
||||
text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted;
|
||||
// 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.fill: parent;
|
||||
// Style
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import stylesUit 1.0
|
||||
import controlsUit 1.0 as HifiControlsUit
|
||||
|
@ -104,6 +105,11 @@ Rectangle {
|
|||
AudioScriptingInterface.setSystemInjectorGain(sliderValue);
|
||||
}
|
||||
}
|
||||
function updateNoiseReductionThresholdFromQML(sliderValue) {
|
||||
if (AudioScriptingInterface.getNoiseReductionThreshold() !== sliderValue) {
|
||||
AudioScriptingInterface.setNoiseReductionThreshold(sliderValue);
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
enablePeakValues();
|
||||
|
@ -164,7 +170,7 @@ Rectangle {
|
|||
x: 2 * margins.paddings;
|
||||
width: parent.width;
|
||||
// switch heights + 2 * top margins
|
||||
height: (root.switchHeight) * 6 + 48;
|
||||
height: (bar.currentIndex === 0) ? (root.switchHeight) * 2 + 48 : (root.switchHeight) * 4 + 48;
|
||||
anchors.top: firstSeparator.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
|
@ -175,6 +181,7 @@ Rectangle {
|
|||
height: parent.height;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left;
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: muteMic;
|
||||
height: root.switchHeight;
|
||||
|
@ -193,45 +200,11 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: noiseReductionSwitch;
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: muteMic.bottom;
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: "Noise Reduction";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.noiseReduction;
|
||||
onCheckedChanged: {
|
||||
AudioScriptingInterface.noiseReduction = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: acousticEchoCancellationSwitch;
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: noiseReductionSwitch.bottom
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: "Echo Cancellation";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.acousticEchoCancellation;
|
||||
onCheckedChanged: {
|
||||
AudioScriptingInterface.acousticEchoCancellation = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; });
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: pttSwitch
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: acousticEchoCancellationSwitch.bottom;
|
||||
anchors.top: muteMic.bottom;
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
|
||||
|
@ -254,6 +227,7 @@ Rectangle {
|
|||
height: parent.height;
|
||||
anchors.top: parent.top
|
||||
anchors.left: switchContainer.right;
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: warnMutedSwitch
|
||||
height: root.switchHeight;
|
||||
|
@ -271,7 +245,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: audioLevelSwitch
|
||||
height: root.switchHeight;
|
||||
|
@ -519,13 +492,282 @@ Rectangle {
|
|||
anchors.top: systemInjectorGainContainer.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noiseReductionHeader
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings * 2;
|
||||
height: 36;
|
||||
anchors.top: secondSeparator.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
HiFiGlyphs {
|
||||
width: margins.sizeCheckBox;
|
||||
text: hifi.glyphs.mic;
|
||||
color: hifi.colors.white;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: -size / 4; // The glyph has empty space at left about 25%
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 30;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: margins.sizeText + margins.sizeLevel;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: margins.sizeCheckBox;
|
||||
size: 22;
|
||||
color: hifi.colors.white;
|
||||
text: qsTr("Noise Reduction");
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noiseReductionSwitches;
|
||||
x: 2 * margins.paddings;
|
||||
width: parent.width;
|
||||
// switch heights + 2 * top margins
|
||||
height: (root.switchHeight) * 5 + 48;
|
||||
anchors.top: noiseReductionHeader.bottom;
|
||||
anchors.topMargin: 0;
|
||||
|
||||
Item {
|
||||
id: noiseReductionSwitchContainer;
|
||||
x: margins.paddings;
|
||||
width: parent.width / 2;
|
||||
height: parent.height;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: acousticEchoCancellationSwitch;
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: noiseReductionSwitchContainer.top
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: "Echo Cancellation";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.acousticEchoCancellation;
|
||||
onCheckedChanged: {
|
||||
AudioScriptingInterface.acousticEchoCancellation = checked;
|
||||
checked = Qt.binding(function () { return AudioScriptingInterface.acousticEchoCancellation; });
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: noiseReductionSwitch;
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: acousticEchoCancellationSwitch.bottom;
|
||||
anchors.topMargin: 24
|
||||
anchors.left: parent.left
|
||||
labelTextOn: "Noise Reduction";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.noiseReduction;
|
||||
onCheckedChanged: {
|
||||
AudioScriptingInterface.noiseReduction = checked;
|
||||
checked = Qt.binding(function () { return AudioScriptingInterface.noiseReduction; }); // restore binding
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Switch {
|
||||
id: noiseReductionAutomaticSwitch;
|
||||
height: root.switchHeight;
|
||||
switchWidth: root.switchWidth;
|
||||
anchors.top: noiseReductionSwitch.bottom;
|
||||
anchors.topMargin: 24;
|
||||
anchors.left: parent.left;
|
||||
labelTextOn: "Manual Noise Reduction";
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: !AudioScriptingInterface.noiseReductionAutomatic;
|
||||
visible: AudioScriptingInterface.noiseReduction;
|
||||
onCheckedChanged: {
|
||||
AudioScriptingInterface.noiseReductionAutomatic = !checked;
|
||||
checked = Qt.binding(function () { return !AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noiseReductionThresholdContainer
|
||||
x: margins.paddings;
|
||||
anchors.top: noiseReductionSwitches.bottom;
|
||||
anchors.topMargin: 16;
|
||||
width: parent.width - margins.paddings * 2;
|
||||
height: avatarGainSliderTextMetrics.height + 10;
|
||||
visible: AudioScriptingInterface.noiseReduction && !AudioScriptingInterface.noiseReductionAutomatic;
|
||||
|
||||
HifiControlsUit.Slider {
|
||||
id: noiseReductionThresholdSlider
|
||||
anchors.right: parent.right
|
||||
height: noiseReductionThresholdSliderTextMetrics.height
|
||||
width: 200
|
||||
minimumValue: 0.0
|
||||
maximumValue: 1.0
|
||||
stepSize: 0.05
|
||||
value: AudioScriptingInterface.getNoiseReductionThreshold()
|
||||
onValueChanged: {
|
||||
updateNoiseReductionThresholdFromQML(value);
|
||||
}
|
||||
onPressedChanged: {
|
||||
if (!pressed) {
|
||||
updateNoiseReductionThresholdFromQML(value);
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onWheel: {
|
||||
// Do nothing.
|
||||
}
|
||||
onDoubleClicked: {
|
||||
noiseReductionThresholdSlider.value = 0.0;
|
||||
}
|
||||
onPressed: {
|
||||
// Pass through to Slider
|
||||
mouse.accepted = false;
|
||||
}
|
||||
onReleased: {
|
||||
// the above mouse.accepted seems to make this
|
||||
// never get called, nonetheless...
|
||||
mouse.accepted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: noiseReductionThresholdSliderTextMetrics
|
||||
text: noiseReductionThresholdSliderText.text
|
||||
font: noiseReductionThresholdSliderText.font
|
||||
}
|
||||
RalewayRegular {
|
||||
// The slider for my card is special, it controls the master gain
|
||||
id: noiseReductionThresholdSliderText;
|
||||
text: "Audio input threshold";
|
||||
size: 16;
|
||||
anchors.left: parent.left;
|
||||
color: hifi.colors.white;
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noisePeak
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 5;
|
||||
anchors.top: noiseReductionThresholdSlider.bottom;
|
||||
|
||||
width: noiseReductionThresholdSlider.width - 10;
|
||||
height: 8;
|
||||
|
||||
Text {
|
||||
id: status;
|
||||
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
visible: AudioScriptingInterface.muted;
|
||||
color: "#E2334D";
|
||||
|
||||
text: "MUTED";
|
||||
font.pointSize: 10;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: noiseBar;
|
||||
|
||||
property bool gated: false;
|
||||
property var level: AudioScriptingInterface.inputLevel;
|
||||
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
|
||||
anchors { fill: parent }
|
||||
|
||||
visible: !status.visible;
|
||||
|
||||
function onNoiseGateOpened() {
|
||||
noiseBar.gated = false;
|
||||
}
|
||||
|
||||
function onNoiseGateClosed() {
|
||||
noiseBar.gated = true;
|
||||
}
|
||||
|
||||
function onInputLevelChanged(level) {
|
||||
noiseBar.level = level;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
AudioScriptingInterface.noiseGateOpened.connect(onNoiseGateOpened);
|
||||
AudioScriptingInterface.noiseGateClosed.connect(onNoiseGateClosed);
|
||||
AudioScriptingInterface.inputLevelChanged.connect(onInputLevelChanged);
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
AudioScriptingInterface.noiseGateOpened.disconnect(onNoiseGateOpened);
|
||||
AudioScriptingInterface.noiseGateClosed.disconnect(onNoiseGateClosed);
|
||||
AudioScriptingInterface.inputLevelChanged.disconnect(onInputLevelChanged);
|
||||
}
|
||||
|
||||
Rectangle { // base
|
||||
radius: 4;
|
||||
anchors { fill: parent }
|
||||
color: colors.gutter;
|
||||
}
|
||||
|
||||
Rectangle { // noiseMask
|
||||
id: noiseMask;
|
||||
width: parent.width * noiseBar.level;
|
||||
radius: 5;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
bottomMargin: 0;
|
||||
top: parent.top;
|
||||
topMargin: 0;
|
||||
left: parent.left;
|
||||
leftMargin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
LinearGradient {
|
||||
anchors { fill: noiseMask }
|
||||
source: noiseMask
|
||||
start: Qt.point(0, 0);
|
||||
end: Qt.point(noiseBar.width, 0);
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0;
|
||||
color: noiseBar.gated ? "#E2334D" : "#39A38F";
|
||||
}
|
||||
GradientStop {
|
||||
position: 1;
|
||||
color: noiseBar.gated ? "#E2334D" : "#39A38F";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: thirdSeparator;
|
||||
anchors.top: noiseReductionThresholdContainer.bottom;
|
||||
anchors.topMargin: 14;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inputDeviceHeader
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2;
|
||||
height: 36;
|
||||
anchors.top: secondSeparator.bottom;
|
||||
anchors.top: thirdSeparator.bottom;
|
||||
anchors.topMargin: 10;
|
||||
|
||||
HiFiGlyphs {
|
||||
|
@ -597,19 +839,19 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
AudioControls.InputPeak {
|
||||
id: inputLevel
|
||||
anchors.right: parent.right
|
||||
peak: model.peak;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR)) &&
|
||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||
id: inputLevel
|
||||
anchors.right: parent.right
|
||||
peak: model.peak;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR)) &&
|
||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: thirdSeparator;
|
||||
id: fourthSeparator;
|
||||
anchors.top: inputView.bottom;
|
||||
anchors.topMargin: 10;
|
||||
}
|
||||
|
@ -617,7 +859,7 @@ Rectangle {
|
|||
Item {
|
||||
id: outputDeviceHeader;
|
||||
anchors.topMargin: 10;
|
||||
anchors.top: thirdSeparator.bottom;
|
||||
anchors.top: fourthSeparator.bottom;
|
||||
x: margins.paddings;
|
||||
width: parent.width - margins.paddings*2
|
||||
height: 36
|
||||
|
@ -684,4 +926,4 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,8 +112,7 @@ MessageBox {
|
|||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Avatars'
|
||||
|
||||
popup.bodyText = 'Get avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
|
||||
'Wear avatars in <b><a href="app://purchases">Inventory.</a></b>'
|
||||
popup.bodyText = 'Get avatars from the Community Bazaar. (Coming soon!)'
|
||||
|
||||
popup.onLinkClicked = function(link) {
|
||||
popup.close();
|
||||
|
|
|
@ -28,13 +28,18 @@ Rectangle {
|
|||
fillMode: Image.PreserveAspectFit
|
||||
source: "../../../images/vircadia-banner.svg"
|
||||
}
|
||||
Item { height: 30; width: 1 }
|
||||
Item { height: 25; width: 1 }
|
||||
Column {
|
||||
id: buildColumm
|
||||
id: buildColumn
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 70
|
||||
anchors.leftMargin: 0
|
||||
RalewayRegular {
|
||||
text: "Build " + About.buildVersion
|
||||
text: "Interface"
|
||||
size: 16
|
||||
color: "white"
|
||||
}
|
||||
RalewayRegular {
|
||||
text: "Build " + About.buildVersion + " " + About.releaseName
|
||||
size: 16
|
||||
color: "white"
|
||||
}
|
||||
|
@ -54,14 +59,25 @@ Rectangle {
|
|||
textFormat: Text.StyledText
|
||||
linkColor: "#00B4EF"
|
||||
color: "white"
|
||||
text: "<a href=\"https://github.com/vircadia/vircadia\">Vircadia Github</a>."
|
||||
text: "<a href=\"https://vircadia.com\">Website</a>"
|
||||
size: 20
|
||||
onLinkActivated: {
|
||||
About.openUrl("https:/github.com/vircadia/vircadia");
|
||||
About.openUrl("https://vircadia.com");
|
||||
}
|
||||
|
||||
}
|
||||
Item { height: 40; width: 1 }
|
||||
RalewayRegular {
|
||||
textFormat: Text.StyledText
|
||||
linkColor: "#00B4EF"
|
||||
color: "white"
|
||||
text: "<a href=\"https://github.com/vircadia/vircadia\">Source</a>"
|
||||
size: 20
|
||||
onLinkActivated: {
|
||||
About.openUrl("https://github.com/vircadia/vircadia");
|
||||
}
|
||||
|
||||
}
|
||||
Item { height: 25; width: 1 }
|
||||
Row {
|
||||
spacing: 5
|
||||
Image {
|
||||
|
@ -117,7 +133,7 @@ Rectangle {
|
|||
Item { height: 20; width: 1 }
|
||||
RalewayRegular {
|
||||
color: "white"
|
||||
text: "© 2019-2020 Vircadia contributors."
|
||||
text: "© 2019 - 2021 Vircadia contributors."
|
||||
size: 14
|
||||
}
|
||||
RalewayRegular {
|
||||
|
@ -135,5 +151,23 @@ Rectangle {
|
|||
About.openUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
|
||||
}
|
||||
}
|
||||
Item { height: 35; width: 1 }
|
||||
RalewayRegular {
|
||||
color: "white"
|
||||
text: "In memoriam,"
|
||||
size: 14
|
||||
}
|
||||
RalewayRegular {
|
||||
color: "white"
|
||||
text: "2012 - 2019 the High Fidelity virtual reality project."
|
||||
size: 14
|
||||
}
|
||||
Item { height: 5; width: 1 }
|
||||
Image {
|
||||
id: hifiLogo
|
||||
width: 200; height: 50
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "../../../images/about-highfidelity.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,56 @@
|
|||
"/": "/0.155245,-0.941538,23.9289/0,0.791589,0,0.611053"
|
||||
},
|
||||
"Entities": [
|
||||
{
|
||||
"id": "{0a199807-4a83-4286-b09c-f21124627c3e}",
|
||||
"type": "Box",
|
||||
"name": "Config Wizard Loader",
|
||||
"lastEdited": 1613737207915514,
|
||||
"visible": false,
|
||||
"position": {
|
||||
"x": -1.2722,
|
||||
"y": 0.4266,
|
||||
"z": 24.2307
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.20000000298023224,
|
||||
"y": 0.20000000298023224,
|
||||
"z": 0.20000000298023224
|
||||
},
|
||||
"rotation": {
|
||||
"x": 0,
|
||||
"y": -0.7660443782806396,
|
||||
"z": 0,
|
||||
"w": -0.6427876949310303
|
||||
},
|
||||
"created": 1613736996738696,
|
||||
"lastEditedBy": "{ff9b500e-e450-4127-b41f-1c42be16f71b}",
|
||||
"queryAACube": {
|
||||
"x": -0.17320507764816284,
|
||||
"y": -0.17320507764816284,
|
||||
"z": -0.17320507764816284,
|
||||
"scale": 0.3464101552963257
|
||||
},
|
||||
"grab": {
|
||||
"grabbable": false
|
||||
},
|
||||
"damping": 0,
|
||||
"angularDamping": 0,
|
||||
"collisionless": true,
|
||||
"ignoreForCollisions": true,
|
||||
"script": "https://cdn-1.vircadia.com/us-e-1/DomainContent/Tutorial/Apps/configWizard/dist/wizardLoader.js",
|
||||
"color": {
|
||||
"red": 0,
|
||||
"green": 180,
|
||||
"blue": 239
|
||||
},
|
||||
"shape": "Cube",
|
||||
"clientOnly": false,
|
||||
"avatarEntity": false,
|
||||
"localEntity": false,
|
||||
"faceCamera": false,
|
||||
"isFacingAvatar": false
|
||||
},
|
||||
{
|
||||
"id": "{eb485a2d-2040-42f6-a960-51c88e2434b9}",
|
||||
"type": "Box",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Vlad Stelmahovsky on 15/5/2018.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -40,6 +41,10 @@ QString AboutUtil::getBuildVersion() const {
|
|||
return BuildInfo::VERSION;
|
||||
}
|
||||
|
||||
QString AboutUtil::getReleaseName() const {
|
||||
return BuildInfo::RELEASE_NAME;
|
||||
}
|
||||
|
||||
QString AboutUtil::getQtVersion() const {
|
||||
return qVersion();
|
||||
}
|
||||
|
@ -57,15 +62,15 @@ void AboutUtil::openUrl(const QString& url) const {
|
|||
|
||||
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
if (tablet->getToolbarMode()) {
|
||||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
if (tablet->getToolbarMode() && offscreenUI) {
|
||||
offscreenUI->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
newObject->setProperty("url", url);
|
||||
});
|
||||
} else {
|
||||
if (!hmd->getShouldShowTablet() && !qApp->isHMDMode()) {
|
||||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
if (!hmd->getShouldShowTablet() && !qApp->isHMDMode() && offscreenUI) {
|
||||
offscreenUI->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
newObject->setProperty("url", url);
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -30,12 +30,14 @@
|
|||
* <em>Read-only.</em>
|
||||
* @property {string} buildDate - The build date of Interface that is currently running. <em>Read-only.</em>
|
||||
* @property {string} buildVersion - The build version of Interface that is currently running. <em>Read-only.</em>
|
||||
* @property {string} releaseName - The release codename of the version that Interface is currently running. <em>Read-only.</em>
|
||||
* @property {string} qtVersion - The Qt version used in Interface that is currently running. <em>Read-only.</em>
|
||||
*
|
||||
* @example <caption>Report information on the version of Interface currently running.</caption>
|
||||
* print("Interface platform: " + About.platform);
|
||||
* print("Interface build date: " + About.buildDate);
|
||||
* print("Interface version: " + About.buildVersion);
|
||||
* print("Interface release name: " + About.releaseName);
|
||||
* print("Qt version: " + About.qtVersion);
|
||||
*/
|
||||
|
||||
|
@ -66,6 +68,7 @@ class AboutUtil : public QObject {
|
|||
Q_PROPERTY(QString platform READ getPlatformName CONSTANT)
|
||||
Q_PROPERTY(QString buildDate READ getBuildDate CONSTANT)
|
||||
Q_PROPERTY(QString buildVersion READ getBuildVersion CONSTANT)
|
||||
Q_PROPERTY(QString releaseName READ getReleaseName CONSTANT)
|
||||
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
|
||||
public:
|
||||
static AboutUtil* getInstance();
|
||||
|
@ -74,6 +77,7 @@ public:
|
|||
QString getPlatformName() const { return "Vircadia"; }
|
||||
QString getBuildDate() const;
|
||||
QString getBuildVersion() const;
|
||||
QString getReleaseName() const;
|
||||
QString getQtVersion() const;
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -254,6 +254,7 @@
|
|||
|
||||
#include "AboutUtil.h"
|
||||
#include "ExternalResource.h"
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <VersionHelpers.h>
|
||||
|
@ -1168,6 +1169,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
if (!DISABLE_WATCHDOG) {
|
||||
auto deadlockWatchdogThread = new DeadlockWatchdogThread();
|
||||
deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId());
|
||||
connect(deadlockWatchdogThread, &QThread::started, [] { setThreadName("DeadlockWatchdogThread"); });
|
||||
deadlockWatchdogThread->start();
|
||||
|
||||
// Pause the deadlock watchdog when we sleep, or it might
|
||||
|
@ -1304,6 +1306,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_entityServerConnectionTimer.setSingleShot(true);
|
||||
connect(&_entityServerConnectionTimer, &QTimer::timeout, this, &Application::setFailedToConnectToEntityServer);
|
||||
|
||||
connect(&domainHandler, &DomainHandler::confirmConnectWithoutAvatarEntities,
|
||||
this, &Application::confirmConnectWithoutAvatarEntities);
|
||||
|
||||
connect(&domainHandler, &DomainHandler::connectedToDomain, this, [this]() {
|
||||
if (!isServerlessMode()) {
|
||||
_entityServerConnectionTimer.setInterval(ENTITY_SERVER_ADDED_TIMEOUT);
|
||||
|
@ -2455,13 +2460,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
EntityItem::setBillboardRotationOperator([](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) {
|
||||
BillboardModeHelpers::setBillboardRotationOperator([](const glm::vec3& position, const glm::quat& rotation,
|
||||
BillboardMode billboardMode, const glm::vec3& frustumPos, bool rotate90x) {
|
||||
const glm::quat ROTATE_90X = glm::angleAxis(-(float)M_PI_2, Vectors::RIGHT);
|
||||
if (billboardMode == BillboardMode::YAW) {
|
||||
//rotate about vertical to face the camera
|
||||
glm::vec3 dPosition = frustumPos - position;
|
||||
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
|
||||
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
|
||||
return glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
|
||||
glm::quat result = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)) * rotation;
|
||||
if (rotate90x) {
|
||||
result *= ROTATE_90X;
|
||||
}
|
||||
return result;
|
||||
} else if (billboardMode == BillboardMode::FULL) {
|
||||
// use the referencial from the avatar, y isn't always up
|
||||
glm::vec3 avatarUP = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * Vectors::UP;
|
||||
|
@ -2470,18 +2481,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// make sure s is not NaN for any component
|
||||
if (glm::length2(s) > 0.0f) {
|
||||
return glm::conjugate(glm::toQuat(glm::lookAt(frustumPos, position, avatarUP)));
|
||||
glm::quat result = glm::conjugate(glm::toQuat(glm::lookAt(frustumPos, position, avatarUP))) * rotation;
|
||||
if (rotate90x) {
|
||||
result *= ROTATE_90X;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return rotation;
|
||||
});
|
||||
EntityItem::setPrimaryViewFrustumPositionOperator([this]() {
|
||||
BillboardModeHelpers::setPrimaryViewFrustumPositionOperator([this]() {
|
||||
ViewFrustum viewFrustum;
|
||||
copyViewFrustum(viewFrustum);
|
||||
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) {
|
||||
bool isTablet = url == TabletScriptingInterface::QML;
|
||||
|
@ -2977,6 +2992,8 @@ Application::~Application() {
|
|||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// 26 Feb 2021 - Tried re-enabling this call but OSX still crashes on exit.
|
||||
//
|
||||
// 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not
|
||||
// fully understood whether it might cause other unknown crashes (B).
|
||||
//
|
||||
|
@ -3561,7 +3578,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
|||
_desktopRootItemCreated = true;
|
||||
}
|
||||
|
||||
void Application::userKickConfirmation(const QUuid& nodeID) {
|
||||
void Application::userKickConfirmation(const QUuid& nodeID, unsigned int banFlags) {
|
||||
auto avatarHashMap = DependencyManager::get<AvatarHashMap>();
|
||||
auto avatar = avatarHashMap->getAvatarBySessionID(nodeID);
|
||||
|
||||
|
@ -3586,7 +3603,7 @@ void Application::userKickConfirmation(const QUuid& nodeID) {
|
|||
// ask the NodeList to kick the user with the given session ID
|
||||
|
||||
if (yes) {
|
||||
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);
|
||||
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID, banFlags);
|
||||
}
|
||||
|
||||
DependencyManager::get<UsersScriptingInterface>()->setWaitForKickResponse(false);
|
||||
|
@ -4001,7 +4018,7 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
// If this is a first run we short-circuit the address passed in
|
||||
if (_firstRun.get()) {
|
||||
if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) {
|
||||
if (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) {
|
||||
DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS);
|
||||
Menu::getInstance()->triggerOption(MenuOption::HomeLocation);
|
||||
}
|
||||
|
@ -5196,6 +5213,7 @@ void getCpuUsage(vec3& systemAndUser) {
|
|||
void setupCpuMonitorThread() {
|
||||
initCpuUsage();
|
||||
auto cpuMonitorThread = QThread::currentThread();
|
||||
setThreadName("CPU Monitor Thread");
|
||||
|
||||
QTimer* timer = new QTimer();
|
||||
timer->setInterval(50);
|
||||
|
@ -7180,6 +7198,10 @@ void Application::updateWindowTitle() const {
|
|||
QString buildVersion = " - Vircadia - "
|
||||
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
||||
+ " " + applicationVersion();
|
||||
|
||||
if (BuildInfo::RELEASE_NAME != "") {
|
||||
buildVersion += " - " + BuildInfo::RELEASE_NAME;
|
||||
}
|
||||
|
||||
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
|
||||
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
|
@ -9155,6 +9177,32 @@ void Application::setShowBulletConstraintLimits(bool value) {
|
|||
_physicsEngine->setShowBulletConstraintLimits(value);
|
||||
}
|
||||
|
||||
void Application::confirmConnectWithoutAvatarEntities() {
|
||||
|
||||
if (_confirmConnectWithoutAvatarEntitiesDialog) {
|
||||
// Dialog is already displayed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getMyAvatar()->hasAvatarEntities()) {
|
||||
// No avatar entities so continue with login.
|
||||
DependencyManager::get<NodeList>()->getDomainHandler().setCanConnectWithoutAvatarEntities(true);
|
||||
return;
|
||||
}
|
||||
|
||||
QString continueMessage = "Your wearables will not display on this domain. Continue?";
|
||||
_confirmConnectWithoutAvatarEntitiesDialog = OffscreenUi::asyncQuestion("Continue Without Wearables", continueMessage,
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (_confirmConnectWithoutAvatarEntitiesDialog->getDialogItem()) {
|
||||
QObject::connect(_confirmConnectWithoutAvatarEntitiesDialog, &ModalDialogListener::response, this, [=](QVariant answer) {
|
||||
QObject::disconnect(_confirmConnectWithoutAvatarEntitiesDialog, &ModalDialogListener::response, this, nullptr);
|
||||
_confirmConnectWithoutAvatarEntitiesDialog = nullptr;
|
||||
bool shouldConnect = (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes);
|
||||
DependencyManager::get<NodeList>()->getDomainHandler().setCanConnectWithoutAvatarEntities(shouldConnect);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Application::createLoginDialog() {
|
||||
const glm::vec3 LOGIN_DIMENSIONS { 0.89f, 0.5f, 0.01f };
|
||||
const auto OFFSET = glm::vec2(0.7f, -0.1f);
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
#include <shared/ConicalViewFrustum.h>
|
||||
#include <shared/FileLogger.h>
|
||||
#include <RunningMarker.h>
|
||||
#include <ModerationFlags.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "FancyCamera.h"
|
||||
|
@ -325,6 +327,8 @@ public:
|
|||
int getOtherAvatarsReplicaCount() { return DependencyManager::get<AvatarHashMap>()->getReplicaCount(); }
|
||||
void setOtherAvatarsReplicaCount(int count) { DependencyManager::get<AvatarHashMap>()->setReplicaCount(count); }
|
||||
|
||||
void confirmConnectWithoutAvatarEntities();
|
||||
|
||||
bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; }
|
||||
void createLoginDialog();
|
||||
void updateLoginDialogPosition();
|
||||
|
@ -608,7 +612,7 @@ private:
|
|||
void toggleTabletUI(bool shouldOpen = false) const;
|
||||
bool shouldCaptureMouse() const;
|
||||
|
||||
void userKickConfirmation(const QUuid& nodeID);
|
||||
void userKickConfirmation(const QUuid& nodeID, unsigned int banFlags = ModerationFlags::getDefaultBanFlags());
|
||||
|
||||
MainWindow* _window;
|
||||
QElapsedTimer& _sessionRunTimer;
|
||||
|
@ -723,6 +727,8 @@ private:
|
|||
bool _loginDialogPoppedUp{ false };
|
||||
bool _desktopRootItemCreated{ false };
|
||||
|
||||
ModalDialogListener* _confirmConnectWithoutAvatarEntitiesDialog { nullptr };
|
||||
|
||||
bool _developerMenuVisible{ false };
|
||||
QString _previousAvatarSkeletonModel;
|
||||
float _previousAvatarTargetScale;
|
||||
|
|
|
@ -61,7 +61,6 @@ void Bookmarks::deleteBookmark(const QString& bookmarkName) {
|
|||
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark) {
|
||||
Menu* menubar = Menu::getInstance();
|
||||
if (contains(bookmarkName)) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
ModalDialogListener* dlg = OffscreenUi::asyncWarning("Duplicate Bookmark",
|
||||
"The bookmark name you entered already exists in your list.",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
|
|
@ -35,6 +35,7 @@ void ConnectionMonitor::init() {
|
|||
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::confirmConnectWithoutAvatarEntities, this, &ConnectionMonitor::stopTimer);
|
||||
connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState);
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::loginComplete, this, &ConnectionMonitor::startTimer);
|
||||
|
|
|
@ -509,7 +509,7 @@ Menu::Menu() {
|
|||
|
||||
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::MaterialProceduralShaders, 0, false);
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
MeshPartPayload::enableMaterialProceduralShaders = action->isChecked();
|
||||
ModelMeshPartPayload::enableMaterialProceduralShaders = action->isChecked();
|
||||
});
|
||||
|
||||
{
|
||||
|
@ -556,8 +556,6 @@ Menu::Menu() {
|
|||
});
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ToggleHipsFollowing, 0, false,
|
||||
avatar.get(), SLOT(setToggleHips(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBaseOfSupport, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawBaseOfSupport(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
|
||||
|
|
|
@ -211,7 +211,6 @@ namespace MenuOption {
|
|||
const QString ThirdPerson = "Third Person Legacy";
|
||||
const QString ThreePointCalibration = "3 Point Calibration";
|
||||
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 TransmitterDrive = "Transmitter Drive";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
|
|
|
@ -544,7 +544,7 @@ void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities)
|
|||
QUuid entityOwnerID = entity->getOwningAvatarID();
|
||||
AvatarSharedPointer avatar = getAvatarBySessionID(entityOwnerID);
|
||||
if (avatar) {
|
||||
avatar->clearAvatarEntity(entity->getID());
|
||||
avatar->clearAvatarEntityInternal(entity->getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -767,6 +767,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
glm::vec3 rayDirectionInv = { rayDirection.x != 0.0f ? 1.0f / rayDirection.x : INFINITY,
|
||||
rayDirection.y != 0.0f ? 1.0f / rayDirection.y : INFINITY,
|
||||
rayDirection.z != 0.0f ? 1.0f / rayDirection.z : INFINITY };
|
||||
glm::vec3 viewFrustumPos = BillboardModeHelpers::getPrimaryViewFrustumPosition();
|
||||
|
||||
for (auto &hit : physicsResults) {
|
||||
auto avatarID = hit._intersectWithAvatar;
|
||||
|
@ -842,7 +843,8 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
|||
BoxFace subMeshFace = BoxFace::UNKNOWN_FACE;
|
||||
glm::vec3 subMeshSurfaceNormal;
|
||||
QVariantMap subMeshExtraInfo;
|
||||
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, subMeshDistance, subMeshFace, subMeshSurfaceNormal, subMeshExtraInfo, true, false)) {
|
||||
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, viewFrustumPos, subMeshDistance,
|
||||
subMeshFace, subMeshSurfaceNormal, subMeshExtraInfo, true, false)) {
|
||||
rayAvatarResult._distance = subMeshDistance;
|
||||
rayAvatarResult._intersectionPoint = ray.origin + subMeshDistance * rayDirection;
|
||||
rayAvatarResult._intersectionNormal = subMeshSurfaceNormal;
|
||||
|
@ -932,6 +934,7 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector
|
|||
std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator);
|
||||
}
|
||||
|
||||
glm::vec3 viewFrustumPos = BillboardModeHelpers::getPrimaryViewFrustumPosition();
|
||||
for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) {
|
||||
const SortedAvatar& sortedAvatar = *it;
|
||||
// We can exit once avatarCapsuleDistance > bestDistance
|
||||
|
@ -944,7 +947,7 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector
|
|||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel();
|
||||
if (avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal, extraInfo, true)) {
|
||||
if (avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, viewFrustumPos, parabolicDistance, face, surfaceNormal, extraInfo, true)) {
|
||||
if (parabolicDistance < result.parabolicDistance) {
|
||||
result.intersects = true;
|
||||
result.avatarID = sortedAvatar.second->getID();
|
||||
|
|
|
@ -58,7 +58,9 @@ bool AvatarPackager::open() {
|
|||
|
||||
if (tablet->getToolbarMode()) {
|
||||
static const QUrl url{ "hifi/AvatarPackagerWindow.qml" };
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "AvatarPackager", packageModelDialogCreated);
|
||||
if (auto offscreenUI = DependencyManager::get<OffscreenUi>()) {
|
||||
offscreenUI->show(url, "AvatarPackager", packageModelDialogCreated);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -283,15 +283,16 @@ class MyAvatar : public Avatar {
|
|||
* the value.</p>
|
||||
* @property {number} analogPlusSprintSpeed - The sprint (run) speed of your avatar for the "AnalogPlus" control scheme.
|
||||
* @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior.
|
||||
* @property {number} isInSittingState - <code>true</code> if the user wearing the HMD is determined to be sitting
|
||||
* (avatar leaning is disabled, recentering is enabled), <code>false</code> if the user wearing the HMD is
|
||||
* determined to be standing (avatar leaning is enabled, and avatar recenters if it leans too far).
|
||||
* If <code>userRecenterModel == 2</code> (i.e., "auto") the property value automatically updates as the user sits
|
||||
* or stands, unless <code>isSitStandStateLocked == true</code>. Setting the property value overrides the current
|
||||
* sitting / standing state, which is updated when the user next sits or stands unless
|
||||
* <code>isSitStandStateLocked == true</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} isInSittingState - <code>true</code> if the user wearing the HMD is determined to be sitting;
|
||||
* <code>false</code> if the user wearing the HMD is determined to be standing. This can affect whether the avatar
|
||||
* is allowed to stand, lean or recenter its footing, depending on user preferences.
|
||||
* The property value automatically updates as the user sits or stands. Setting the property value overrides the current
|
||||
* sitting / standing state, which is updated when the user next sits or stands.
|
||||
* @property {boolean} isSitStandStateLocked - <code>true</code> to lock the avatar sitting/standing state, i.e., use this
|
||||
* to disable automatically changing state.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.
|
||||
* See also: <code>getUserRecenterModel</code> and <code>setUserRecenterModel</code>.</p>
|
||||
* @property {boolean} allowTeleporting - <code>true</code> if teleporting is enabled in the Interface settings,
|
||||
* <code>false</code> if it isn't. <em>Read-only.</em>
|
||||
*
|
||||
|
@ -413,8 +414,8 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed NOTIFY walkBackwardSpeedChanged);
|
||||
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed NOTIFY sprintSpeedChanged);
|
||||
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);
|
||||
Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel);
|
||||
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked);
|
||||
Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel); // Deprecated
|
||||
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked); // Deprecated
|
||||
Q_PROPERTY(bool allowTeleporting READ getAllowTeleporting)
|
||||
|
||||
const QString DOMINANT_LEFT_HAND = "left";
|
||||
|
@ -519,6 +520,7 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* <p>Specifies different avatar leaning and recentering behaviors.</p>
|
||||
* <p class="important">Deprecated: This type is deprecated and will be removed.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
|
||||
|
@ -549,6 +551,29 @@ public:
|
|||
};
|
||||
Q_ENUM(SitStandModelType)
|
||||
|
||||
// Note: The option strings in setupPreferences (PreferencesDialog.cpp) must match this order.
|
||||
enum class AllowAvatarStandingPreference : uint {
|
||||
WhenUserIsStanding,
|
||||
Always,
|
||||
Count,
|
||||
Default = Always
|
||||
};
|
||||
Q_ENUM(AllowAvatarStandingPreference)
|
||||
|
||||
// Note: The option strings in setupPreferences (PreferencesDialog.cpp) must match this order.
|
||||
enum class AllowAvatarLeaningPreference : uint {
|
||||
WhenUserIsStanding,
|
||||
Always,
|
||||
Never,
|
||||
AlwaysNoRecenter, // experimental
|
||||
Count,
|
||||
Default = WhenUserIsStanding
|
||||
};
|
||||
Q_ENUM(AllowAvatarLeaningPreference)
|
||||
|
||||
static const std::array<QString, (uint)AllowAvatarStandingPreference::Count> allowAvatarStandingPreferenceStrings;
|
||||
static const std::array<QString, (uint)AllowAvatarLeaningPreference::Count> allowAvatarLeaningPreferenceStrings;
|
||||
|
||||
explicit MyAvatar(QThread* thread);
|
||||
virtual ~MyAvatar();
|
||||
|
||||
|
@ -1417,7 +1442,6 @@ public:
|
|||
controller::Pose getControllerPoseInSensorFrame(controller::Action action) const;
|
||||
controller::Pose getControllerPoseInWorldFrame(controller::Action action) const;
|
||||
controller::Pose getControllerPoseInAvatarFrame(controller::Action action) const;
|
||||
glm::quat getOffHandRotation() const;
|
||||
|
||||
bool hasDriveInput() const;
|
||||
|
||||
|
@ -1430,6 +1454,7 @@ public:
|
|||
|
||||
void removeWornAvatarEntity(const EntityItemID& entityID);
|
||||
void clearWornAvatarEntities();
|
||||
bool hasAvatarEntities() const;
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether your avatar is flying.
|
||||
|
@ -1596,7 +1621,7 @@ public:
|
|||
* @function MyAvatar.getAvatarScale
|
||||
* @returns {number} The target scale for the avatar, range <code>0.005</code> – <code>1000.0</code>.
|
||||
*/
|
||||
Q_INVOKABLE float getAvatarScale();
|
||||
Q_INVOKABLE float getAvatarScale() const;
|
||||
|
||||
/**jsdoc
|
||||
* Sets the target scale of the avatar. The target scale is the desired scale of the avatar without any restrictions on
|
||||
|
@ -1709,7 +1734,7 @@ public:
|
|||
|
||||
// derive avatar body position and orientation from the current HMD Sensor location.
|
||||
// results are in sensor frame (-z forward)
|
||||
glm::mat4 deriveBodyFromHMDSensor() const;
|
||||
glm::mat4 deriveBodyFromHMDSensor(const bool forceFollowYPos = false) const;
|
||||
|
||||
glm::mat4 getSpine2RotationRigSpace() const;
|
||||
|
||||
|
@ -1753,10 +1778,14 @@ public:
|
|||
bool getIsInWalkingState() const;
|
||||
void setIsInSittingState(bool isSitting);
|
||||
bool getIsInSittingState() const;
|
||||
void setUserRecenterModel(MyAvatar::SitStandModelType modelName);
|
||||
MyAvatar::SitStandModelType getUserRecenterModel() const;
|
||||
void setIsSitStandStateLocked(bool isLocked);
|
||||
bool getIsSitStandStateLocked() const;
|
||||
void setUserRecenterModel(MyAvatar::SitStandModelType modelName); // Deprecated, will be removed.
|
||||
MyAvatar::SitStandModelType getUserRecenterModel() const; // Deprecated, will be removed.
|
||||
void setIsSitStandStateLocked(bool isLocked); // Deprecated, will be removed.
|
||||
bool getIsSitStandStateLocked() const; // Deprecated, will be removed.
|
||||
void setAllowAvatarStandingPreference(const AllowAvatarStandingPreference preference);
|
||||
AllowAvatarStandingPreference getAllowAvatarStandingPreference() const;
|
||||
void setAllowAvatarLeaningPreference(const AllowAvatarLeaningPreference preference);
|
||||
AllowAvatarLeaningPreference getAllowAvatarLeaningPreference() const;
|
||||
void setWalkSpeed(float value);
|
||||
float getWalkSpeed() const;
|
||||
void setWalkBackwardSpeed(float value);
|
||||
|
@ -1772,7 +1801,7 @@ public:
|
|||
void setAnalogPlusSprintSpeed(float value);
|
||||
float getAnalogPlusSprintSpeed() const;
|
||||
void setSitStandStateChange(bool stateChanged);
|
||||
float getSitStandStateChange() const;
|
||||
bool getSitStandStateChange() const;
|
||||
void updateSitStandState(float newHeightReading, float dt);
|
||||
|
||||
QVector<QString> getScriptUrls();
|
||||
|
@ -1911,6 +1940,8 @@ public:
|
|||
|
||||
void avatarEntityDataToJson(QJsonObject& root) const override;
|
||||
|
||||
void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload) override;
|
||||
|
||||
/**jsdoc
|
||||
* @comment Uses the base class's JSDoc.
|
||||
*/
|
||||
|
@ -1989,6 +2020,10 @@ public:
|
|||
glm::vec3 getLookAtPivotPoint();
|
||||
glm::vec3 getCameraEyesPosition(float deltaTime);
|
||||
bool isJumping();
|
||||
bool getHMDCrouchRecenterEnabled() const;
|
||||
bool isAllowedToLean() const;
|
||||
bool areFeetTracked() const { return _isBodyPartTracked._feet; }; // Determine if the feet are under direct control.
|
||||
bool areHipsTracked() const { return _isBodyPartTracked._hips; }; // Determine if the hips are under direct control.
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -2245,12 +2280,6 @@ public slots:
|
|||
*/
|
||||
bool getEnableMeshVisible() const override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.storeAvatarEntityDataPayload
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload) override;
|
||||
|
||||
/**jsdoc
|
||||
* @comment Uses the base class's JSDoc.
|
||||
*/
|
||||
|
@ -2624,6 +2653,7 @@ private slots:
|
|||
|
||||
protected:
|
||||
void handleChangedAvatarEntityData();
|
||||
void handleCanRezAvatarEntitiesChanged(bool canRezAvatarEntities);
|
||||
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override;
|
||||
virtual void forgetChild(SpatiallyNestablePointer newChild) const override;
|
||||
virtual void recalculateChildCauterization() const override;
|
||||
|
@ -2678,6 +2708,10 @@ private:
|
|||
void attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties);
|
||||
AttachmentData entityPropertiesToAttachmentData(const EntityItemProperties& properties) const;
|
||||
bool findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID);
|
||||
void addAvatarEntitiesToTree();
|
||||
|
||||
// FIXME: Rename to clearAvatarEntity() once the API call is removed.
|
||||
void clearAvatarEntityInternal(const QUuid& entityID) override;
|
||||
|
||||
bool cameraInsideHead(const glm::vec3& cameraPosition) const;
|
||||
|
||||
|
@ -2709,6 +2743,16 @@ private:
|
|||
bool _isBraking { false };
|
||||
bool _isAway { false };
|
||||
|
||||
// Indicates which parts of the body are under direct control (tracked).
|
||||
struct {
|
||||
bool _feet { false }; // Left or right foot.
|
||||
bool _feetPreviousUpdate{ false };// Value of _feet on the previous update.
|
||||
bool _hips{ false };
|
||||
bool _leftHand{ false };
|
||||
bool _rightHand{ false };
|
||||
bool _head{ false };
|
||||
} _isBodyPartTracked;
|
||||
|
||||
float _boomLength { ZOOM_DEFAULT };
|
||||
float _yawSpeed; // degrees/sec
|
||||
float _pitchSpeed; // degrees/sec
|
||||
|
@ -2791,6 +2835,7 @@ private:
|
|||
void resetLookAtRotation(const glm::vec3& avatarPosition, const glm::quat& avatarOrientation);
|
||||
void resetPointAt();
|
||||
static glm::vec3 aimToBlendValues(const glm::vec3& aimVector, const glm::quat& frameOrientation);
|
||||
void centerBodyInternal(const bool forceFollowYPos = false);
|
||||
|
||||
// Avatar Preferences
|
||||
QUrl _fullAvatarURLFromPreferences;
|
||||
|
@ -2841,26 +2886,21 @@ private:
|
|||
struct FollowHelper {
|
||||
FollowHelper();
|
||||
|
||||
enum FollowType {
|
||||
Rotation = 0,
|
||||
Horizontal,
|
||||
Vertical,
|
||||
NumFollowTypes
|
||||
};
|
||||
float _timeRemaining[NumFollowTypes];
|
||||
CharacterController::FollowTimePerType _timeRemaining;
|
||||
|
||||
void deactivate();
|
||||
void deactivate(FollowType type);
|
||||
void activate();
|
||||
void activate(FollowType type);
|
||||
void deactivate(CharacterController::FollowType type);
|
||||
void activate(CharacterController::FollowType type, const bool snapFollow);
|
||||
bool isActive() const;
|
||||
bool isActive(FollowType followType) const;
|
||||
float getMaxTimeRemaining() const;
|
||||
bool isActive(CharacterController::FollowType followType) const;
|
||||
void decrementTimeRemaining(float dt);
|
||||
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool& shouldSnapOut) const;
|
||||
bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const;
|
||||
bool shouldActivateHorizontal(const MyAvatar& myAvatar,
|
||||
const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix,
|
||||
bool& resetModeOut,
|
||||
bool& goToWalkingStateOut) const;
|
||||
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
|
||||
glm::mat4 postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
|
||||
bool getForceActivateRotation() const;
|
||||
|
@ -2869,18 +2909,23 @@ private:
|
|||
void setForceActivateVertical(bool val);
|
||||
bool getForceActivateHorizontal() const;
|
||||
void setForceActivateHorizontal(bool val);
|
||||
bool getToggleHipsFollowing() const;
|
||||
void setToggleHipsFollowing(bool followHead);
|
||||
bool _squatDetected { false };
|
||||
std::atomic<bool> _forceActivateRotation { false };
|
||||
std::atomic<bool> _forceActivateVertical { false };
|
||||
std::atomic<bool> _forceActivateHorizontal { false };
|
||||
std::atomic<bool> _toggleHipsFollowing { true };
|
||||
|
||||
private:
|
||||
bool shouldActivateHorizontal_userSitting(const MyAvatar& myAvatar,
|
||||
const glm::mat4& desiredBodyMatrix,
|
||||
const glm::mat4& currentBodyMatrix) const;
|
||||
bool shouldActivateHorizontal_userStanding(const MyAvatar& myAvatar,
|
||||
bool& resetModeOut,
|
||||
bool& goToWalkingStateOut) const;
|
||||
};
|
||||
|
||||
FollowHelper _follow;
|
||||
|
||||
bool isFollowActive(FollowHelper::FollowType followType) const;
|
||||
bool isFollowActive(CharacterController::FollowType followType) const;
|
||||
|
||||
bool _goToPending { false };
|
||||
bool _physicsSafetyPending { false };
|
||||
|
@ -2922,6 +2967,9 @@ private:
|
|||
|
||||
bool _centerOfGravityModelEnabled { true };
|
||||
bool _hmdLeanRecenterEnabled { true };
|
||||
bool _hmdCrouchRecenterEnabled {
|
||||
true
|
||||
}; // Is MyAvatar allowed to recenter vertically (stand) when the user is sitting in the real world.
|
||||
bool _sprint { false };
|
||||
|
||||
AnimPose _prePhysicsRoomPose;
|
||||
|
@ -2953,7 +3001,6 @@ private:
|
|||
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
|
||||
float _averageUserHeightSensorSpace { _userHeight.get() };
|
||||
bool _sitStandStateChange { false };
|
||||
ThreadSafeValueCache<bool> _lockSitStandState { false };
|
||||
|
||||
// max unscaled forward movement speed
|
||||
ThreadSafeValueCache<float> _defaultWalkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
|
||||
|
@ -2969,9 +3016,13 @@ private:
|
|||
float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR };
|
||||
bool _isInWalkingState { false };
|
||||
ThreadSafeValueCache<bool> _isInSittingState { false };
|
||||
ThreadSafeValueCache<MyAvatar::SitStandModelType> _userRecenterModel { MyAvatar::SitStandModelType::Auto };
|
||||
ThreadSafeValueCache<MyAvatar::AllowAvatarStandingPreference> _allowAvatarStandingPreference{
|
||||
MyAvatar::AllowAvatarStandingPreference::Default
|
||||
}; // The user preference of when MyAvatar may stand.
|
||||
ThreadSafeValueCache<MyAvatar::AllowAvatarLeaningPreference> _allowAvatarLeaningPreference{
|
||||
MyAvatar::AllowAvatarLeaningPreference::Default
|
||||
}; // The user preference of when MyAvatar may lean.
|
||||
float _sitStandStateTimer { 0.0f };
|
||||
float _squatTimer { 0.0f };
|
||||
float _tippingPoint { _userHeight.get() };
|
||||
|
||||
// load avatar scripts once when rig is ready
|
||||
|
@ -3012,7 +3063,8 @@ private:
|
|||
Setting::Handle<int> _controlSchemeIndexSetting;
|
||||
std::vector<Setting::Handle<QUuid>> _avatarEntityIDSettings;
|
||||
std::vector<Setting::Handle<QByteArray>> _avatarEntityDataSettings;
|
||||
Setting::Handle<QString> _userRecenterModelSetting;
|
||||
Setting::Handle<QString> _allowAvatarStandingPreferenceSetting;
|
||||
Setting::Handle<QString> _allowAvatarLeaningPreferenceSetting;
|
||||
|
||||
// AvatarEntities stuff:
|
||||
// We cache the "map of unfortunately-formatted-binary-blobs" because they are expensive to compute
|
||||
|
@ -3057,6 +3109,8 @@ private:
|
|||
|
||||
glm::vec3 _cameraEyesOffset;
|
||||
float _landingAfterJumpTime { 0.0f };
|
||||
|
||||
QTimer _addAvatarEntitiesToTreeTimer;
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -26,7 +26,9 @@ void MyCharacterController::RayShotgunResult::reset() {
|
|||
walkable = true;
|
||||
}
|
||||
|
||||
MyCharacterController::MyCharacterController(std::shared_ptr<MyAvatar> avatar) {
|
||||
MyCharacterController::MyCharacterController(std::shared_ptr<MyAvatar> avatar,
|
||||
const FollowTimePerType& followTimeRemainingPerType) :
|
||||
CharacterController(followTimeRemainingPerType) {
|
||||
|
||||
assert(avatar);
|
||||
_avatar = avatar;
|
||||
|
|
|
@ -23,7 +23,7 @@ class DetailedMotionState;
|
|||
|
||||
class MyCharacterController : public CharacterController {
|
||||
public:
|
||||
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar);
|
||||
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar, const FollowTimePerType& followTimeRemainingPerType);
|
||||
~MyCharacterController ();
|
||||
|
||||
void addToWorld() override;
|
||||
|
|
|
@ -65,13 +65,21 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Use the center-of-gravity model if the user and the avatar are standing, unless flying or walking.
|
||||
// If artificial standing is disabled, use center-of-gravity regardless of the user's sit/stand state.
|
||||
bool useCenterOfGravityModel =
|
||||
myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !myAvatar->getIsInWalkingState() &&
|
||||
(!myAvatar->getHMDCrouchRecenterEnabled() || !myAvatar->getIsInSittingState()) &&
|
||||
myAvatar->getHMDLeanRecenterEnabled() &&
|
||||
(myAvatar->getAllowAvatarLeaningPreference() != MyAvatar::AllowAvatarLeaningPreference::AlwaysNoRecenter);
|
||||
|
||||
glm::mat4 hipsMat;
|
||||
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState()) && myAvatar->getHMDLeanRecenterEnabled()) {
|
||||
if (useCenterOfGravityModel) {
|
||||
// then we use center of gravity model
|
||||
hipsMat = myAvatar->deriveBodyUsingCgModel();
|
||||
} else {
|
||||
// otherwise use the default of putting the hips under the head
|
||||
hipsMat = myAvatar->deriveBodyFromHMDSensor();
|
||||
hipsMat = myAvatar->deriveBodyFromHMDSensor(true);
|
||||
}
|
||||
glm::vec3 hipsPos = extractTranslation(hipsMat);
|
||||
glm::quat hipsRot = glmExtractRotation(hipsMat);
|
||||
|
@ -82,7 +90,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
|||
|
||||
// dampen hips rotation, by mixing it with the avatar orientation in sensor space
|
||||
// turning this off for center of gravity model because it is already mixed in there
|
||||
if (!(myAvatar->getCenterOfGravityModelEnabled())) {
|
||||
if (!useCenterOfGravityModel) {
|
||||
const float MIX_RATIO = 0.5f;
|
||||
hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID };
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1); }
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { return Item::Bound(); }
|
||||
template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
|
||||
PerformanceTimer perfTimer("worldBox");
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff);
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff);
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args);
|
||||
template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args);
|
||||
}
|
||||
|
||||
|
|
|
@ -415,7 +415,7 @@ gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabo
|
|||
|
||||
for (auto& key : keys) {
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setDepthTest(true, !std::get<0>(key), gpu::LESS_EQUAL);
|
||||
if (std::get<0>(key)) {
|
||||
PrepareStencil::testMask(*state);
|
||||
} else {
|
||||
|
@ -462,9 +462,9 @@ namespace render {
|
|||
template <> const ItemKey payloadGetKey(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload) {
|
||||
return payload->getKey();
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload) {
|
||||
template <> const Item::Bound payloadGetBound(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload, RenderArgs* args) {
|
||||
if (payload) {
|
||||
return payload->getBound();
|
||||
return payload->getBound(args);
|
||||
}
|
||||
return Item::Bound();
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
|
||||
void render(RenderArgs* args);
|
||||
render::Item::Bound& editBound() { return _bound; }
|
||||
const render::Item::Bound& getBound() { return _bound; }
|
||||
const render::Item::Bound& getBound(RenderArgs* args) { return _bound; }
|
||||
render::ItemKey getKey() const { return _key; }
|
||||
|
||||
void setVisible(bool visible);
|
||||
|
@ -128,7 +128,7 @@ private:
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload, RenderArgs* args);
|
||||
template <> void payloadRender(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload, RenderArgs* args);
|
||||
template <> const ShapeKey shapeGetShapeKey(const ParabolaPointer::RenderState::ParabolaRenderItem::Pointer& payload);
|
||||
}
|
||||
|
|
|
@ -76,8 +76,13 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
|
|||
"Use the field below to place your file in a specific folder or to rename it. "
|
||||
"Specifying a new folder name will automatically create that folder for you.";
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto result = offscreenUi->inputDialog(OffscreenUi::ICON_INFORMATION, "Specify Asset Path",
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
if (!offscreenUI) {
|
||||
completedCallback.call({ -1 });
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = offscreenUI->inputDialog(OffscreenUi::ICON_INFORMATION, "Specify Asset Path",
|
||||
dropEvent ? dropHelpText : helpText, mapping);
|
||||
|
||||
if (!result.isValid() || result.toString() == "") {
|
||||
|
@ -94,7 +99,7 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
|
|||
// Check for override
|
||||
if (isKnownMapping(mapping)) {
|
||||
auto message = mapping + "\n" + "This file already exists. Do you want to overwrite it?";
|
||||
auto button = offscreenUi->messageBox(OffscreenUi::ICON_QUESTION, "Overwrite File", message,
|
||||
auto button = offscreenUI->messageBox(OffscreenUi::ICON_QUESTION, "Overwrite File", message,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (button == QMessageBox::No) {
|
||||
completedCallback.call({ -1 });
|
||||
|
|
|
@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" };
|
|||
QString Audio::HMD { "VR" };
|
||||
|
||||
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> enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true };
|
||||
|
||||
|
@ -40,6 +42,8 @@ Audio::Audio() : _devices(_contextIsHMD) {
|
|||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
|
||||
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction);
|
||||
connect(client, &AudioClient::noiseReductionAutomaticChanged, this, &Audio::enableNoiseReductionAutomatic);
|
||||
connect(client, &AudioClient::noiseReductionThresholdChanged, this, &Audio::setNoiseReductionThreshold);
|
||||
connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted);
|
||||
connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation);
|
||||
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
|
||||
|
@ -47,6 +51,8 @@ Audio::Audio() : _devices(_contextIsHMD) {
|
|||
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
|
||||
connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk);
|
||||
enableNoiseReduction(enableNoiseReductionSetting.get());
|
||||
enableNoiseReductionAutomatic(enableNoiseReductionAutomaticSetting.get());
|
||||
setNoiseReductionThreshold(setNoiseReductionThresholdSetting.get());
|
||||
enableWarnWhenMuted(enableWarnWhenMutedSetting.get());
|
||||
enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get());
|
||||
onContextChanged();
|
||||
|
@ -286,6 +292,50 @@ void Audio::enableNoiseReduction(bool enable) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Audio::noiseReductionAutomatic() const {
|
||||
return resultWithReadLock<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 {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
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
|
||||
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
||||
* above the noise floor.
|
||||
* @property {boolean} noiseReductionAutomatic - <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>.
|
||||
* 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>.
|
||||
|
@ -92,6 +97,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
|
||||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
||||
Q_PROPERTY(bool noiseReductionAutomatic READ noiseReductionAutomatic WRITE enableNoiseReductionAutomatic NOTIFY noiseReductionAutomaticChanged)
|
||||
Q_PROPERTY(float noiseReductionThreshold READ getNoiseReductionThreshold WRITE setNoiseReductionThreshold NOTIFY noiseReductionThresholdChanged)
|
||||
Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged)
|
||||
Q_PROPERTY(bool acousticEchoCancellation
|
||||
READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged)
|
||||
|
@ -124,6 +131,7 @@ public:
|
|||
|
||||
bool isMuted() const;
|
||||
bool noiseReductionEnabled() const;
|
||||
bool noiseReductionAutomatic() const;
|
||||
bool warnWhenMutedEnabled() const;
|
||||
bool acousticEchoCancellationEnabled() const;
|
||||
float getInputVolume() const;
|
||||
|
@ -270,6 +278,20 @@ public:
|
|||
* @returns {number} The injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE float getSystemInjectorGain();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the noise gate threshold before your mic audio is transmitted. (Applies only if <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
|
||||
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
|
||||
|
@ -398,6 +420,23 @@ signals:
|
|||
* @returns {Signal}
|
||||
*/
|
||||
void noiseReductionChanged(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the audio input noise reduction mode is changed.
|
||||
* @function Audio.noiseReductionAutomaticChanged
|
||||
* @param {boolean} isEnabled - <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
|
||||
* Triggered when "warn when muted" is enabled or disabled.
|
||||
|
@ -512,6 +551,7 @@ public slots:
|
|||
private slots:
|
||||
void setMuted(bool muted);
|
||||
void enableNoiseReduction(bool enable);
|
||||
void enableNoiseReductionAutomatic(bool enable);
|
||||
void enableWarnWhenMuted(bool enable);
|
||||
void enableAcousticEchoCancellation(bool enable);
|
||||
void setInputVolume(float volume);
|
||||
|
@ -533,8 +573,10 @@ private:
|
|||
float _localInjectorGain { 0.0f }; // in dB
|
||||
float _systemInjectorGain { 0.0f }; // in dB
|
||||
float _pttOutputGainDesktop { 0.0f }; // in dB
|
||||
float _noiseReductionThreshold { 0.1f }; // Match default value of AudioClient::_noiseReductionThreshold.
|
||||
bool _isClipping { false };
|
||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||
bool _noiseReductionAutomatic { true }; // Match default value of AudioClient::_noiseReductionAutomatic.
|
||||
bool _enableWarnWhenMuted { true };
|
||||
bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled
|
||||
bool _contextIsHMD { false };
|
||||
|
|
|
@ -99,11 +99,16 @@ void DesktopScriptingInterface::setHUDAlpha(float alpha) {
|
|||
}
|
||||
|
||||
void DesktopScriptingInterface::show(const QString& path, const QString& title) {
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
if (!offscreenUI) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection, Q_ARG(QString, path), Q_ARG(QString, title));
|
||||
return;
|
||||
}
|
||||
DependencyManager::get<OffscreenUi>()->show(path, title);
|
||||
offscreenUI->show(path, title);
|
||||
}
|
||||
|
||||
InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString& sourceUrl, const QVariantMap& properties) {
|
||||
|
|
|
@ -96,8 +96,9 @@ bool HMDScriptingInterface::shouldShowHandControllers() const {
|
|||
|
||||
void HMDScriptingInterface::activateHMDHandMouse() {
|
||||
QWriteLocker lock(&_hmdHandMouseLock);
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", true);
|
||||
if (auto offscreenUI = DependencyManager::get<OffscreenUi>()) {
|
||||
offscreenUI->getDesktop()->setProperty("hmdHandMouseActive", true);
|
||||
}
|
||||
_hmdHandMouseCount++;
|
||||
}
|
||||
|
||||
|
@ -105,8 +106,9 @@ void HMDScriptingInterface::deactivateHMDHandMouse() {
|
|||
QWriteLocker lock(&_hmdHandMouseLock);
|
||||
_hmdHandMouseCount = std::max(_hmdHandMouseCount - 1, 0);
|
||||
if (_hmdHandMouseCount == 0) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", false);
|
||||
if (auto offscreenUI = DependencyManager::get<OffscreenUi>()) {
|
||||
offscreenUI->getDesktop()->setProperty("hmdHandMouseActive", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ PlatformInfoScriptingInterface::PlatformTier PlatformInfoScriptingInterface::get
|
|||
}
|
||||
|
||||
QStringList PlatformInfoScriptingInterface::getPlatformTierNames() {
|
||||
static const QStringList platformTierNames = { "UNKNWON", "LOW", "MID", "HIGH" };
|
||||
static const QStringList platformTierNames = { "UNKNOWN", "LOW", "MID", "HIGH" };
|
||||
return platformTierNames;
|
||||
}
|
||||
|
||||
|
|
|
@ -199,9 +199,9 @@ void WindowScriptingInterface::setInterstitialModeEnabled(bool enableInterstitia
|
|||
DependencyManager::get<NodeList>()->getDomainHandler().setInterstitialModeEnabled(enableInterstitialMode);
|
||||
}
|
||||
|
||||
bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->isPointOnDesktopWindow(point);
|
||||
bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUI ? offscreenUI->isPointOnDesktopWindow(point) : false;
|
||||
}
|
||||
|
||||
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
|
||||
|
@ -553,12 +553,14 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu
|
|||
* @typedef {number} Window.MessageBoxButton
|
||||
*/
|
||||
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
|
||||
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
|
||||
static_cast<QFlags<QMessageBox::StandardButton>>(buttons), static_cast<QMessageBox::StandardButton>(defaultButton));
|
||||
connect(messageBox, SIGNAL(selected(int)), this, SLOT(onMessageBoxSelected(int)));
|
||||
if (auto offscreenUI = DependencyManager::get<OffscreenUi>()) {
|
||||
auto messageBox = offscreenUI->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
|
||||
static_cast<QFlags<QMessageBox::StandardButton>>(buttons), static_cast<QMessageBox::StandardButton>(defaultButton));
|
||||
connect(messageBox, SIGNAL(selected(int)), this, SLOT(onMessageBoxSelected(int)));
|
||||
|
||||
_lastMessageBoxID += 1;
|
||||
_messageBoxes.insert(_lastMessageBoxID, messageBox);
|
||||
_lastMessageBoxID += 1;
|
||||
_messageBoxes.insert(_lastMessageBoxID, messageBox);
|
||||
}
|
||||
|
||||
return _lastMessageBoxID;
|
||||
}
|
||||
|
@ -646,13 +648,17 @@ void WindowScriptingInterface::setActiveDisplayPlugin(int index) {
|
|||
}
|
||||
|
||||
void WindowScriptingInterface::openWebBrowser(const QString& url) {
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
if (!offscreenUI) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "openWebBrowser", Q_ARG(const QString&, url));
|
||||
return;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
offscreenUI->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
if (!url.isEmpty()) {
|
||||
newObject->setProperty("url", url);
|
||||
}
|
||||
|
|
|
@ -67,13 +67,13 @@ void AnimStats::updateStats(bool force) {
|
|||
|
||||
// print if we are recentering or not.
|
||||
_recenterText = "Recenter: ";
|
||||
if (myAvatar->isFollowActive(MyAvatar::FollowHelper::Rotation)) {
|
||||
if (myAvatar->isFollowActive(CharacterController::FollowType::Rotation)) {
|
||||
_recenterText += "Rotation ";
|
||||
}
|
||||
if (myAvatar->isFollowActive(MyAvatar::FollowHelper::Horizontal)) {
|
||||
if (myAvatar->isFollowActive(CharacterController::FollowType::Horizontal)) {
|
||||
_recenterText += "Horizontal ";
|
||||
}
|
||||
if (myAvatar->isFollowActive(MyAvatar::FollowHelper::Vertical)) {
|
||||
if (myAvatar->isFollowActive(CharacterController::FollowType::Vertical)) {
|
||||
_recenterText += "Vertical ";
|
||||
}
|
||||
emit recenterTextChanged();
|
||||
|
|
|
@ -100,10 +100,10 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
|||
// threads, we need to use a sync object to deteremine when
|
||||
// the current UI texture is no longer being read from, and only
|
||||
// then release it back to the UI for re-use
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||
bool newTextureAvailable = offscreenUi->fetchTexture(newTextureAndFence);
|
||||
bool newTextureAvailable = offscreenUI ? offscreenUI->fetchTexture(newTextureAndFence) : false;
|
||||
if (newTextureAvailable) {
|
||||
_uiTexture->setExternalTexture(newTextureAndFence.first, newTextureAndFence.second);
|
||||
}
|
||||
|
|
|
@ -362,10 +362,11 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
object->setObjectName("InteractiveWindow");
|
||||
object->setProperty(SOURCE_PROPERTY, sourceURL);
|
||||
};
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
offscreenUi->loadInNewContext(CONTENT_WINDOW_QML, objectInitLambda, contextInitLambda);
|
||||
if (auto offscreenUI = DependencyManager::get<OffscreenUi>()) {
|
||||
// Build the event bridge and wrapper on the main thread
|
||||
offscreenUI->loadInNewContext(CONTENT_WINDOW_QML, objectInitLambda, contextInitLambda);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,9 +250,9 @@ void setupPreferences() {
|
|||
{
|
||||
auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
||||
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
|
||||
preferences->addPreference(new CheckPreference("Privacy", "Send data - High Fidelity uses information provided by your "
|
||||
preferences->addPreference(new CheckPreference("Privacy", "Send data - Vircadia uses information provided by your "
|
||||
"client to improve the product through the logging of errors, tracking of usage patterns, "
|
||||
"installation and system details. By allowing High Fidelity to collect this information "
|
||||
"installation and system details. By allowing Vircadia to collect this information "
|
||||
"you are helping to improve the product. ", getter, setter));
|
||||
}
|
||||
|
||||
|
@ -364,7 +364,7 @@ void setupPreferences() {
|
|||
auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Camera Sensitivity", getter, setter);
|
||||
preference->setMin(0.01f);
|
||||
preference->setMax(5.0f);
|
||||
preference->setStep(0.1);
|
||||
preference->setStep(0.1f);
|
||||
preference->setDecimals(2);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
@ -422,40 +422,40 @@ void setupPreferences() {
|
|||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int {
|
||||
switch (myAvatar->getUserRecenterModel()) {
|
||||
case MyAvatar::SitStandModelType::Auto:
|
||||
default:
|
||||
return 0;
|
||||
case MyAvatar::SitStandModelType::ForceSit:
|
||||
return 1;
|
||||
case MyAvatar::SitStandModelType::ForceStand:
|
||||
return 2;
|
||||
case MyAvatar::SitStandModelType::DisableHMDLean:
|
||||
return 3;
|
||||
}
|
||||
IntPreference::Getter getter = [myAvatar]() -> int {
|
||||
return static_cast<int>(myAvatar->getAllowAvatarStandingPreference());
|
||||
};
|
||||
auto setter = [myAvatar](int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
default:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::Auto);
|
||||
break;
|
||||
case 1:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceSit);
|
||||
break;
|
||||
case 2:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceStand);
|
||||
break;
|
||||
case 3:
|
||||
myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::DisableHMDLean);
|
||||
break;
|
||||
}
|
||||
|
||||
IntPreference::Setter setter = [myAvatar](const int& value) {
|
||||
myAvatar->setAllowAvatarStandingPreference(static_cast<MyAvatar::AllowAvatarStandingPreference>(value));
|
||||
};
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Force Stand / Disable Recenter", getter, setter);
|
||||
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Allow my avatar to stand", getter, setter);
|
||||
QStringList items;
|
||||
items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Standing - enables avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]";
|
||||
preference->setHeading("Avatar leaning behavior");
|
||||
items << "When I'm standing"
|
||||
<< "Always"; // Must match the order in MyAvatar::AllowAvatarStandingPreference.
|
||||
assert(items.size() == static_cast<uint>(MyAvatar::AllowAvatarStandingPreference::Count));
|
||||
preference->setHeading("Allow my avatar to stand:");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
IntPreference::Getter getter = [myAvatar]() -> int {
|
||||
return static_cast<int>(myAvatar->getAllowAvatarLeaningPreference());
|
||||
};
|
||||
|
||||
IntPreference::Setter setter = [myAvatar](const int& value) {
|
||||
myAvatar->setAllowAvatarLeaningPreference(static_cast<MyAvatar::AllowAvatarLeaningPreference>(value));
|
||||
};
|
||||
|
||||
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Allow my avatar to lean", getter, setter);
|
||||
QStringList items;
|
||||
items << "When I'm standing"
|
||||
<< "Always"
|
||||
<< "Never"
|
||||
<< "Always, no recenter (Experimental)"; // Must match the order in MyAvatar::AllowAvatarLeaningPreference.
|
||||
assert(items.size() == static_cast<uint>(MyAvatar::AllowAvatarLeaningPreference::Count));
|
||||
preference->setHeading("Allow my avatar to lean:");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
|
|
@ -248,9 +248,9 @@ private: \
|
|||
* <em>Read-only.</em>
|
||||
* @property {string} lodStatus - Description of the current LOD.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} numEntityUpdates - The number of entity updates that happened last frame.
|
||||
* @property {number} numEntityUpdates - The number of entity updates that happened last frame.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} numNeededEntityUpdates - The total number of entity updates scheduled for last frame.
|
||||
* @property {number} numNeededEntityUpdates - The total number of entity updates scheduled for last frame.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} timingStats - Details of the average time (ms) spent in and number of calls made to different parts of
|
||||
* the code. Provided only if <code>timingExpanded</code> is <code>true</code>. Only the top 10 items are provided if
|
||||
|
@ -547,8 +547,8 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, lodAngle, 0)
|
||||
STATS_PROPERTY(int, lodTargetFramerate, 0)
|
||||
STATS_PROPERTY(QString, lodStatus, QString())
|
||||
STATS_PROPERTY(int, numEntityUpdates, 0)
|
||||
STATS_PROPERTY(int, numNeededEntityUpdates, 0)
|
||||
STATS_PROPERTY(quint64, numEntityUpdates, 0)
|
||||
STATS_PROPERTY(quint64, numNeededEntityUpdates, 0)
|
||||
STATS_PROPERTY(QString, timingStats, QString())
|
||||
STATS_PROPERTY(QString, gameUpdateStats, QString())
|
||||
STATS_PROPERTY(int, serverElements, 0)
|
||||
|
|
|
@ -66,7 +66,7 @@ private:
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay);
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay);
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay, RenderArgs* args);
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
|
||||
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay);
|
||||
template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems);
|
||||
|
|
|
@ -1212,8 +1212,8 @@ float Overlays::width() {
|
|||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getWindow()->size().width();
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUI ? offscreenUI->getWindow()->size().width() : -1.0f;
|
||||
}
|
||||
|
||||
float Overlays::height() {
|
||||
|
@ -1224,8 +1224,8 @@ float Overlays::height() {
|
|||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getWindow()->size().height();
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUI ? offscreenUI->getWindow()->size().height() : -1.0f;
|
||||
}
|
||||
|
||||
void Overlays::mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event) {
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace render {
|
|||
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
|
||||
return overlay->getKey();
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) {
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
return overlay->getBounds();
|
||||
}
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
|
|
|
@ -24,13 +24,17 @@ QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* overlay)
|
|||
}
|
||||
|
||||
void QmlOverlay::buildQmlElement(const QUrl& url) {
|
||||
auto offscreenUI = DependencyManager::get<OffscreenUi>();
|
||||
if (!offscreenUI) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "buildQmlElement", Q_ARG(QUrl, url));
|
||||
return;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
|
||||
offscreenUI->load(url, [=](QQmlContext* context, QObject* object) {
|
||||
_qmlElement = dynamic_cast<QQuickItem*>(object);
|
||||
connect(_qmlElement, &QObject::destroyed, this, &QmlOverlay::qmlElementDestroyed);
|
||||
});
|
||||
|
|
|
@ -82,9 +82,9 @@ namespace render {
|
|||
template <> const ItemKey payloadGetKey(const GameWorkloadRenderItem::Pointer& payload) {
|
||||
return payload->getKey();
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload) {
|
||||
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args) {
|
||||
if (payload) {
|
||||
return payload->getBound();
|
||||
return payload->getBound(args);
|
||||
}
|
||||
return Item::Bound();
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
void render(RenderArgs* args);
|
||||
|
||||
render::Item::Bound& editBound() { return _bound; }
|
||||
const render::Item::Bound& getBound() { return _bound; }
|
||||
const render::Item::Bound& getBound(RenderArgs* args) { return _bound; }
|
||||
|
||||
void setVisible(bool visible);
|
||||
void showProxies(bool show);
|
||||
|
@ -96,7 +96,7 @@ protected:
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const GameWorkloadRenderItem::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args);
|
||||
template <> void payloadRender(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args);
|
||||
template <> const ShapeKey shapeGetShapeKey(const GameWorkloadRenderItem::Pointer& payload);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "CubicHermiteSpline.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "AnimSkeleton.h"
|
||||
|
||||
static const int MAX_TARGET_MARKERS = 30;
|
||||
static const float JOINT_CHAIN_INTERP_TIME = 0.5f;
|
||||
|
@ -66,7 +67,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, cons
|
|||
poleVectorVar(poleVectorVarIn),
|
||||
weight(weightIn),
|
||||
numFlexCoefficients(flexCoefficientsIn.size()),
|
||||
jointIndex(-1)
|
||||
jointIndex(AnimSkeleton::INVALID_JOINT_INDEX)
|
||||
{
|
||||
numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS);
|
||||
for (size_t i = 0; i < numFlexCoefficients; i++) {
|
||||
|
@ -172,14 +173,14 @@ bool debounceJointWarnings() {
|
|||
|
||||
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
|
||||
|
||||
_hipsTargetIndex = -1;
|
||||
_hipsTargetIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
|
||||
targets.reserve(_targetVarVec.size());
|
||||
|
||||
for (auto& targetVar : _targetVarVec) {
|
||||
|
||||
// update targetVar jointIndex cache
|
||||
if (targetVar.jointIndex == -1) {
|
||||
if (targetVar.jointIndex == AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
|
||||
if (jointIndex >= 0) {
|
||||
// this targetVar has a valid joint --> cache the indices
|
||||
|
@ -190,7 +191,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
|||
}
|
||||
|
||||
IKTarget target;
|
||||
if (targetVar.jointIndex != -1) {
|
||||
if (targetVar.jointIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
|
||||
target.setIndex(targetVar.jointIndex);
|
||||
if (target.getType() != IKTarget::Type::Unknown) {
|
||||
|
@ -329,7 +330,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
|||
// update the absolutePoses
|
||||
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
|
||||
auto parentIndex = _skeleton->getParentIndex((int)i);
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
|
||||
}
|
||||
}
|
||||
|
@ -351,12 +352,12 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
|||
// finally set the relative rotation of each tip to agree with absolute target rotation
|
||||
for (auto& target: targets) {
|
||||
int tipIndex = target.getIndex();
|
||||
int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : -1;
|
||||
int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
int chainIndex = targetToChainMap[tipIndex];
|
||||
bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f;
|
||||
float alpha = needsInterpolation ? getInterpolationAlpha(_prevJointChainInfoVec[chainIndex].timer) : 0.0f;
|
||||
// update rotationOnly targets that don't lie on the ik chain of other ik targets.
|
||||
if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() &&
|
||||
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX && !_rotationAccumulators[tipIndex].isDirty() &&
|
||||
(target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) {
|
||||
if (target.getType() == IKTarget::Type::RotationOnly) {
|
||||
const glm::quat& targetRotation = target.getRotation();
|
||||
|
@ -434,11 +435,11 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
|
||||
int tipIndex = target.getIndex();
|
||||
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
||||
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
|
||||
if (pivotIndex == AnimSkeleton::INVALID_JOINT_INDEX || pivotIndex == _hipsIndex) {
|
||||
return;
|
||||
}
|
||||
int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
|
||||
if (pivotsParentIndex == -1) {
|
||||
if (pivotsParentIndex == AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
// TODO?: handle case where tip's parent is root?
|
||||
return;
|
||||
}
|
||||
|
@ -485,7 +486,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
chainDepth++;
|
||||
|
||||
// descend toward root, pivoting each joint to get tip closer to target position
|
||||
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
|
||||
while (pivotIndex != _hipsIndex && pivotsParentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
|
||||
assert(chainDepth < jointChainInfoOut.jointInfoVec.size());
|
||||
|
||||
|
@ -579,12 +580,12 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
if (target.getPoleVectorEnabled()) {
|
||||
int topJointIndex = target.getIndex();
|
||||
int midJointIndex = _skeleton->getParentIndex(topJointIndex);
|
||||
if (midJointIndex != -1) {
|
||||
if (midJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
int baseJointIndex = _skeleton->getParentIndex(midJointIndex);
|
||||
if (baseJointIndex != -1) {
|
||||
if (baseJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex);
|
||||
AnimPose topPose, midPose, basePose;
|
||||
int topChainIndex = -1, baseChainIndex = -1;
|
||||
int topChainIndex = AnimSkeleton::INVALID_JOINT_INDEX, baseChainIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
const size_t MAX_CHAIN_DEPTH = 30;
|
||||
AnimPose postAbsPoses[MAX_CHAIN_DEPTH];
|
||||
AnimPose accum = absolutePoses[_hipsIndex];
|
||||
|
@ -756,7 +757,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani
|
|||
|
||||
int index = target.getIndex();
|
||||
int endIndex = _skeleton->getParentIndex(_hipsIndex);
|
||||
while (index != endIndex) {
|
||||
while (index != endIndex && index != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index);
|
||||
|
||||
float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength;
|
||||
|
@ -1460,7 +1461,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
|||
|
||||
// invalidate all targetVars
|
||||
for (auto& targetVar: _targetVarVec) {
|
||||
targetVar.jointIndex = -1;
|
||||
targetVar.jointIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
}
|
||||
|
||||
for (auto& accumulator: _rotationAccumulators) {
|
||||
|
@ -1480,18 +1481,18 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
|||
if (_hipsIndex >= 0) {
|
||||
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
|
||||
} else {
|
||||
_hipsParentIndex = -1;
|
||||
_hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
}
|
||||
|
||||
_leftHandIndex = _skeleton->nameToJointIndex("LeftHand");
|
||||
_rightHandIndex = _skeleton->nameToJointIndex("RightHand");
|
||||
} else {
|
||||
clearConstraints();
|
||||
_headIndex = -1;
|
||||
_hipsIndex = -1;
|
||||
_hipsParentIndex = -1;
|
||||
_leftHandIndex = -1;
|
||||
_rightHandIndex = -1;
|
||||
_headIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
_hipsIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
_hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
_leftHandIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
_rightHandIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1531,7 +1532,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c
|
|||
|
||||
// draw line to parent
|
||||
int parentIndex = _skeleton->getParentIndex(i);
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
||||
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
|
||||
}
|
||||
|
@ -1580,7 +1581,7 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf
|
|||
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
|
||||
|
||||
// draw line to parent
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
||||
glm::vec4 color = GRAY;
|
||||
|
||||
|
@ -1629,13 +1630,13 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
|
|||
|
||||
// draw line to parent
|
||||
int parentIndex = _skeleton->getParentIndex(i);
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
||||
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
|
||||
}
|
||||
|
||||
glm::quat parentAbsRot;
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||
parentAbsRot = poses[parentIndex].rot();
|
||||
}
|
||||
|
||||
|
@ -1733,7 +1734,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC
|
|||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||
|
||||
for (auto& target : targets) {
|
||||
if (target.getIndex() != -1 && target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||
if (target.getIndex() != AnimSkeleton::INVALID_JOINT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||
for (int i = 0; i < NUM_LIMBS; i++) {
|
||||
if (limbs[i].first == target.getIndex()) {
|
||||
int tipIndex = limbs[i].first;
|
||||
|
|
|
@ -66,7 +66,7 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
|
|||
if (_jointIndicesByName.end() != itr) {
|
||||
return itr.value();
|
||||
}
|
||||
return -1;
|
||||
return INVALID_JOINT_INDEX;
|
||||
}
|
||||
|
||||
int AnimSkeleton::getNumJoints() const {
|
||||
|
@ -80,7 +80,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const {
|
|||
do {
|
||||
chainDepth++;
|
||||
index = _parentIndices[index];
|
||||
} while (index != -1);
|
||||
} while (index != INVALID_JOINT_INDEX);
|
||||
return chainDepth;
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -108,7 +108,7 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
|
|||
std::vector<int> AnimSkeleton::getChildrenOfJoint(int jointIndex) const {
|
||||
// Children and grandchildren, etc.
|
||||
std::vector<int> result;
|
||||
if (jointIndex != -1) {
|
||||
if (jointIndex != INVALID_JOINT_INDEX) {
|
||||
for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) {
|
||||
if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) {
|
||||
result.push_back(i);
|
||||
|
@ -135,7 +135,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const {
|
|||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||
for (int i = 0; i < lastIndex; ++i) {
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||
poses[i] = poses[parentIndex] * poses[i];
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const {
|
|||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||
poses[i] = poses[parentIndex].inverse() * poses[i];
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ void AnimSkeleton::convertRelativeRotationsToAbsolute(std::vector<glm::quat>& ro
|
|||
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
||||
for (int i = 0; i < lastIndex; ++i) {
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||
rotations[i] = rotations[parentIndex] * rotations[i];
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector<glm::quat>& ro
|
|||
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||
int parentIndex = _parentIndices[i];
|
||||
if (parentIndex != -1) {
|
||||
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||
rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i];
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints,
|
|||
// so we can restore them after a future mirror operation
|
||||
_nonMirroredIndices.push_back(i);
|
||||
}
|
||||
int mirrorJointIndex = -1;
|
||||
int mirrorJointIndex = INVALID_JOINT_INDEX;
|
||||
if (_joints[i].name.startsWith("Left")) {
|
||||
QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right");
|
||||
mirrorJointIndex = nameToJointIndex(mirrorJointName);
|
||||
|
@ -350,7 +350,7 @@ std::vector<int> AnimSkeleton::lookUpJointIndices(const std::vector<QString>& jo
|
|||
result.reserve(jointNames.size());
|
||||
for (auto& name : jointNames) {
|
||||
int index = nameToJointIndex(name);
|
||||
if (index == -1) {
|
||||
if (index == INVALID_JOINT_INDEX) {
|
||||
qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with name " << name;
|
||||
}
|
||||
result.push_back(index);
|
||||
|
|
|
@ -30,6 +30,8 @@ public:
|
|||
const QString& getJointName(int jointIndex) const;
|
||||
int getNumJoints() const;
|
||||
int getChainDepth(int jointIndex) const;
|
||||
|
||||
static const int INVALID_JOINT_INDEX { -1 };
|
||||
|
||||
// the default poses are the orientations of the joints on frame 0.
|
||||
const AnimPose& getRelativeDefaultPose(int jointIndex) const;
|
||||
|
|
|
@ -1855,6 +1855,16 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJoin
|
|||
return position;
|
||||
}
|
||||
|
||||
// Get the scale factor to convert distances in the geometry frame into the unscaled rig frame.
|
||||
// Typically it will be the unit conversion from cm to m.
|
||||
float Rig::GetScaleFactorGeometryToUnscaledRig() const {
|
||||
// Normally the model offset transform will contain the avatar scale factor; we explicitly remove it here.
|
||||
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), getModelOffsetPose().rot(), getModelOffsetPose().trans());
|
||||
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * getGeometryOffsetPose();
|
||||
|
||||
return geomToRigWithoutAvatarScale.scale().x; // in practice this is always a uniform scale factor.
|
||||
}
|
||||
|
||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
|
@ -2703,10 +2713,10 @@ void Rig::computeAvatarBoundingCapsule(
|
|||
Extents totalExtents;
|
||||
totalExtents.reset();
|
||||
|
||||
// HACK by convention our Avatars are always modeled such that y=0 is the ground plane.
|
||||
// add the zero point so that our avatars will always have bounding volumes that are flush with the ground
|
||||
// HACK by convention our Avatars are always modeled such that y=0 (GEOMETRY_GROUND_Y) is the ground plane.
|
||||
// add the ground point so that our avatars will always have bounding volumes that are flush with the ground
|
||||
// even if they do not have legs (default robot)
|
||||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
totalExtents.addPoint(glm::vec3(0.0f, GEOMETRY_GROUND_Y, 0.0f));
|
||||
|
||||
// To reduce the radius of the bounding capsule to be tight with the torso, we only consider joints
|
||||
// from the head to the hips when computing the rest of the bounding capsule.
|
||||
|
@ -2747,24 +2757,20 @@ void Rig::initFlow(bool isActive) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get the vertical position of eye joints, in the rig coordinate frame, ignoring the avatar scale.
|
||||
float Rig::getUnscaledEyeHeight() const {
|
||||
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
|
||||
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), getModelOffsetPose().rot(), getModelOffsetPose().trans());
|
||||
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * getGeometryOffsetPose();
|
||||
|
||||
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
||||
// Typically it will be the unit conversion from cm to m.
|
||||
float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor.
|
||||
// Factor to scale distances in the geometry frame into the unscaled rig frame.
|
||||
float scaleFactor = GetScaleFactorGeometryToUnscaledRig();
|
||||
|
||||
int headTopJoint = indexOfJoint("HeadTop_End");
|
||||
int headJoint = indexOfJoint("Head");
|
||||
int eyeJoint = indexOfJoint("LeftEye") != -1 ? indexOfJoint("LeftEye") : indexOfJoint("RightEye");
|
||||
int toeJoint = indexOfJoint("LeftToeBase") != -1 ? indexOfJoint("LeftToeBase") : indexOfJoint("RightToeBase");
|
||||
|
||||
// Makes assumption that the y = 0 plane in geometry is the ground plane.
|
||||
// We also make that assumption in Rig::computeAvatarBoundingCapsule()
|
||||
const float GROUND_Y = 0.0f;
|
||||
|
||||
// Values from the skeleton are in the geometry coordinate frame.
|
||||
auto skeleton = getAnimSkeleton();
|
||||
if (eyeJoint >= 0 && toeJoint >= 0) {
|
||||
|
@ -2772,8 +2778,8 @@ float Rig::getUnscaledEyeHeight() const {
|
|||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||
return scaleFactor * eyeHeight;
|
||||
} else if (eyeJoint >= 0) {
|
||||
// Measure Eye joint to y = 0 plane.
|
||||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y;
|
||||
// Measure Eye joint to ground plane.
|
||||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * eyeHeight;
|
||||
} else if (headTopJoint >= 0 && toeJoint >= 0) {
|
||||
// Measure from ToeBase joint to HeadTop_End joint, then remove forehead distance.
|
||||
|
@ -2783,19 +2789,36 @@ float Rig::getUnscaledEyeHeight() const {
|
|||
} else if (headTopJoint >= 0) {
|
||||
// Measure from HeadTop_End joint to the ground, then remove forehead distance.
|
||||
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
|
||||
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y;
|
||||
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * (headHeight - headHeight * ratio);
|
||||
} else if (headJoint >= 0) {
|
||||
// Measure Head joint to the ground, then add in distance from neck to eye.
|
||||
const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||
const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT;
|
||||
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y;
|
||||
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * (neckHeight + neckHeight * ratio);
|
||||
} else {
|
||||
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the vertical position of the hips joint, in the rig coordinate frame, ignoring the avatar scale.
|
||||
float Rig::getUnscaledHipsHeight() const {
|
||||
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
||||
float scaleFactor = GetScaleFactorGeometryToUnscaledRig();
|
||||
|
||||
int hipsJoint = indexOfJoint("Hips");
|
||||
|
||||
// Values from the skeleton are in the geometry coordinate frame.
|
||||
if (hipsJoint >= 0) {
|
||||
// Measure hip joint to ground plane.
|
||||
float hipsHeight = getAnimSkeleton()->getAbsoluteDefaultPose(hipsJoint).trans().y - GEOMETRY_GROUND_Y;
|
||||
return scaleFactor * hipsHeight;
|
||||
} else {
|
||||
return DEFAULT_AVATAR_HIPS_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setDirectionalBlending(const QString& targetName, const glm::vec3& blendingTarget, const QString& alphaName, float alpha) {
|
||||
_animVars.set(targetName, blendingTarget);
|
||||
_animVars.set(alphaName, alpha);
|
||||
|
|
|
@ -251,6 +251,7 @@ public:
|
|||
Flow& getFlow() { return _internalFlow; }
|
||||
|
||||
float getUnscaledEyeHeight() const;
|
||||
float getUnscaledHipsHeight() const;
|
||||
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) const;
|
||||
|
||||
int getOverrideJointCount() const;
|
||||
|
@ -287,6 +288,11 @@ protected:
|
|||
glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
|
||||
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const;
|
||||
|
||||
// Get the scale factor to convert distances in the geometry frame into the unscaled rig frame.
|
||||
float GetScaleFactorGeometryToUnscaledRig() const;
|
||||
|
||||
// The ground plane Y position in geometry space.
|
||||
static constexpr float GEOMETRY_GROUND_Y = 0.0f;
|
||||
|
||||
AnimPose _modelOffset; // model to rig space
|
||||
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
|
||||
|
|
|
@ -1266,7 +1266,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC
|
|||
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
|
||||
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
|
||||
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1342,6 +1342,13 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
}
|
||||
}
|
||||
|
||||
float AudioClient::loudnessToLevel(float loudness) {
|
||||
float level = loudness * (1 / 32768.0f); // level in [0, 1]
|
||||
level = 6.02059991f * fastLog2f(level); // convert to dBFS
|
||||
level = (level + 48.0f) * (1 / 42.0f); // map [-48, -6] dBFS to [0, 1]
|
||||
return glm::clamp(level, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
||||
if (!_audioPaused) {
|
||||
|
||||
|
@ -1352,9 +1359,14 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
||||
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
|
||||
if (_isNoiseGateEnabled) {
|
||||
if (_isNoiseGateEnabled && _isNoiseReductionAutomatic) {
|
||||
// The audio gate includes DC removal
|
||||
audioGateOpen = _audioGate->render(samples, samples, numFrames);
|
||||
} else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic &&
|
||||
loudnessToLevel(_lastSmoothedRawInputLoudness) >= _noiseReductionThreshold) {
|
||||
audioGateOpen = _audioGate->removeDC(samples, samples, numFrames);
|
||||
} else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic) {
|
||||
audioGateOpen = false;
|
||||
} else {
|
||||
audioGateOpen = _audioGate->removeDC(samples, samples, numFrames);
|
||||
}
|
||||
|
@ -1750,6 +1762,24 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioClient::setNoiseReductionAutomatic(bool enable, bool emitSignal) {
|
||||
if (_isNoiseReductionAutomatic != enable) {
|
||||
_isNoiseReductionAutomatic = enable;
|
||||
if (emitSignal) {
|
||||
emit noiseReductionAutomaticChanged(_isNoiseReductionAutomatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::setNoiseReductionThreshold(float threshold, bool emitSignal) {
|
||||
if (_noiseReductionThreshold != threshold) {
|
||||
_noiseReductionThreshold = threshold;
|
||||
if (emitSignal) {
|
||||
emit noiseReductionThresholdChanged(_noiseReductionThreshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) {
|
||||
if (_warnWhenMuted != enable) {
|
||||
_warnWhenMuted = enable;
|
||||
|
|
|
@ -217,6 +217,12 @@ public slots:
|
|||
|
||||
void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true);
|
||||
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
|
||||
|
||||
void setNoiseReductionAutomatic(bool isNoiseGateAutomatic, bool emitSignal = true);
|
||||
bool isNoiseReductionAutomatic() const { return _isNoiseReductionAutomatic; }
|
||||
|
||||
void setNoiseReductionThreshold(float noiseReductionThreshold, bool emitSignal = true);
|
||||
float noiseReductionThreshold() const { return _noiseReductionThreshold; }
|
||||
|
||||
void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true);
|
||||
bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; }
|
||||
|
@ -265,6 +271,8 @@ signals:
|
|||
void inputVolumeChanged(float volume);
|
||||
void muteToggled(bool muted);
|
||||
void noiseReductionChanged(bool noiseReductionEnabled);
|
||||
void noiseReductionAutomaticChanged(bool noiseReductionAutomatic);
|
||||
void noiseReductionThresholdChanged(bool noiseReductionThreshold);
|
||||
void warnWhenMutedChanged(bool warnWhenMutedEnabled);
|
||||
void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled);
|
||||
void mutedByMixer();
|
||||
|
@ -310,6 +318,8 @@ private:
|
|||
friend class CheckDevicesThread;
|
||||
friend class LocalInjectorsThread;
|
||||
|
||||
float loudnessToLevel(float loudness);
|
||||
|
||||
// background tasks
|
||||
void checkDevices();
|
||||
void checkPeakValues();
|
||||
|
@ -397,6 +407,8 @@ private:
|
|||
bool _shouldEchoLocally{ false };
|
||||
bool _shouldEchoToServer{ false };
|
||||
bool _isNoiseGateEnabled{ true };
|
||||
bool _isNoiseReductionAutomatic{ true };
|
||||
float _noiseReductionThreshold{ 0.1f };
|
||||
bool _warnWhenMuted;
|
||||
bool _isAECEnabled{ true };
|
||||
|
||||
|
|
|
@ -91,8 +91,8 @@ static const float azimuthTable[NAZIMUTH][3] = {
|
|||
// A first-order shelving filter is used to minimize the disturbance in ITD.
|
||||
//
|
||||
// Loosely based on data from S. Spagnol, "Distance rendering and perception of nearby virtual sound sources
|
||||
// with a near-field filter model,” Applied Acoustics (2017)
|
||||
//
|
||||
// with a near-field filter model," Applied Acoustics (2017)
|
||||
//
|
||||
static const int NNEARFIELD = 9;
|
||||
static const float nearFieldTable[NNEARFIELD][3] = { // { b0, b1, a1 }
|
||||
{ 0.008410604f, -0.000262748f, -0.991852144f }, // gain = 1/256
|
||||
|
@ -388,7 +388,12 @@ void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames)
|
|||
void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain);
|
||||
|
||||
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
|
||||
#ifndef STACK_PROTECTOR
|
||||
// Enabling -fstack-protector on gcc causes an undefined reference to FIR_1x4_AVX512 here
|
||||
static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE);
|
||||
#else
|
||||
static auto f = cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE;
|
||||
#endif
|
||||
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <SharedUtil.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
#include "AudioConstants.h"
|
||||
#include "AudioInjector.h"
|
||||
|
@ -54,11 +55,14 @@ AudioInjectorManager::~AudioInjectorManager() {
|
|||
}
|
||||
|
||||
void AudioInjectorManager::createThread() {
|
||||
_thread = new QThread;
|
||||
_thread = new QThread();
|
||||
_thread->setObjectName("Audio Injector Thread");
|
||||
|
||||
// when the thread is started, have it call our run to handle injection of audio
|
||||
connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection);
|
||||
connect(_thread, &QThread::started, this, [this] {
|
||||
setThreadName("AudioInjectorManager");
|
||||
run();
|
||||
}, Qt::DirectConnection);
|
||||
|
||||
moveToThread(_thread);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace render {
|
|||
}
|
||||
return keyBuilder.build();
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
|
||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar, RenderArgs* args) {
|
||||
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
|
||||
if (avatarPtr) {
|
||||
return avatarPtr->getRenderBounds();
|
||||
|
@ -1444,9 +1444,7 @@ int Avatar::getJointIndex(const QString& name) const {
|
|||
}
|
||||
|
||||
withValidJointIndicesCache([&]() {
|
||||
if (_modelJointIndicesCache.contains(name)) {
|
||||
result = _modelJointIndicesCache.value(name) - 1;
|
||||
}
|
||||
result = _modelJointIndicesCache.value(name, result + 1) - 1;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
|
||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar);
|
||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar, RenderArgs* args);
|
||||
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args);
|
||||
template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems);
|
||||
}
|
||||
|
|
|
@ -156,17 +156,13 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
updateAttitude(_owningAvatar->getWorldOrientation());
|
||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());
|
||||
|
||||
Parent::simulate(deltaTime, fullUpdate);
|
||||
if (fullUpdate) {
|
||||
|
||||
Parent::simulate(deltaTime, fullUpdate);
|
||||
|
||||
// let rig compute the model offset
|
||||
glm::vec3 registrationPoint;
|
||||
if (_rig.getModelRegistrationPoint(registrationPoint)) {
|
||||
setOffset(registrationPoint);
|
||||
}
|
||||
} else {
|
||||
Parent::simulate(deltaTime, fullUpdate);
|
||||
}
|
||||
|
||||
// FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue