diff --git a/.github/workflows/dump.yml b/.github/workflows/dump.yml new file mode 100644 index 0000000000..dc7dd686d0 --- /dev/null +++ b/.github/workflows/dump.yml @@ -0,0 +1,46 @@ +name: Dump Contexts + +on: + push: + branches: + - master + + +jobs: + one: + strategy: + matrix: + os: [windows-latest, macOS-latest] + + runs-on: ${{ matrix.os }} + steps: + - name: Dump GitHub context + shell: bash + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Dump job context + shell: bash + env: + JOB_CONTEXT: ${{ toJson(job) }} + run: echo "$JOB_CONTEXT" + - name: Dump steps context + shell: bash + env: + STEPS_CONTEXT: ${{ toJson(steps) }} + run: echo "$STEPS_CONTEXT" + - name: Dump runner context + shell: bash + env: + RUNNER_CONTEXT: ${{ toJson(runner) }} + run: echo "$RUNNER_CONTEXT" + - name: Dump strategy context + shell: bash + env: + STRATEGY_CONTEXT: ${{ toJson(strategy) }} + run: echo "$STRATEGY_CONTEXT" + - name: Dump matrix context + shell: bash + env: + MATRIX_CONTEXT: ${{ toJson(matrix) }} + run: echo "$MATRIX_CONTEXT" diff --git a/.github/workflows/master_build.yml b/.github/workflows/master_build.yml new file mode 100644 index 0000000000..9ef4728cb0 --- /dev/null +++ b/.github/workflows/master_build.yml @@ -0,0 +1,256 @@ +name: Master CI Build + +on: + push: + branches: + - master + +env: + #APP_NAME: gpu-frame-player + APP_NAME: interface + BUILD_TYPE: Release + BUCKET_NAME: hifi-gh-builds + CI_BUILD: Github + CMAKE_BACKTRACE_URL: https://highfidelity.sp.backtrace.io:6098 + CMAKE_BACKTRACE_TOKEN: ${{ secrets.backtrace_token }} + CMAKE_BACKTRACE_SYMBOLS_TOKEN: ${{ secrets.backtrace_symbols_token }} + GIT_COMMIT: ${{ github.sha }} + HIFI_VCPKG_BOOTSTRAP: true + LAUNCHER_HMAC_SECRET: ${{ secrets.launcher_hmac_secret }} + OCULUS_APP_ID: '${{ secrets.oculus_app_id }}' + RELEASE_TYPE: PRODUCTION + RELEASE_DYNAMODB_V2: ReleaseManager2-ReleaseQueue-prod + STABLE_BUILD: 0 + + + # OSX specific variables + DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer + MACOSX_DEPLOYMENT_TARGET: '10.11' + + # WIN32 specific variables + PreferredToolArchitecture: X64 + +# Mac OS +#PLATFORM_CMAKE_GENERATOR=Xcode +#PLATFORM_BUILD_ARGUMENTS=--config Release --target package +#ARTIFACT_EXPRESSION=build/*.dmg,build/*.zip + +# Windows +#PLATFORM_CMAKE_GENERATOR=Visual Studio 15 2017 Win64 +#PLATFORM_BUILD_ARGUMENTS=--target package --config release +#ARTIFACT_EXPRESSION=build/*.exe,build/*.zip,*-symbols.zip + +# Ubuntu +#PLATFORM_CMAKE_GENERATOR=Unix Makefiles +#PLATFORM_BUILD_ARGUMENTS=--target all -- -j4 +#ARTIFACT_EXPRESSION=build/assignment-client/**,build/domain-server/**,build/ice-server/ice-server,build/tools/ice-client/ice-client,build/tools/ac-client/ac-client,build/tools/oven,build/ext/makefiles/nvtt/project/lib/**,build/ext/makefiles/quazip/project/lib/** + +# Android +# branch: master +# GA_TRACKING_ID: ${{ secrets.ga_tracking_id }} +# ANDROID_OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_NIGHTLY} +# ANDROID_OAUTH_CLIENT_ID=6c7d2349c0614640150db37457a1f75dce98a28ffe8f14d47f6cfae4de5b262a +# ANDROID_OAUTH_REDIRECT_URI=https://dev-android-interface.highfidelity.com/auth +# branch: !master +# GA_TRACKING_ID=UA-39558647-11 +# ANDROID_OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_RELEASE} +# ANDROID_OAUTH_CLIENT_ID= c1063ea5d0b0c405e0c9cd77351328e211a91496a3f25985a99e861f1661db1d +# ANDROID_OAUTH_REDIRECT_URI=https://android-interface.highfidelity.com/auth +# ARTIFACT_EXPRESSION=android/*.apk +# ANDROID_APK_NAME=HighFidelity-Beta-PR${RELEASE_NUMBER}-${GIT_COMMIT_SHORT}.apk +# ANDROID_BUILT_APK_NAME=interface-debug.apk +# ANDROID_APP=interface +# ANDROID_BUILD_DIR=debug +# ANDROID_BUILD_TARGET=assembleDebug +# STABLE_BUILD=0 + + + +jobs: + generate_build_number: + runs-on: ubuntu-latest + steps: + - name: Generate build number + id: buildnumber + uses: highfidelity/build-number@v3 + with: + token: ${{secrets.github_token}} + - name: Upload build number + uses: actions/upload-artifact@v1 + with: + name: BUILD_NUMBER + path: BUILD_NUMBER + + build: + strategy: + matrix: + os: [windows-latest, macOS-latest] + build_type: [full, client] + #os: [windows-latest, macOS-latest, ubuntu-latest] + # exclude: + # - os: ubuntu-latest + # build_type: client + runs-on: ${{matrix.os}} + needs: generate_build_number + steps: + - name: Download build number + uses: actions/download-artifact@v1 + with: + name: BUILD_NUMBER + - name: Restore build number + id: buildnumber + uses: highfidelity/build-number@v3 + with: + output_name: RELEASE_NUMBER + - name: Configure Build Environment 1 + shell: bash + id: buildenv1 + run: | + echo ::set-env name=UPLOAD_PREFIX::master + echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7` + # Linux build variables + if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then + echo ::set-env name=INSTALLER_EXT::tgz + fi + # Mac build variables + if [ "${{ matrix.os }}" = "macOS-latest" ]; then + echo ::set-env name=PYTHON_EXEC::python3 + echo ::set-env name=ZIP_COMMAND::zip + echo ::set-env name=ZIP_ARGS::-r + echo ::set-env name=INSTALLER_EXT::dmg + echo ::set-env name=SYMBOL_REGEX::dSYM + echo "::set-output name=symbols_archive::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-mac-symbols.zip" + fi + # Windows build variables + if [ "${{ matrix.os }}" = "windows-latest" ]; then + echo ::set-env name=PYTHON_EXEC::python + echo ::set-env name=ZIP_COMMAND::7z + echo ::set-env name=ZIP_ARGS::a + echo ::set-env name=INSTALLER_EXT::exe + echo "::set-env name=SYMBOL_REGEX::\(exe\|dll\|pdb\)" + echo "::set-output name=symbols_archive::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-win-symbols.zip" + fi + # Configuration is broken into two steps because you can't set an env var and also reference it in the same step + - name: Configure Build Environment 2 + shell: bash + run: | + echo "${{ steps.buildenv1.outputs.symbols_archive }}" + echo ::set-env name=ARTIFACT_PATTERN::HighFidelity-Beta-*.$INSTALLER_EXT + # Build type variables + if [ "${{ matrix.build_type }}" = "full" ]; then + echo ::set-env name=CLIENT_ONLY::FALSE + echo ::set-env name=INSTALLER::HighFidelity-Beta-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT + else + echo ::set-env name=CLIENT_ONLY::TRUE + echo ::set-env name=INSTALLER::HighFidelity-Beta-Interface-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT + fi + # Linux build variables + if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then + echo ::set-env name=PYTHON_EXEC::python3 + echo ::set-env name=CMAKE_EXTRA::"" + fi + # Mac build variables + if [ "${{ matrix.os }}" = "macOS-latest" ]; then + echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode" + fi + # Windows build variables + if [ "${{ matrix.os }}" = "windows-latest" ]; then + echo ::set-env name=CMAKE_EXTRA::"-A x64" + echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}} + echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx" + fi + - name: Clear Working Directory + if: matrix.os == 'windows-latest' + shell: bash + working-directory: ${{runner.workspace}} + run: rm -rf ./* + - uses: actions/checkout@v1 + with: + submodules: true + fetch-depth: 1 + - name: Create Build Directory + run: cmake -E make_directory ${{runner.workspace}}/build + - name: Decrypt Signing Key (Windows) + if: matrix.os == 'windows-latest' + working-directory: ${{runner.workspace}}/build + shell: bash + run: gpg --batch --yes -o codesign.pfx --passphrase "${{secrets.gpg_symmetric_key}}" --decrypt $GITHUB_WORKSPACE/tools/ci-scripts/codesign.pfx.gpg + - name: Import Signing Key (Windows) + if: matrix.os == 'windows-latest' + working-directory: ${{runner.workspace}}/build + shell: powershell + run: | + $mypwd=ConvertTo-SecureString -String ${{ secrets.pfx_key }} -Force -AsPlainText + Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\CurrentUser\My -FilePath ${{runner.workspace}}\build\codesign.pfx + Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\LocalMachine\My -FilePath ${{runner.workspace}}\build\codesign.pfx + - name: Install Python modules + if: matrix.os != 'ubuntu-latest' + shell: bash + run: $PYTHON_EXEC -m pip install boto3 PyGithub + - name: Configure CMake + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY $CMAKE_EXTRA + - name: Build Application + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target $APP_NAME + - name: Build Console + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target packaged-server-console + - name: Build Domain Server (FullBuild) + if: matrix.build_type == 'full' + shell: bash + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config $BUILD_TYPE --target domain-server + - name: Build Assignment Client (FullBuild) + if: matrix.build_type == 'full' + shell: bash + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config $BUILD_TYPE --target assignment-client + - name: Build Installer + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target package + - name: Sign Installer (Windows) + if: matrix.os == 'windows-latest' + shell: powershell + working-directory: C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64 + run: .\signtool.exe sign /fd sha256 /f ${{runner.workspace}}\build\codesign.pfx /p ${{secrets.pfx_key}} /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${{runner.workspace}}\build\${env:INSTALLER} + - name: Upload Artifact + if: matrix.os != 'ubuntu-latest' + shell: bash + working-directory: ${{runner.workspace}}/build + env: + AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }} + run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py + - name: Archive Symbols + if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') + working-directory: ${{runner.workspace}} + shell: bash + run: | + SYMBOLS_TEMP="symbols-temp" + mkdir $SYMBOLS_TEMP + find "./build" \( -path '*/tools/gpu-frame-player/*' -or -path '*/interface/*' -or -path '*/plugins/*' \) -regex ".*\.$SYMBOL_REGEX" -exec cp -r {} $SYMBOLS_TEMP \; + cd $SYMBOLS_TEMP + $ZIP_COMMAND $ZIP_ARGS ../${{ steps.buildenv1.outputs.symbols_archive }} . + - name: Upload Symbols + if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') + working-directory: ${{runner.workspace}} + shell: bash + run: | + curl --data-binary @${{ steps.buildenv1.outputs.symbols_archive }} "$CMAKE_BACKTRACE_URL/post?format=symbols&token=$CMAKE_BACKTRACE_SYMBOLS_TOKEN&upload_file=${{steps.buildenv1.outputs.symbols_archive}}&tag=$RELEASE_NUMBER" + # - name: Debug List Symbols + # if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') + # working-directory: ${{runner.workspace}} + # shell: bash + # run: | + # unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}" + # - name: Debug Upload Symbols Artifact + # if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') + # uses: actions/upload-artifact@v1 + # with: + # name: symbols + # path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }} diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml new file mode 100644 index 0000000000..f28cf1e66b --- /dev/null +++ b/.github/workflows/pr_build.yml @@ -0,0 +1,164 @@ +name: Pull Request CI Build + +on: + pull_request: + types: [opened, synchronize, reopened, labeled] + +env: + APP_NAME: interface + BUILD_TYPE: Release + CI_BUILD: Github + GIT_COMMIT: ${{ github.sha }} + # VCPKG did not build well on OSX disabling HIFI_VCPKG_BOOTSTRAP, which invokes a download to a working version of vcpkg + # HIFI_VCPKG_BOOTSTRAP: true + RELEASE_TYPE: PR + RELEASE_NUMBER: ${{ github.event.number }} + VERSION_CODE: ${{ github.event.number }} + + + # OSX specific variables + DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer + MACOSX_DEPLOYMENT_TARGET: '10.11' + + # WIN32 specific variables + PreferredToolArchitecture: X64 + GIT_COMMIT_SHORT: ${{ github.sha }} + + +jobs: + build: + strategy: + matrix: + os: [windows-latest, macOS-latest, ubuntu-18.04] + build_type: [full] + include: + - os: ubuntu-18.04 + build_type: full + apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 + fail-fast: false + runs-on: ${{matrix.os}} + if: github.event.action != 'labeled' || github.event.label.name == 'rebuild' + steps: + - name: Configure Build Environment 1 + shell: bash + id: buildenv1 + run: | + echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7` + echo ::set-env name=JOB_NAME::"build (${{matrix.os}}, ${{matrix.build_type}})" + + # Linux build variables + if [[ "${{ matrix.os }}" = "ubuntu-"* ]]; then + echo ::set-env name=PYTHON_EXEC::python3 + echo ::set-env name=INSTALLER_EXT::* + echo ::set-env name=CMAKE_BUILD_EXTRA::"-- -j3" + echo ::set-env name=CMAKE_EXTRA::"-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" + fi + # Mac build variables + if [ "${{ matrix.os }}" = "macOS-latest" ]; then + echo ::set-env name=PYTHON_EXEC::python3 + echo ::set-env name=INSTALLER_EXT::dmg + echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode" + fi + # Windows build variables + if [ "${{ matrix.os }}" = "windows-latest" ]; then + echo ::set-env name=PYTHON_EXEC::python + echo ::set-env name=INSTALLER_EXT::exe + echo ::set-env name=CMAKE_EXTRA::"-A x64" + fi + # Configuration is broken into two steps because you can't set an env var and also reference it in the same step + - name: Configure Build Environment 2 + shell: bash + run: | + echo "${{ steps.buildenv1.outputs.symbols_archive }}" + echo ::set-env name=ARTIFACT_PATTERN::Vircadia-Alpha-PR${{ github.event.number }}-*.$INSTALLER_EXT + # Build type variables + echo ::set-env name=INSTALLER::Vircadia-Alpha-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT + - name: Clear Working Directory + if: startsWith(matrix.os, 'windows') + shell: bash + working-directory: ${{runner.workspace}} + run: rm -rf ./* + - uses: actions/checkout@v1 + with: + submodules: true + fetch-depth: 1 + - name: Install dependencies + shell: bash + if: startsWith(matrix.os, 'ubuntu') + run: | + echo "Installing Python Modules:" + pip3 install distro || exit 1 + + echo "Updating apt repository index" + sudo apt update || exit 1 + + echo "Installing apt packages" + sudo apt install -y ${{ matrix.apt-dependencies }} || exit 1 + - name: Create Build Environment + shell: bash + run: cmake -E make_directory "${{runner.workspace}}/build" + - name: Configure CMake + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release $CMAKE_EXTRA + - name: Build Application + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target $APP_NAME $CMAKE_BUILD_EXTRA + - name: Build Domain Server + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target domain-server $CMAKE_BUILD_EXTRA + - name: Build Assignment Client + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target assignment-client $CMAKE_BUILD_EXTRA + - name: Build Console + working-directory: ${{runner.workspace}}/build + shell: bash + run: cmake --build . --config $BUILD_TYPE --target packaged-server-console $CMAKE_BUILD_EXTRA + - name: Build Installer + working-directory: ${{runner.workspace}}/build + shell: bash + run: | + echo "Retry code from https://unix.stackexchange.com/a/137639" + function fail { + echo $1 >&2 + exit 1 + } + + function retry { + local n=1 + local max=5 + local delay=15 + while true; do + "$@" && break || { + if [[ $n -lt $max ]]; then + ((n++)) + echo "Command failed. Attempt $n/$max:" + sleep $delay; + else + fail "The command has failed after $n attempts." + fi + } + done + } + retry cmake --build . --config $BUILD_TYPE --target package $CMAKE_BUILD_EXTRA + - name: Output system stats + if: ${{ always() }} + working-directory: ${{runner.workspace}}/build + shell: bash + run: | + echo "Disk usage:" + df -h + - name: Output Installer Logs + if: failure() && matrix.os == 'windows-latest' + shell: bash + working-directory: ${{runner.workspace}}/build + run: cat ./_CPack_Packages/win64/NSIS/NSISOutput.log + - name: Upload Artifact + shell: bash + working-directory: ${{runner.workspace}}/build + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: $PYTHON_EXEC "$GITHUB_WORKSPACE/tools/ci-scripts/upload_to_publish_server.py" diff --git a/BUILD.md b/BUILD.md index 87b9c14c7b..db81ecb30d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,26 +1,28 @@ # General Build Information -*Last Updated on December 21, 2019* +*Last Updated on June 27, 2020* ### OS Specific Build Guides * [Build Windows](BUILD_WIN.md) - complete instructions for Windows. * [Build Linux](BUILD_LINUX.md) - additional instructions for Linux. * [Build OSX](BUILD_OSX.md) - additional instructions for OS X. -* [Build Android](BUILD_ANDROID.md) - additional instructions for Android +* [Build Android](BUILD_ANDROID.md) - additional instructions for Android. ### Dependencies - [git](https://git-scm.com/downloads): >= 1.6 - [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. ### CMake External Project Dependencies These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required. - [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83 - [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8 -- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac) -- [OpenVR](https://github.com/ValveSoftware/openvr): 1.0.6 (Win32 only) +- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Windows) / 0.5 (Mac) +- [OpenVR](https://github.com/ValveSoftware/openvr): 1.11.11 (Windows, Linux) - [Polyvox](http://www.volumesoffun.com/): 0.2.1 - [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3 - [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3 @@ -36,7 +38,7 @@ These are not placed in your normal build tree when doing an out of source build #### CMake -Athena uses CMake to generate build files and project files for your platform. +Vircadia uses CMake to generate build files and project files for your platform. #### Qt CMake will download Qt 5.12.3 using vcpkg. @@ -49,9 +51,9 @@ This can either be entered directly into your shell session before you build or export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.12.3/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake -#### Vcpkg +#### VCPKG -Athena uses vcpkg to download and build dependencies. +Vircadia uses vcpkg to download and build dependencies. 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. @@ -61,7 +63,33 @@ 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. -#### Generating build files +#### 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 + + // The type of release. + RELEASE_TYPE=PRODUCTION|PR|DEV + + // TODO: What do these do? + PRODUCTION_BUILD=0|1 + PR_BUILD=0|1 + STABLE_BUILD=0|1 + + // TODO: What do these do? + 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. @@ -69,7 +97,19 @@ Create a build directory in the root of your checkout and then run the CMake bui cd build cmake .. -If cmake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`. +If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`. + +##### Generating a release/debug only vcpkg build + +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: + +`cmake .. -DVCPKG_BUILD_TYPE=release` + +For debug only vcpkg: + +`cmake .. -DVCPKG_BUILD_TYPE=debug` #### Variables @@ -83,13 +123,13 @@ For example, to pass the QT_CMAKE_PREFIX_PATH variable (if not using the vcpkg'e The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies). -You can point our [Cmake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency. +You can point our [CMake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency. In the examples below the variable $NAME would be replaced by the name of the dependency in uppercase, and $name would be replaced by the name of the dependency in lowercase (ex: OPENSSL_ROOT_DIR, openssl). * $NAME_ROOT_DIR - pass this variable to Cmake with the -DNAME_ROOT_DIR= flag when running Cmake to generate build files * $NAME_ROOT_DIR - set this variable in your ENV -* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name' +* HIFI_LIB_DIR - set this variable in your ENV to your Vircadia lib folder, should contain a folder '$name' ### Optional Components @@ -110,4 +150,4 @@ The following build options can be used when running CMake #### Devices You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. - \ No newline at end of file + diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index b549d222da..f6287846ed 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -1,6 +1,6 @@ # Build Linux -*Last Updated on January 20, 2020* +*Last Updated on April 11, 2020* Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file. @@ -89,6 +89,16 @@ Then checkout the main branch with: git checkout kasen/core ``` +### Using a custom Qt build + +Qt binaries are only provided for Ubuntu. In order to build on other distributions, a Qt5 install needs to be provided as follows: + +* Set `VIRCADIA_USE_PREBUILT_QT=1` +* Set `VIRCADIA_USE_QT_VERSION` to the Qt version (defaults to `5.12.3`) +* Set `HIFI_QT_BASE=/path/to/qt` + +Qt must be installed in `$HIFI_QT_BASE/$VIRCADIA_USE_QT_VERSION/qt5-install`. + ### Compiling Create the build directory: diff --git a/BUILD_LINUX_CHEATSHEET.md b/BUILD_LINUX_CHEATSHEET.md index 9e7534418a..9297f9fd7d 100644 --- a/BUILD_LINUX_CHEATSHEET.md +++ b/BUILD_LINUX_CHEATSHEET.md @@ -1,4 +1,7 @@ ## This guide is specific to Ubuntu 16.04. + +THIS DOCUMENT IS OUTDATED. + Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com ``` diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 69cfed7de9..2f9246f6a6 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,31 +1,36 @@ # Build OSX -*Last Updated on April 30, 2019* +*Last Updated on July 13, 2020* Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only macOS specific instructions are found in this document. ### Homebrew -[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some High Fidelity dependencies very simple. +[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some Vircadia dependencies very simple. - brew install cmake openssl + brew install cmake openssl npm ### Python 3 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 the OSX SDK for building. The easiest way to get this is to install Xcode from the App Store. + ### 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. -For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: +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` - export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2l - -Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change. ### Xcode -If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. +If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. 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 .. -G Xcode @@ -34,3 +39,7 @@ If `cmake` complains about Python 3 being missing, you may need to update your C 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. If the build completes successfully, you will have built targets for all components located in the `build/${target_name}/Debug` directories. + +### make + +If you build with make rather than Xcode, you can append `-j4`for assigning more threads. The number indicates the number of threads, e.g. 4. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index bdab7e6e6d..c057d9ca5d 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,6 +1,6 @@ # Build Windows -*Last Updated on January 13, 2020* +*Last Updated on May 17, 2020* This is a stand-alone guide for creating your first Vircadia build for Windows 64-bit. @@ -47,7 +47,7 @@ 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 -In the next step, you will use CMake to build Project Athena. 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. +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: * Naviagte to 'Edit the System Environment Variables' Through the start menu. @@ -68,7 +68,7 @@ To create this variable: ### Step 5. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: -`cd "%HIFI_DIR%"` +`cd "%VIRCADIA_DIR%"` `mkdir build` `cd build` @@ -78,11 +78,11 @@ Run `cmake .. -G "Visual Studio 15 Win64"`. #### If you're using Visual Studio 2019, Run `cmake .. -G "Visual Studio 16 2019" -A x64`. -Where `%HIFI_DIR%` is the directory for the highfidelity repository. +Where `%VIRCADIA_DIR%` is the directory for the Vircadia repository. ### Step 6. Making a Build -Open `%HIFI_DIR%\build\athena.sln` using Visual Studio. +Open `%VIRCADIA_DIR%\build\vircadia.sln` using Visual Studio. Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance. @@ -98,22 +98,22 @@ Restart Visual Studio again. In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run from the menu bar `Debug > Start Debugging`. -Now, you should have a full build of Project Athena and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow. +Now, you should have a full build of Vircadia and be able to run the Interface using Visual Studio. -Note: You can also run Interface by launching it from command line or File Explorer from `%HIFI_DIR%\build\interface\Release\interface.exe` +Note: You can also run Interface by launching it from command line or File Explorer from `%VIRCADIA_DIR%\build\interface\Release\interface.exe` ## Troubleshooting For any problems after Step #6, first try this: -* Delete your locally cloned copy of the highfidelity repository +* Delete your locally cloned copy of the Vircadia repository * Restart your computer * Redownload the [repository](https://github.com/kasenvr/project-athena) * Restart directions from Step #6 #### CMake gives you the same error message repeatedly after the build fails -Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. +Remove `CMakeCache.txt` found in the `%VIRCADIA_DIR%\build` directory. #### CMake can't find OpenSSL -Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. Verify that your HIFI_VCPKG_BASE environment variable is set and pointing to the correct location. Verify that the file `${HIFI_VCPKG_BASE}/installed/x64-windows/include/openssl/ssl.h` exists. +Remove `CMakeCache.txt` found in the `%VIRCADIA_DIR%\build` directory. Verify that your HIFI_VCPKG_BASE environment variable is set and pointing to the correct location. Verify that the file `${HIFI_VCPKG_BASE}/installed/x64-windows/include/openssl/ssl.h` exists. diff --git a/CMakeLists.txt b/CMakeLists.txt index b2dc89d105..eac12d5ae7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ endif() # (needs to be set before first project() call and before prebuild.py) # Will affect VCPKG dependencies if (APPLE) - set(ENV{MACOSX_DEPLOYMENT_TARGET} 10.9) + set(ENV{MACOSX_DEPLOYMENT_TARGET} 10.11) endif() set(RELEASE_TYPE "$ENV{RELEASE_TYPE}") @@ -67,8 +67,12 @@ if (HIFI_ANDROID) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) else() + set(VCPKG_BUILD_TYPE_PARAM "") + if (VCPKG_BUILD_TYPE) + set(VCPKG_BUILD_TYPE_PARAM --vcpkg-build-type ${VCPKG_BUILD_TYPE}) + endif() execute_process( - COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --release-type ${RELEASE_TYPE} --build-root ${CMAKE_BINARY_DIR} + COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --release-type ${RELEASE_TYPE} --build-root ${CMAKE_BINARY_DIR} ${VCPKG_BUILD_TYPE_PARAM} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) # squelch the Policy CMP0074 warning without requiring an update to cmake 3.12. @@ -82,9 +86,21 @@ if(NOT EXISTS "${CMAKE_BINARY_DIR}/vcpkg.cmake") endif() include("${CMAKE_BINARY_DIR}/vcpkg.cmake") -project(athena) +if (HIFI_ANDROID) + set(QT_CMAKE_PREFIX_PATH "$ENV{HIFI_ANDROID_PRECOMPILED}/qt/lib/cmake") +else() + if(NOT EXISTS "${CMAKE_BINARY_DIR}/qt.cmake") + message(FATAL_ERROR "qt configuration missing.") + endif() + include("${CMAKE_BINARY_DIR}/qt.cmake") +endif() + +option(VCPKG_APPLOCAL_DEPS OFF) + +project(vircadia) include("cmake/init.cmake") include("cmake/compiler.cmake") +option(VCPKG_APPLOCAL_DEPS OFF) add_paths_to_fixup_libs(${VCPKG_INSTALL_ROOT}/bin) add_paths_to_fixup_libs(${VCPKG_INSTALL_ROOT}/debug/bin) @@ -278,6 +294,11 @@ if (ANDROID) return() endif() +if (BUILD_GPU_FRAME_PLAYER_ONLY) + # This is for CI build testing + add_subdirectory(tools/gpu-frame-player) +else() + # add subdirectories for all targets if (BUILD_SERVER) add_subdirectory(assignment-client) @@ -290,6 +311,7 @@ endif() if (BUILD_CLIENT) add_subdirectory(interface) + add_subdirectory(screenshare) set_target_properties(interface PROPERTIES FOLDER "Apps") option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF) @@ -303,6 +325,8 @@ endif() # BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway add_subdirectory(tools) +endif() + if (BUILD_TESTS) # Turn on testing so that add_test works # MUST be in the root cmake file for ctest to work diff --git a/CODING_STANDARD.md b/CODING_STANDARD.md index fd1843e981..1c2f3cb27e 100644 --- a/CODING_STANDARD.md +++ b/CODING_STANDARD.md @@ -976,18 +976,17 @@ while (true) { #### [4.3.4] Source files (header and implementation) must include a boilerplate. -Boilerplates should include the filename, location, creator, copyright Project Athena contributors, and Apache 2.0 License -information. This should be placed at the top of the file. If editing an existing file that is copyright High Fidelity, add a -second copyright line, copyright Project Athena contributors. +Boilerplates should include the filename, creator, copyright Vircadia contributors, and Apache 2.0 License information. +This should be placed at the top of the file. If editing an existing file that is copyright High Fidelity, add a second +copyright line, copyright Vircadia contributors. ```cpp // // NodeList.h -// libraries/shared/src // // Created by Stephen Birarda on 15 Feb 2013. // Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Project Athena contributors. +// Copyright 2020 Vircadia contributors. // // This is where you could place an optional one line comment about the file. // diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a62e4b2825..aeb6f49280 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Contributing ``` git remote add upstream https://github.com/kasenvr/project-athena - git pull upstream kasen/core + git pull upstream master ``` Resolve any conflicts that arise with this step. @@ -29,7 +29,7 @@ Contributing 7. Push to your fork ``` - git push origin kasen/core + git push origin new_branch_name ``` 8. Submit a pull request diff --git a/INSTALL.md b/INSTALL.md index 10858200e7..af398ee6b3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,21 +1,21 @@ # Creating an Installer -Follow the [build guide](BUILD.md) to figure out how to build Project Athena for your platform. +Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. During generation, CMake should produce an `install` target and a `package` target. -The `install` target will copy the Project Athena targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. +The `install` target will copy the Vircadia targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt` ### Packaging -To produce an installer, run the `package` target. +To produce an installer, run the `package` target. However you will want to follow the steps specific to your platform below. #### Windows To produce an executable installer on Windows, the following are required: -1. [7-zip]() +1. [7-zip]() 1. [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.04 Install using defaults (will install to `C:\Program Files (x86)\NSIS`) @@ -56,22 +56,22 @@ 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. [npm]() +1. [Node.JS and NPM]() 1. Install version 10.15.0 LTS 1. Perform a clean cmake from a new terminal. -1. Open the `athena.sln` solution and select the Release configuration. +1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration. 1. Build the solution. 1. Build `packaged-server-console-npm-install` (found under **hidden/Server Console**) 1. Build `packaged-server-console` (found under **Server Console**) This will add 2 folders to `build\server-console\` - `server-console-win32-x64` and `x64` -1. Build CMakeTargets->PACKAGE - Installer is now available in `build\_CPack_Packages\win64\NSIS` +1. Build CMakeTargets->PACKAGE + The installer is now available in `build\_CPack_Packages\win64\NSIS` #### OS X 1. [npm]() - Install version 10.15.0 LTS + Install version 12.16.3 LTS 1. Perform a clean CMake. 1. Perform a Release build of ALL_BUILD @@ -80,3 +80,9 @@ To produce an executable installer on Windows, the following are required: Sandbox-darwin-x64 1. Perform a Release build of `package` Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop + +### 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. diff --git a/LICENSE b/LICENSE index f88e751de5..8dfe384174 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Copyright (c) 2013-2019, High Fidelity, Inc. -Copyright (c) 2019-2020, Project Athena Contributors. +Copyright (c) 2019-2020, Vircadia contributors. All rights reserved. -https://projectathena.io +https://vircadia.com Licensed under the Apache License version 2.0 (the "License"); You may not use this software except in compliance with the License. diff --git a/README.md b/README.md index f61a60d431..baf333d81f 100644 --- a/README.md +++ b/README.md @@ -12,23 +12,31 @@ Vircadia is a 3D social software project seeking to incrementally bring about a ### How to build the Interface -[For Windows](https://github.com/kasenvr/project-athena/blob/kasen/core/BUILD_WIN.md) +[For Windows](https://github.com/kasenvr/project-athena/blob/master/BUILD_WIN.md) -[For Linux](https://github.com/kasenvr/project-athena/blob/kasen/core/BUILD_LINUX.md) +[For Mac](https://github.com/kasenvr/project-athena/blob/master/BUILD_OSX.md) -[For Linux - Athena Builder](https://github.com/daleglass/athena-builder) +[For Linux](https://github.com/kasenvr/project-athena/blob/master/BUILD_LINUX.md) + +[For Linux - Vircadia Builder](https://github.com/kasenvr/vircadia-builder) ### How to deploy a Server -[For Windows and Linux](https://vircadia.com/download-vircadia/#server) +[For Windows and Linux](https://vircadia.com/deploy-a-server/) ### How to build a Server -[For Linux - Athena Builder](https://github.com/daleglass/athena-builder) +[For Linux - Vircadia Builder](https://github.com/kasenvr/vircadia-builder) + +### How to generate an Installer + +[For Windows](https://github.com/kasenvr/project-athena/blob/master/INSTALL.md) + +[For Linux - AppImage - Vircadia Builder](https://github.com/kasenvr/vircadia-builder/blob/master/README.md#building-appimages) ### Boot to Metaverse: The Goal -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, that's the dream. +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 diff --git a/README_hifi.md b/README_hifi.md index 46433a155c..325cba8d42 100644 --- a/README_hifi.md +++ b/README_hifi.md @@ -1,3 +1,5 @@ +# THIS DOCUMENT IS OUTDATED + High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR. This repository contains the source to many of the components in our @@ -15,7 +17,7 @@ Come chat with us in [our Gitter](https://gitter.im/highfidelity/hifi) if you ha Documentation ========= -Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.com), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project). +Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.com/), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project). There is also detailed [documentation on our coding standards](CODING_STANDARD.md). diff --git a/android/apps/interface/src/main/res/values/strings.xml b/android/apps/interface/src/main/res/values/strings.xml index b60caf1f2c..c1d7303f36 100644 --- a/android/apps/interface/src/main/res/values/strings.xml +++ b/android/apps/interface/src/main/res/values/strings.xml @@ -27,9 +27,9 @@ Online Sign Up SIGN UP - Creating your High Fidelity account + Creating your Vircadia account Email, username or password incorrect. - You are now signed into High Fidelity + You are now signed into Vircadia You are now logged in! Welcome Cancel diff --git a/android/apps/questInterface/src/main/AndroidManifest.xml b/android/apps/questInterface/src/main/AndroidManifest.xml index a5de47bdce..27cc18072b 100644 --- a/android/apps/questInterface/src/main/AndroidManifest.xml +++ b/android/apps/questInterface/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + - $) -else() - add_custom_command( - TARGET ${TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - $ - $/oven) -endif() + if (WIN32) + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + $ + $) + else() + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + $ + $/oven) + endif() +endif (BUILD_TOOLS) if (WIN32) package_libraries_for_deployment() diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index ad68b07bb1..59e53bb2cb 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -615,6 +615,10 @@ void Agent::setIsAvatar(bool isAvatar) { delete _avatarQueryTimer; _avatarQueryTimer = nullptr; + // Clear the skeleton model so that if agent is set to an avatar again the skeleton model is (re)loaded. + auto scriptedAvatar = DependencyManager::get(); + scriptedAvatar->setSkeletonModelURL(QUrl()); + // The avatar mixer never times out a connection (e.g., based on identity or data packets) // but rather keeps avatars in its list as long as "connected". As a result, clients timeout // when we stop sending identity, but then get woken up again by the mixer itself, which sends diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 502cf15aa2..b3344e3832 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -176,7 +176,7 @@ std::pair AssetServer::getAssetStatus(const A } else if (loaded && meta.failedLastBake) { return { AssetUtils::Error, meta.lastBakeErrors }; } - + return { AssetUtils::Pending, "" }; } @@ -199,7 +199,7 @@ void AssetServer::maybeBake(const AssetUtils::AssetPath& path, const AssetUtils: void AssetServer::createEmptyMetaFile(const AssetUtils::AssetHash& hash) { QString metaFilePath = "atp:/" + hash + "/meta.json"; QFile metaFile { metaFilePath }; - + if (!metaFile.exists()) { qDebug() << "Creating metafile for " << hash; if (metaFile.open(QFile::WriteOnly)) { @@ -285,7 +285,7 @@ void updateConsumedCores() { auto coreCount = std::thread::hardware_concurrency(); if (isInterfaceRunning) { coreCount = coreCount > MIN_CORES_FOR_MULTICORE ? CPU_AFFINITY_COUNT_HIGH : CPU_AFFINITY_COUNT_LOW; - } + } qCDebug(asset_server) << "Setting max consumed cores to " << coreCount; setMaxCores(coreCount); } @@ -931,6 +931,9 @@ void AssetServer::sendStatsPacket() { connectionStats["5. Period (us)"] = stats.packetSendPeriod; connectionStats["6. Up (Mb/s)"] = stats.sentBytes * megabitsPerSecPerByte; connectionStats["7. Down (Mb/s)"] = stats.receivedBytes * megabitsPerSecPerByte; + connectionStats["last_heard_time_msecs"] = date.toUTC().toMSecsSinceEpoch(); + connectionStats["last_heard_ago_msecs"] = date.msecsTo(QDateTime::currentDateTime()); + nodeStats["Connection Stats"] = connectionStats; using Events = udt::ConnectionStats::Stats::Event; @@ -1147,7 +1150,7 @@ bool AssetServer::deleteMappings(const AssetUtils::AssetPathList& paths) { hashesToCheckForDeletion << it->second; qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << it->second; - + _fileMappings.erase(it); } else { qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index e2f28c221e..470ab3f233 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -668,6 +668,12 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { downstreamStats["min_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMin); downstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax); downstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage); + downstreamStats["min_gap_usecs"] = static_cast(streamStats._timeGapMin); + downstreamStats["max_gap_usecs"] = static_cast(streamStats._timeGapMax); + downstreamStats["avg_gap_usecs"] = static_cast(streamStats._timeGapAverage); + downstreamStats["min_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowMin); + downstreamStats["max_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowMax); + downstreamStats["avg_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowAverage); result["downstream"] = downstreamStats; @@ -695,6 +701,13 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { upstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax); upstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage); + upstreamStats["min_gap_usecs"] = static_cast(streamStats._timeGapMin); + upstreamStats["max_gap_usecs"] = static_cast(streamStats._timeGapMax); + upstreamStats["avg_gap_usecs"] = static_cast(streamStats._timeGapAverage); + upstreamStats["min_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowMin); + upstreamStats["max_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowMax); + upstreamStats["avg_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowAverage); + result["upstream"] = upstreamStats; } else { result["upstream"] = "mic unknown"; @@ -725,6 +738,12 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { upstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax); upstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage); + upstreamStats["min_gap_usecs"] = static_cast(streamStats._timeGapMin); + upstreamStats["max_gap_usecs"] = static_cast(streamStats._timeGapMax); + upstreamStats["avg_gap_usecs"] = static_cast(streamStats._timeGapAverage); + upstreamStats["min_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowMin); + upstreamStats["max_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowMax); + upstreamStats["avg_gap_30s_usecs"] = static_cast(streamStats._timeGapWindowAverage); injectorArray.push_back(upstreamStats); } } diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index e5e9f89984..204095ab94 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -782,7 +782,7 @@ float computeGain(float masterAvatarGain, gain *= std::max(1.0f - d / (distanceLimit - ATTN_DISTANCE_REF), 0.0f); gain = std::min(gain, ATTN_GAIN_MAX); - } else { + } else if (attenuationPerDoublingInDistance < 1.0f) { // translate a positive zone setting to gain per log2(distance) const float MIN_ATTENUATION_COEFFICIENT = 0.001f; // -60dB per log2(distance) float g = glm::clamp(1.0f - attenuationPerDoublingInDistance, MIN_ATTENUATION_COEFFICIENT, 1.0f); @@ -792,6 +792,10 @@ float computeGain(float masterAvatarGain, float d = (1.0f / ATTN_DISTANCE_REF) * std::max(distance, HRTF_NEARFIELD_MIN); gain *= fastExp2f(fastLog2f(g) * fastLog2f(d)); gain = std::min(gain, ATTN_GAIN_MAX); + + } else { + // translate a zone setting of 1.0 be silent at any distance + gain = 0.0f; } return gain; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index c7ab810c9a..807f54953e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -1081,6 +1081,12 @@ void AvatarMixer::setupEntityQuery() { priorityZoneQuery["avatarPriority"] = true; priorityZoneQuery["type"] = "Zone"; + QJsonObject queryFlags; + queryFlags["includeAncestors"] = true; + queryFlags["includeDescendants"] = true; + priorityZoneQuery["flags"] = queryFlags; + priorityZoneQuery["name"] = true; // Handy for debugging. + _entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery); _slaveSharedData.entityTree = entityTree; } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 1195f0d801..f86dc7f766 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -23,8 +23,7 @@ #include "AvatarMixerSlave.h" -AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID, nodeLocalID) { +AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID, nodeLocalID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); } @@ -92,39 +91,48 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData } namespace { - using std::static_pointer_cast; +using std::static_pointer_cast; - // Operator to find if a point is within an avatar-priority (hero) Zone Entity. - struct FindPriorityZone { - glm::vec3 position; - bool isInPriorityZone { false }; - float zoneVolume { std::numeric_limits::max() }; +// Operator to find if a point is within an avatar-priority (hero) Zone Entity. +struct FindContainingZone { + glm::vec3 position; + bool isInPriorityZone { false }; + bool isInScreenshareZone { false }; + float priorityZoneVolume { std::numeric_limits::max() }; + float screenshareZoneVolume { priorityZoneVolume }; + EntityItemID screenshareZoneid{}; - static bool operation(const OctreeElementPointer& element, void* extraData) { - auto findPriorityZone = static_cast(extraData); - if (element->getAACube().contains(findPriorityZone->position)) { - const EntityTreeElementPointer entityTreeElement = static_pointer_cast(element); - entityTreeElement->forEachEntity([&findPriorityZone](EntityItemPointer item) { - if (item->getType() == EntityTypes::Zone - && item->contains(findPriorityZone->position)) { - auto zoneItem = static_pointer_cast(item); - if (zoneItem->getAvatarPriority() != COMPONENT_MODE_INHERIT) { - float volume = zoneItem->getVolumeEstimate(); - if (volume < findPriorityZone->zoneVolume) { // Smaller volume wins - findPriorityZone->isInPriorityZone = zoneItem->getAvatarPriority() == COMPONENT_MODE_ENABLED; - findPriorityZone->zoneVolume = volume; - } - } + static bool operation(const OctreeElementPointer& element, void* extraData) { + auto findContainingZone = static_cast(extraData); + if (element->getAACube().contains(findContainingZone->position)) { + const EntityTreeElementPointer entityTreeElement = static_pointer_cast(element); + entityTreeElement->forEachEntity([&findContainingZone](EntityItemPointer item) { + if (item->getType() == EntityTypes::Zone && item->contains(findContainingZone->position)) { + auto zoneItem = static_pointer_cast(item); + auto avatarPriorityProperty = zoneItem->getAvatarPriority(); + auto screenshareProperty = zoneItem->getScreenshare(); + float volume = zoneItem->getVolumeEstimate(); + if (avatarPriorityProperty != COMPONENT_MODE_INHERIT + && volume < findContainingZone->priorityZoneVolume) { // Smaller volume wins + findContainingZone->isInPriorityZone = avatarPriorityProperty == COMPONENT_MODE_ENABLED; + findContainingZone->priorityZoneVolume = volume; } - }); - return true; // Keep recursing - } else { // Position isn't within this subspace, so end recursion. - return false; - } + if (screenshareProperty != COMPONENT_MODE_INHERIT + && volume < findContainingZone->screenshareZoneVolume) { + findContainingZone->isInScreenshareZone = screenshareProperty == COMPONENT_MODE_ENABLED; + findContainingZone->screenshareZoneVolume = volume; + findContainingZone->screenshareZoneid = zoneItem->getEntityItemID(); + } + } + }); + return true; // Keep recursing + } else { // Position isn't within this subspace, so end recursion. + return false; } - }; + } +}; -} // Close anonymous namespace. +} // namespace int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveSharedData& slaveSharedData) { // pull the sequence number from the data first @@ -150,9 +158,24 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared auto newPosition = _avatar->getClientGlobalPosition(); if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) { EntityTree& entityTree = *slaveSharedData.entityTree; - FindPriorityZone findPriorityZone { newPosition } ; - entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone); - _avatar->setHasPriority(findPriorityZone.isInPriorityZone); + FindContainingZone findContainingZone{ newPosition }; + entityTree.recurseTreeWithOperation(&FindContainingZone::operation, &findContainingZone); + bool currentlyHasPriority = findContainingZone.isInPriorityZone; + if (currentlyHasPriority != _avatar->getHasPriority()) { + _avatar->setHasPriority(currentlyHasPriority); + } + bool isInScreenshareZone = findContainingZone.isInScreenshareZone; + if (isInScreenshareZone != _avatar->isInScreenshareZone() + || findContainingZone.screenshareZoneid != _avatar->getScreenshareZone()) { + _avatar->setInScreenshareZone(isInScreenshareZone); + _avatar->setScreenshareZone(findContainingZone.screenshareZoneid); + const QUuid& zoneId = isInScreenshareZone ? findContainingZone.screenshareZoneid : QUuid(); + auto nodeList = DependencyManager::get(); + auto packet = NLPacket::create(PacketType::AvatarZonePresence, 2 * NUM_BYTES_RFC4122_UUID, true); + packet->write(_avatar->getSessionUUID().toRfc4122()); + packet->write(zoneId.toRfc4122()); + nodeList->sendPacket(std::move(packet), nodeList->getDomainSockAddr()); + } _avatar->setNeedsHeroCheck(false); } @@ -217,8 +240,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, } } else { // Trying to read more bytes than available, bail - if (message.getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID + - sizeof(AvatarTraits::TraitWireSize))) { + if (message.getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID + sizeof(AvatarTraits::TraitWireSize))) { qWarning() << "Refusing to process malformed traits packet from" << message.getSenderSockAddr(); return; } @@ -234,8 +256,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, break; } - if (traitType == AvatarTraits::AvatarEntity || - traitType == AvatarTraits::Grab) { + if (traitType == AvatarTraits::AvatarEntity || traitType == AvatarTraits::Grab) { auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID); if (packetTraitVersion > instanceVersionRef) { @@ -293,7 +314,8 @@ void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& m auto simpleReceivedIt = traitVersions.simpleCBegin(); while (simpleReceivedIt != traitVersions.simpleCEnd()) { if (*simpleReceivedIt != AvatarTraits::DEFAULT_TRAIT_VERSION) { - auto traitType = static_cast(std::distance(traitVersions.simpleCBegin(), simpleReceivedIt)); + auto traitType = + static_cast(std::distance(traitVersions.simpleCBegin(), simpleReceivedIt)); _perNodeAckedTraitVersions[nodeId][traitType] = *simpleReceivedIt; } simpleReceivedIt++; @@ -351,8 +373,8 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedDa // make sure we're not unecessarily overriding the default avatar with the default avatar if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) { // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change - qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() - << "to replacement" << slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID(); + qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() << "to replacement" + << slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID(); _avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL); auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); @@ -453,9 +475,7 @@ void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums), - [&](const ConicalViewFrustum& viewFrustum) { - return viewFrustum.intersects(otherAvatarBox); - }); + [&](const ConicalViewFrustum& viewFrustum) { return viewFrustum.intersects(otherAvatarBox); }); } void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { @@ -474,7 +494,8 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView; } -AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const { +AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint( + Node::LocalID otherAvatar) const { auto it = _lastSentTraitsTimestamps.find(otherAvatar); if (it != _lastSentTraitsTimestamps.end()) { diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h index ec24d4e6bc..09a3522067 100644 --- a/assignment-client/src/avatars/MixerAvatar.h +++ b/assignment-client/src/avatars/MixerAvatar.h @@ -43,6 +43,11 @@ public: }; Q_ENUM(VerifyState) + bool isInScreenshareZone() const { return _inScreenshareZone; } + void setInScreenshareZone(bool value = true) { _inScreenshareZone = value; } + const QUuid& getScreenshareZone() const { return _screenshareZone; } + void setScreenshareZone(QUuid zone) { _screenshareZone = zone; } + private: bool _needsHeroCheck { false }; static const char* stateToName(VerifyState state); @@ -65,6 +70,8 @@ private: int _numberChallenges { 0 }; bool _certifyFailed { false }; bool _needsIdentityUpdate { false }; + bool _inScreenshareZone { false }; + QUuid _screenshareZone; bool generateFSTHash(); bool validateFSTHash(const QString& publicKey) const; diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 54eb499be1..cbf6ff4eaf 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -23,6 +23,7 @@ #include #include #include +#include ScriptableAvatar::ScriptableAvatar() { @@ -221,7 +222,7 @@ void ScriptableAvatar::updateJointMappings() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL); networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); DependencyManager::get()->update( _skeletonModelURL, -1, "AvatarData::updateJointMappings"); QNetworkReply* networkReply = networkAccessManager.get(networkRequest); @@ -280,6 +281,17 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() { } AvatarEntityMap ScriptableAvatar::getAvatarEntityData() const { + auto data = getAvatarEntityDataInternal(true); + return data; +} + +AvatarEntityMap ScriptableAvatar::getAvatarEntityDataNonDefault() const { + auto data = getAvatarEntityDataInternal(false); + return data; + +} + +AvatarEntityMap ScriptableAvatar::getAvatarEntityDataInternal(bool allProperties) const { // DANGER: Now that we store the AvatarEntityData in packed format this call is potentially Very Expensive! // Avoid calling this method if possible. AvatarEntityMap data; @@ -288,9 +300,18 @@ AvatarEntityMap ScriptableAvatar::getAvatarEntityData() const { for (const auto& itr : _entities) { QUuid id = itr.first; EntityItemPointer entity = itr.second; - EntityItemProperties properties = entity->getProperties(); + + EncodeBitstreamParams params; + auto desiredProperties = entity->getEntityProperties(params); + desiredProperties += PROP_LOCAL_POSITION; + desiredProperties += PROP_LOCAL_ROTATION; + desiredProperties += PROP_LOCAL_VELOCITY; + desiredProperties += PROP_LOCAL_ANGULAR_VELOCITY; + desiredProperties += PROP_LOCAL_DIMENSIONS; + EntityItemProperties properties = entity->getProperties(desiredProperties); + QByteArray blob; - EntityItemProperties::propertiesToBlob(_scriptEngine, sessionID, properties, blob); + EntityItemProperties::propertiesToBlob(_scriptEngine, sessionID, properties, blob, allProperties); data[id] = blob; } }); diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index f2f5a1e6f4..1e6046ba7e 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -76,6 +76,22 @@ * size in the virtual world. Read-only. * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. * Read-only. + * @property {boolean} hasScriptedBlendshapes=false - true if blend shapes are controlled by scripted actions, + * otherwise false. Set this to true before using the {@link MyAvatar.setBlendshape} method, + * and set back to false after you no longer want scripted control over the blend shapes. + *

Note: This property will automatically be set to true if the Controller system has valid facial + * blend shape actions.

+ * @property {boolean} hasProceduralBlinkFaceMovement=true - true if avatars blink automatically by animating + * facial blend shapes, false if automatic blinking is disabled. Set to false to fully control + * the blink facial blend shapes via the {@link MyAvatar.setBlendshape} method. + * @property {boolean} hasProceduralEyeFaceMovement=true - true if the facial blend shapes for an avatar's eyes + * adjust automatically as the eyes move, false if this automatic movement is disabled. Set this property + * to true to prevent the iris from being obscured by the upper or lower lids. Set to false to + * fully control the eye blend shapes via the {@link MyAvatar.setBlendshape} method. + * @property {boolean} hasAudioEnabledFaceMovement=true - true if the avatar's mouth blend shapes animate + * automatically based on detected microphone input, false if this automatic movement is disabled. Set + * this property to false to fully control the mouth facial blend shapes via the + * {@link MyAvatar.setBlendshape} method. * * @example Create a scriptable avatar. * (function () { @@ -157,13 +173,17 @@ public: * Gets details of all avatar entities. *

Warning: Potentially an expensive call. Do not use if possible.

* @function Avatar.getAvatarEntityData - * @returns {AvatarEntityMap} Details of the avatar entities. + * @returns {AvatarEntityMap} Details of all avatar entities. * @example Report the current avatar entities. * var avatarEntityData = Avatar.getAvatarEntityData(); * print("Avatar entities: " + JSON.stringify(avatarEntityData)); */ Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override; + AvatarEntityMap getAvatarEntityDataNonDefault() const override; + + AvatarEntityMap getAvatarEntityDataInternal(bool allProperties) const; + /**jsdoc * Sets all avatar entities from an object. *

Warning: Potentially an expensive call. Do not use if possible.

diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index a6ab382781..4c4fcbf2dd 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -370,16 +370,18 @@ void EntityServer::entityFilterAdded(EntityItemID id, bool success) { void EntityServer::nodeAdded(SharedNodePointer node) { EntityTreePointer tree = std::static_pointer_cast(_tree); - tree->knowAvatarID(node->getUUID()); + if (tree) { + tree->knowAvatarID(node->getUUID()); + } OctreeServer::nodeAdded(node); } void EntityServer::nodeKilled(SharedNodePointer node) { EntityTreePointer tree = std::static_pointer_cast(_tree); - tree->withWriteLock([&] { + if (tree) { tree->deleteDescendantsOfAvatar(node->getUUID()); - }); - tree->forgetAvatarID(node->getUUID()); + tree->forgetAvatarID(node->getUUID()); + } OctreeServer::nodeKilled(node); } diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 80e0060299..63520262cd 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -89,6 +89,7 @@ int OctreeServer::_shortProcessWait = 0; int OctreeServer::_noProcessWait = 0; static const QString PERSIST_FILE_DOWNLOAD_PATH = "/models.json.gz"; +static const double NANOSECONDS_PER_SECOND = 1000000.0;; void OctreeServer::resetSendingStats() { @@ -1197,7 +1198,7 @@ void OctreeServer::domainSettingsRequestComplete() { } else { beginRunning(); } -} +} void OctreeServer::beginRunning() { auto nodeList = DependencyManager::get(); @@ -1344,6 +1345,10 @@ QString OctreeServer::getUptime() { return formattedUptime; } +double OctreeServer::getUptimeSeconds() { + return (usecTimestampNow() - _startedUSecs) / NANOSECONDS_PER_SECOND; +} + QString OctreeServer::getFileLoadTime() { QString result; if (isInitialLoadComplete()) { @@ -1386,6 +1391,10 @@ QString OctreeServer::getFileLoadTime() { return result; } +double OctreeServer::getFileLoadTimeSeconds() { + return getLoadElapsedTime() / NANOSECONDS_PER_SECOND; +} + QString OctreeServer::getConfiguration() { QString result; for (int i = 1; i < _argc; i++) { @@ -1421,6 +1430,8 @@ void OctreeServer::sendStatsPacket() { statsArray1["4. persistFileLoadTime"] = getFileLoadTime(); statsArray1["5. clients"] = getCurrentClientCount(); statsArray1["6. threads"] = threadsStats; + statsArray1["uptime_seconds"] = getUptimeSeconds(); + statsArray1["persistFileLoadTime_seconds"] = getFileLoadTimeSeconds(); // Octree Stats QJsonObject octreeStats; diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 07b1e334b1..3ae4dddee9 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -158,7 +158,9 @@ protected: void initHTTPManager(int port); void resetSendingStats(); QString getUptime(); + double getUptimeSeconds(); QString getFileLoadTime(); + double getFileLoadTimeSeconds(); QString getConfiguration(); QString getStatusLink(); diff --git a/cmake/externals/glad32es/CMakeLists.txt b/cmake/externals/glad32es/CMakeLists.txt deleted file mode 100644 index 8487e5aaaf..0000000000 --- a/cmake/externals/glad32es/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(EXTERNAL_NAME glad32es) - -include(ExternalProject) -include(SelectLibraryConfigurations) - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL "${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad32es.zip" - URL_MD5 6a641d8c49dee4c895fa59315f5682a6 - CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -if (UNIX) - set(LIB_PREFIX "lib") - set(LIB_EXT "a") -elseif (WIN32) - set(LIB_EXT "lib") -endif () - -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) -select_library_configurations(${EXTERNAL_NAME_UPPER}) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") -set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/glad41/CMakeLists.txt b/cmake/externals/glad41/CMakeLists.txt deleted file mode 100644 index 76382dd05d..0000000000 --- a/cmake/externals/glad41/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(EXTERNAL_NAME glad41) - -include(ExternalProject) -include(SelectLibraryConfigurations) - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL "${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad41.zip" - URL_MD5 1324eeec33abe91e67d19ae551ba624d - CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -if (UNIX) - set(LIB_PREFIX "lib") - set(LIB_EXT "a") -elseif (WIN32) - set(LIB_EXT "lib") -endif () - -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) -select_library_configurations(${EXTERNAL_NAME_UPPER}) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") -set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/glad45/CMakeLists.txt b/cmake/externals/glad45/CMakeLists.txt deleted file mode 100644 index 477a5f79d5..0000000000 --- a/cmake/externals/glad45/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(EXTERNAL_NAME glad45) - -include(ExternalProject) -include(SelectLibraryConfigurations) - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL "${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad45.zip" - URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05 - CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -if (UNIX) - set(LIB_PREFIX "lib") - set(LIB_EXT "a") -elseif (WIN32) - set(LIB_EXT "lib") -endif () - -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) -select_library_configurations(${EXTERNAL_NAME_UPPER}) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") -set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt deleted file mode 100644 index 63a4bee043..0000000000 --- a/cmake/externals/polyvox/CMakeLists.txt +++ /dev/null @@ -1,82 +0,0 @@ -set(EXTERNAL_NAME polyvox) - -include(ExternalProject) - -message(STATUS "===== POLYVOX BUILD_TYPE ${BUILD_TYPE} ${CMAKE_BUILD_TYPE}") -set(CMAKE_BUILD_TYPE Release) - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL "${EXTERNAL_BUILD_ASSETS}/dependencies/polyvox-master-2015-7-15.zip" - URL_MD5 9ec6323b87e849ae36e562ae1c7494a9 - CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -if (APPLE) - set(INSTALL_NAME_LIBRARY_DIR ${INSTALL_DIR}/lib) - - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - change-install-name-debug - COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" - COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR}/Debug -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake - DEPENDEES install - WORKING_DIRECTORY - LOG 1 - ) - - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - change-install-name-release - COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" - COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR}/Release -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake - DEPENDEES install - WORKING_DIRECTORY - LOG 1 - ) -endif () - - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -if (WIN32) - set(${EXTERNAL_NAME_UPPER}_CORE_INCLUDE_DIRS ${INSTALL_DIR}/PolyVoxCore/include CACHE FILEPATH - "Path to polyvox core include directory") - set(${EXTERNAL_NAME_UPPER}_UTIL_INCLUDE_DIRS ${INSTALL_DIR}/PolyVoxUtil/include CACHE FILEPATH - "Path to polyvox util include directory") -else () - set(${EXTERNAL_NAME_UPPER}_CORE_INCLUDE_DIRS ${INSTALL_DIR}/include/PolyVoxCore CACHE FILEPATH - "Path to polyvox core include directory") - set(${EXTERNAL_NAME_UPPER}_UTIL_INCLUDE_DIRS ${INSTALL_DIR}/include/PolyVoxUtil CACHE FILEPATH - "Path to polyvox util include directory") -endif () - - -if (WIN32) - set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY_DEBUG ${INSTALL_DIR}/PolyVoxCore/lib/Debug/PolyVoxCore.lib CACHE FILEPATH "polyvox core library") - - # use generator expression to ensure the correct library is found when building different configurations in VS - set(_LIB_FOLDER "$<$:PolyVoxCore/lib/RelWithDebInfo>") - set(_LIB_FOLDER "${_LIB_FOLDER}$<$:build/library/PolyVoxCore/MinSizeRel>") - set(_LIB_FOLDER "${_LIB_FOLDER}$<$,$>:PolyVoxCore/lib/Release>") - - set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY_RELEASE "${INSTALL_DIR}/${_LIB_FOLDER}/PolyVoxCore.lib" CACHE FILEPATH "polyvox core library") -# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/PolyVoxUtil/lib/PolyVoxUtil.lib CACHE FILEPATH "polyvox util library") -elseif (APPLE) - set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libPolyVoxCore.dylib CACHE FILEPATH "polyvox core library") - set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libPolyVoxCore.dylib CACHE FILEPATH "polyvox core library") -# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxUtil.dylib CACHE FILEPATH "polyvox util library") -else () - set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libPolyVoxCore.so CACHE FILEPATH "polyvox core library") - set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libPolyVoxCore.so CACHE FILEPATH "polyvox core library") -# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxUtil.so CACHE FILEPATH "polyvox util library") -endif () diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt deleted file mode 100644 index 8190a35988..0000000000 --- a/cmake/externals/quazip/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -set(EXTERNAL_NAME quazip) -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -include(ExternalProject) - -set(QUAZIP_CMAKE_ARGS - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_INSTALL_PREFIX:PATH= - -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} - -DCMAKE_INSTALL_NAME_DIR:PATH=/lib - -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} - -DZLIB_ROOT=${VCPKG_INSTALL_ROOT} - -DCMAKE_POSITION_INDEPENDENT_CODE=ON) - -if (NOT APPLE) - set(QUAZIP_CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} -DCMAKE_CXX_STANDARD=11) -endif () - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL "${EXTERNAL_BUILD_ASSETS}/dependencies/quazip-0.7.3.zip" - URL_MD5 ed03754d39b9da1775771819b8001d45 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES - FOLDER "hidden/externals" - INSTALL_NAME_DIR ${INSTALL_DIR}/lib - BUILD_WITH_INSTALL_RPATH True) - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of QuaZip include directories") -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of QuaZip include directories") -set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location of QuaZip DLL") - -if (APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") -elseif (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library") -elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library") -else () - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") -endif () - -include(SelectLibraryConfigurations) -select_library_configurations(${EXTERNAL_NAME_UPPER}) - -# Force selected libraries into the cache -set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of QuaZip libraries") -set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") diff --git a/cmake/externals/vhacd/CMakeLists.txt b/cmake/externals/vhacd/CMakeLists.txt deleted file mode 100644 index 5fb89b7e9b..0000000000 --- a/cmake/externals/vhacd/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -set(EXTERNAL_NAME vhacd) - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL "${EXTERNAL_BUILD_ASSETS}/dependencies/v-hacd-master.zip" - URL_MD5 3bfc94f8dd3dfbfe8f4dc088f4820b3e - CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -if (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/VHACD_LIB.lib CACHE FILEPATH "Path to V-HACD debug library") - - # use generator expression to ensure the correct library is found when building different configurations in VS - set(_LIB_FOLDER "$<$:build/src/VHACD_Lib/RelWithDebInfo>") - set(_LIB_FOLDER "${_LIB_FOLDER}$<$:build/src/VHACD_Lib/MinSizeRel>") - set(_LIB_FOLDER "${_LIB_FOLDER}$<$,$>:lib/Release>") - - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/VHACD_LIB.lib CACHE FILEPATH "Path to V-HACD release library") -else () - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to V-HACD debug library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libVHACD.a CACHE FILEPATH "Path to V-HACD release library") -endif () - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to V-HACD include directory") diff --git a/cmake/init.cmake b/cmake/init.cmake index e97cfa476e..ca95ad1a72 100644 --- a/cmake/init.cmake +++ b/cmake/init.cmake @@ -61,6 +61,8 @@ endif () if (APPLE) set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") endif() if (UNIX) diff --git a/cmake/installer/installer-header.bmp b/cmake/installer/installer-header.bmp index 2d262d3ef9..de8448ed44 100644 Binary files a/cmake/installer/installer-header.bmp and b/cmake/installer/installer-header.bmp differ diff --git a/cmake/installer/installer.ico b/cmake/installer/installer.ico index 63a3eff482..b337544f88 100644 Binary files a/cmake/installer/installer.ico and b/cmake/installer/installer.ico differ diff --git a/cmake/installer/uninstaller-header.bmp b/cmake/installer/uninstaller-header.bmp index 2d262d3ef9..de8448ed44 100644 Binary files a/cmake/installer/uninstaller-header.bmp and b/cmake/installer/uninstaller-header.bmp differ diff --git a/cmake/macros/DumpCmakeVariables.cmake b/cmake/macros/DumpCmakeVariables.cmake new file mode 100644 index 0000000000..f0434ec90b --- /dev/null +++ b/cmake/macros/DumpCmakeVariables.cmake @@ -0,0 +1,14 @@ +function(dump_cmake_variables) + get_cmake_property(_variableNames VARIABLES) + list (SORT _variableNames) + foreach (_variableName ${_variableNames}) + if (ARGV0) + unset(MATCHED) + string(REGEX MATCH ${ARGV0} MATCHED ${_variableName}) + if (NOT MATCHED) + continue() + endif() + endif() + message(STATUS "${_variableName}=${${_variableName}}") + endforeach() +endfunction() \ No newline at end of file diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 83bdefeada..0442df55cf 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -18,20 +18,20 @@ macro(GENERATE_INSTALLERS) if (CLIENT_ONLY) set(_PACKAGE_NAME_EXTRA "-Interface") set(INSTALLER_TYPE "client_only") - string(REGEX REPLACE "Project Athena" "Project Athena Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION}) + string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION}) elseif (SERVER_ONLY) set(_PACKAGE_NAME_EXTRA "-Sandbox") set(INSTALLER_TYPE "server_only") - string(REGEX REPLACE "Project Athena" "Project Athena Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION}) + string(REGEX REPLACE "Vircadia" "Vircadia Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION}) else () set(_DISPLAY_NAME ${BUILD_ORGANIZATION}) set(INSTALLER_TYPE "full") endif () set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME}) - set(CPACK_PACKAGE_VENDOR "Project Athena") + set(CPACK_PACKAGE_VENDOR "Vircadia") set(CPACK_PACKAGE_VERSION ${BUILD_VERSION}) - set(CPACK_PACKAGE_FILE_NAME "ProjectAthena-Alpha${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "Vircadia-Alpha${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) if (PR_BUILD) @@ -118,11 +118,11 @@ macro(GENERATE_INSTALLERS) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") if (BUILD_CLIENT) - cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "Project Athena Interface") + cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "Vircadia Interface") endif () if (BUILD_SERVER) - cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Project Athena Sandbox") + cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Sandbox") endif () include(CPack) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index d279ae8db9..1b7b3dbe8e 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -35,7 +35,7 @@ macro(SET_PACKAGING_PARAMETERS) set(DEPLOY_PACKAGE TRUE) set(PRODUCTION_BUILD 1) set(BUILD_VERSION ${RELEASE_NUMBER}) - set(BUILD_ORGANIZATION "Project Athena") + set(BUILD_ORGANIZATION "Vircadia") set(HIGH_FIDELITY_PROTOCOL "hifi") set(HIGH_FIDELITY_APP_PROTOCOL "hifiapp") set(INTERFACE_BUNDLE_NAME "interface") @@ -60,7 +60,7 @@ macro(SET_PACKAGING_PARAMETERS) set(DEPLOY_PACKAGE TRUE) set(PR_BUILD 1) set(BUILD_VERSION "PR${RELEASE_NUMBER}") - set(BUILD_ORGANIZATION "Project Athena - PR${RELEASE_NUMBER}") + set(BUILD_ORGANIZATION "Vircadia - PR${RELEASE_NUMBER}") set(INTERFACE_BUNDLE_NAME "interface") set(INTERFACE_ICON_PREFIX "interface-beta") @@ -69,7 +69,7 @@ macro(SET_PACKAGING_PARAMETERS) else () set(DEV_BUILD 1) set(BUILD_VERSION "dev") - set(BUILD_ORGANIZATION "Project Athena - ${BUILD_VERSION}") + set(BUILD_ORGANIZATION "Vircadia - ${BUILD_VERSION}") set(INTERFACE_BUNDLE_NAME "interface") set(INTERFACE_ICON_PREFIX "interface-beta") @@ -91,39 +91,11 @@ macro(SET_PACKAGING_PARAMETERS) endif () if ((PRODUCTION_BUILD OR PR_BUILD) AND NOT STABLE_BUILD) + set(GIT_COMMIT_SHORT $ENV{GIT_COMMIT_SHORT}) # append the abbreviated commit SHA to the build version # since this is a PR build or master/nightly builds - - # for PR_BUILDS, we need to grab the abbreviated SHA - # for the second parent of HEAD (not HEAD) since that is the - # SHA of the commit merged to master for the build - if (PR_BUILD) - set(_GIT_LOG_FORMAT "%p %h") - else () - set(_GIT_LOG_FORMAT "%h") - endif () - - execute_process( - COMMAND git log -1 --abbrev=7 --format=${_GIT_LOG_FORMAT} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE _GIT_LOG_OUTPUT - ERROR_VARIABLE _GIT_LOG_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - if (PR_BUILD) - separate_arguments(_COMMIT_PARENTS UNIX_COMMAND ${_GIT_LOG_OUTPUT}) - list(GET _COMMIT_PARENTS 1 GIT_COMMIT_HASH) - else () - set(GIT_COMMIT_HASH ${_GIT_LOG_OUTPUT}) - endif () - - if (_GIT_LOG_ERROR OR NOT GIT_COMMIT_HASH) - message(FATAL_ERROR "Could not retreive abbreviated SHA for PR or production master build") - endif () - set(BUILD_VERSION_NO_SHA ${BUILD_VERSION}) - set(BUILD_VERSION "${BUILD_VERSION}-${GIT_COMMIT_HASH}") + set(BUILD_VERSION "${BUILD_VERSION}-${GIT_COMMIT_SHORT}") # pass along a release number without the SHA in case somebody # wants to compare master or PR builds as integers @@ -146,23 +118,27 @@ macro(SET_PACKAGING_PARAMETERS) set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc") - set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) - set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) - set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(SCREENSHARE_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) + set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME}) if (CLIENT_ONLY) set(CONSOLE_EXEC_NAME "Console.app") else () set(CONSOLE_EXEC_NAME "Sandbox.app") endif() - set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}") + set(SCREENSHARE_EXEC_NAME "hifi-screenshare.app") + set(SCREENSHARE_INSTALL_APP_PATH "${SCREENSHARE_INSTALL_DIR}/${SCREENSHARE_EXEC_NAME}") + set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents") set(COMPONENT_APP_PATH "${CONSOLE_APP_CONTENTS}/MacOS/Components.app") set(COMPONENT_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/MacOS") set(CONSOLE_PLUGIN_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/PlugIns") - + + set(SCREENSHARE_APP_CONTENTS "${SCREENSHARE_INSTALL_APP_PATH}/Contents") set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app") set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns") @@ -170,9 +146,11 @@ macro(SET_PACKAGING_PARAMETERS) else () if (WIN32) set(CONSOLE_INSTALL_DIR "server-console") + set(SCREENSHARE_INSTALL_DIR "hifi-screenshare") set(NITPICK_INSTALL_DIR "nitpick") else () set(CONSOLE_INSTALL_DIR ".") + set(SCREENSHARE_INSTALL_DIR ".") set(NITPICK_INSTALL_DIR ".") endif () @@ -186,27 +164,28 @@ macro(SET_PACKAGING_PARAMETERS) set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico") set(CONSOLE_EXEC_NAME "server-console.exe") + set(SCREENSHARE_EXEC_NAME "hifi-screenshare.exe") set(DS_EXEC_NAME "domain-server.exe") set(AC_EXEC_NAME "assignment-client.exe") # shortcut names if (PRODUCTION_BUILD) - set(INTERFACE_SHORTCUT_NAME "Project Athena") + set(INTERFACE_SHORTCUT_NAME "Vircadia") set(CONSOLE_SHORTCUT_NAME "Console") set(SANDBOX_SHORTCUT_NAME "Sandbox") set(APP_USER_MODEL_ID "com.highfidelity.console") else () - set(INTERFACE_SHORTCUT_NAME "Project Athena - ${BUILD_VERSION_NO_SHA}") + 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}") endif () set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}") - set(CONSOLE_HF_SHORTCUT_NAME "Project Athena ${CONSOLE_SHORTCUT_NAME}") - set(SANDBOX_HF_SHORTCUT_NAME "Project Athena ${SANDBOX_SHORTCUT_NAME}") + set(CONSOLE_HF_SHORTCUT_NAME "Vircadia ${CONSOLE_SHORTCUT_NAME}") + set(SANDBOX_HF_SHORTCUT_NAME "Vircadia ${SANDBOX_SHORTCUT_NAME}") - set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "Project Athena") + set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "Vircadia") set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "Server Console") # check if we need to find signtool diff --git a/cmake/macros/SetupQt.cmake b/cmake/macros/SetupQt.cmake index c09c2b0f6b..bd198a2195 100644 --- a/cmake/macros/SetupQt.cmake +++ b/cmake/macros/SetupQt.cmake @@ -48,19 +48,13 @@ macro(setup_qt) # if we are in a development build and QT_CMAKE_PREFIX_PATH is specified # then use it, # otherwise, use the vcpkg'ed version - if(NOT DEFINED VCPKG_QT_CMAKE_PREFIX_PATH) - message(FATAL_ERROR "VCPKG_QT_CMAKE_PREFIX_PATH should have been set by hifi_vcpkg.py") + if(NOT DEFINED QT_CMAKE_PREFIX_PATH) + message(FATAL_ERROR "QT_CMAKE_PREFIX_PATH should have been set by hifi_qt.py") endif() - if (NOT DEV_BUILD) - message("override QT_CMAKE_PREFIX_PATH with VCPKG_QT_CMAKE_PREFIX_PATH") - set(QT_CMAKE_PREFIX_PATH ${VCPKG_QT_CMAKE_PREFIX_PATH}) - else() - # DEV_BUILD - if (DEFINED ENV{QT_CMAKE_PREFIX_PATH}) - set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) - else() - set(QT_CMAKE_PREFIX_PATH ${VCPKG_QT_CMAKE_PREFIX_PATH}) - endif() + if (DEV_BUILD) + if (DEFINED ENV{QT_CMAKE_PREFIX_PATH}) + set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH}) + endif() endif() message("QT_CMAKE_PREFIX_PATH = " ${QT_CMAKE_PREFIX_PATH}) diff --git a/cmake/macros/TargetAristo.cmake b/cmake/macros/TargetAristo.cmake new file mode 100644 index 0000000000..a28e7b5cdd --- /dev/null +++ b/cmake/macros/TargetAristo.cmake @@ -0,0 +1,15 @@ +# +# Copyright 2019 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_ARISTO) + +if (WIN32) + target_include_directories(${TARGET_NAME} SYSTEM PUBLIC "${VCPKG_INSTALL_ROOT}/include") + find_library(ARISTO_LIBRARY NAMES aristo_interface PATHS ${VCPKG_INSTALL_ROOT}/lib/ NO_DEFAULT_PATH) + target_link_libraries(${TARGET_NAME} ${ARISTO_LIBRARY}) +endif() + +endmacro() diff --git a/cmake/macros/TargetDraco.cmake b/cmake/macros/TargetDraco.cmake index 520786d4c3..e23069d1d3 100755 --- a/cmake/macros/TargetDraco.cmake +++ b/cmake/macros/TargetDraco.cmake @@ -1,6 +1,5 @@ macro(TARGET_DRACO) set(LIBS draco dracodec dracoenc) - find_library(LIBPATH ${LIB} PATHS ) if (ANDROID) set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/draco) set(DRACO_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE STRING INTERNAL) @@ -12,6 +11,8 @@ macro(TARGET_DRACO) else() set(LIB_SEARCH_PATH_RELEASE ${VCPKG_INSTALL_ROOT}/lib/) set(LIB_SEARCH_PATH_DEBUG ${VCPKG_INSTALL_ROOT}/debug/lib/) + set(DRACO_LIBRARY_RELEASE "") + set(DRACO_LIBRARY_DEBUG "") foreach(LIB ${LIBS}) find_library(${LIB}_LIBPATH ${LIB} PATHS ${LIB_SEARCH_PATH_RELEASE} NO_DEFAULT_PATH) list(APPEND DRACO_LIBRARY_RELEASE ${${LIB}_LIBPATH}) diff --git a/cmake/macros/TargetGlad.cmake b/cmake/macros/TargetGlad.cmake index c9a2529986..aa7258e345 100644 --- a/cmake/macros/TargetGlad.cmake +++ b/cmake/macros/TargetGlad.cmake @@ -16,21 +16,15 @@ macro(TARGET_GLAD) find_library(EGL EGL) target_link_libraries(${TARGET_NAME} ${EGL}) else() - if (USE_GLES) - set(GLAD_VER "32es") - else() - set(GLAD_VER "45") - endif() find_package(OpenGL REQUIRED) list(APPEND GLAD_EXTRA_LIBRARIES ${OPENGL_LIBRARY}) + find_library(GLAD_LIBRARY_RELEASE glad PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + find_library(GLAD_LIBRARY_DEBUG glad_d PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + select_library_configurations(GLAD) if (NOT WIN32) list(APPEND GLAD_EXTRA_LIBRARIES dl) endif() - set(GLAD "glad${GLAD_VER}") - string(TOUPPER ${GLAD} GLAD_UPPER) - add_dependency_external_projects(${GLAD}) set(GLAD_INCLUDE_DIRS ${${GLAD_UPPER}_INCLUDE_DIRS}) - set(GLAD_LIBRARY ${${GLAD_UPPER}_LIBRARY}) endif() target_include_directories(${TARGET_NAME} PUBLIC ${GLAD_INCLUDE_DIRS}) diff --git a/cmake/macros/TargetLiblo.cmake b/cmake/macros/TargetLiblo.cmake new file mode 100644 index 0000000000..dac4197b1e --- /dev/null +++ b/cmake/macros/TargetLiblo.cmake @@ -0,0 +1,4 @@ +macro(target_liblo) + find_library(LIBLO LIBLO) + target_link_libraries(${TARGET_NAME} ${LIBLO}) +endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetOpenEXR.cmake b/cmake/macros/TargetOpenEXR.cmake index 8d61f216e7..9d63ba3ef4 100644 --- a/cmake/macros/TargetOpenEXR.cmake +++ b/cmake/macros/TargetOpenEXR.cmake @@ -28,6 +28,8 @@ macro(TARGET_OPENEXR) string(REGEX MATCHALL "[0-9]" OPENEXR_MINOR_VERSION ${TMP}) endif() + set(OPENEXR_LIBRARY_RELEASE "") + set(OPENEXR_LIBRARY_DEBUG "") foreach(OPENEXR_LIB IlmImf IlmImfUtil diff --git a/cmake/macros/TargetPolyvox.cmake b/cmake/macros/TargetPolyvox.cmake index b2c4e30dd2..576b454f57 100644 --- a/cmake/macros/TargetPolyvox.cmake +++ b/cmake/macros/TargetPolyvox.cmake @@ -13,12 +13,16 @@ macro(TARGET_POLYVOX) list(APPEND POLYVOX_LIBRARIES ${LIB_DIR}/libPolyVoxUtil.so) list(APPEND POLYVOX_LIBRARIES ${LIB_DIR}/Release/libPolyVoxCore.so) else() - add_dependency_external_projects(polyvox) - find_package(PolyVox REQUIRED) + find_library(POLYVOX_LIBRARY_RELEASE PolyVoxCore PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + find_library(POLYVOX_UTIL_LIBRARY_RELEASE PolyVoxUtil PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + list(APPEND POLYVOX_LIBRARY_RELEASE ${POLYVOX_UTIL_LIBRARY_RELEASE}) + find_library(POLYVOX_LIBRARY_DEBUG PolyVoxCore PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + find_library(POLYVOX_UTIL_LIBRARY_DEBUG PolyVoxUtil PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + list(APPEND POLYVOX_LIBRARY_DEBUG ${POLYVOX_UTIL_LIBRARY_DEBUG}) + select_library_configurations(POLYVOX) + list(APPEND POLYVOX_INCLUDE_DIRS ${VCPKG_INSTALL_ROOT}/include) endif() - target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) + target_include_directories(${TARGET_NAME} PUBLIC ${POLYVOX_INCLUDE_DIRS}) endmacro() - - diff --git a/cmake/macros/TargetPython.cmake b/cmake/macros/TargetPython.cmake index 2c055cf8bc..c9089c17bd 100644 --- a/cmake/macros/TargetPython.cmake +++ b/cmake/macros/TargetPython.cmake @@ -19,4 +19,5 @@ macro(TARGET_PYTHON) message(FATAL_ERROR "Unable to locate Python interpreter 3.5 or higher") endif() endif() -endmacro() \ No newline at end of file + message("Using the Python interpreter located at: " ${HIFI_PYTHON_EXEC}) +endmacro() diff --git a/cmake/macros/TargetQuazip.cmake b/cmake/macros/TargetQuazip.cmake index f704f03050..fa05ec55eb 100644 --- a/cmake/macros/TargetQuazip.cmake +++ b/cmake/macros/TargetQuazip.cmake @@ -6,11 +6,8 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_QUAZIP) - add_dependency_external_projects(quazip) - find_package(QuaZip REQUIRED) - target_include_directories(${TARGET_NAME} PUBLIC ${QUAZIP_INCLUDE_DIRS}) + find_library(QUAZIP_LIBRARY_RELEASE quazip5 PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + find_library(QUAZIP_LIBRARY_DEBUG quazip5 PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + select_library_configurations(QUAZIP) target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) - if (WIN32) - add_paths_to_fixup_libs(${QUAZIP_DLL_PATH}) - endif () -endmacro() \ No newline at end of file +endmacro() diff --git a/cmake/macros/TargetSPIRV.cmake b/cmake/macros/TargetSPIRV.cmake deleted file mode 100644 index 94c9df9d13..0000000000 --- a/cmake/macros/TargetSPIRV.cmake +++ /dev/null @@ -1,15 +0,0 @@ -macro(TARGET_SPIRV) - add_dependency_external_projects(spirv_cross) - target_link_libraries(${TARGET_NAME} ${SPIRV_CROSS_LIBRARIES}) - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SPIRV_CROSS_INCLUDE_DIRS}) - - # spirv-tools requires spirv-headers - add_dependency_external_projects(spirv_headers) - add_dependency_external_projects(spirv_tools) - target_link_libraries(${TARGET_NAME} ${SPIRV_TOOLS_LIBRARIES}) - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SPIRV_TOOLS_INCLUDE_DIRS}) - - add_dependency_external_projects(glslang) - target_link_libraries(${TARGET_NAME} ${GLSLANG_LIBRARIES}) - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${GLSLANG_INCLUDE_DIRS}) -endmacro() diff --git a/cmake/macros/TargetSRanipalEye.cmake b/cmake/macros/TargetSRanipalEye.cmake new file mode 100644 index 0000000000..5644d30e92 --- /dev/null +++ b/cmake/macros/TargetSRanipalEye.cmake @@ -0,0 +1,18 @@ +# +# Copyright 2019 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_SRANIPAL) + +if (WIN32) + target_include_directories(${TARGET_NAME} SYSTEM PUBLIC "${VCPKG_INSTALL_ROOT}/include") + find_library(SRANIPAL_LIBRARY NAMES SRanipal PATHS ${VCPKG_INSTALL_ROOT}/lib/ NO_DEFAULT_PATH) + target_link_libraries(${TARGET_NAME} ${SRANIPAL_LIBRARY}) + + find_library(SRANIPAL_LIBRARY NAMES SRanipal PATHS ${VCPKG_INSTALL_ROOT}/lib/ NO_DEFAULT_PATH) + target_link_libraries(${TARGET_NAME} ${SRANIPAL_LIBRARY}) +endif() + +endmacro() diff --git a/cmake/macros/TargetSpirvBinaries.cmake b/cmake/macros/TargetSpirvBinaries.cmake deleted file mode 100644 index 6cc102d38e..0000000000 --- a/cmake/macros/TargetSpirvBinaries.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# -# Created by Bradley Austin Davis on 2016/02/16 -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# -macro(TARGET_SPIRV_BINARIES) - add_dependency_external_projects(spirv_binaries) -endmacro() - diff --git a/cmake/macros/TargetTBB.cmake b/cmake/macros/TargetTBB.cmake index b2aeeb99aa..087f3f3242 100644 --- a/cmake/macros/TargetTBB.cmake +++ b/cmake/macros/TargetTBB.cmake @@ -15,11 +15,6 @@ if (ANDROID) set(TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY}) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) -elseif(APPLE) - add_dependency_external_projects(tbb) - find_package(TBB REQUIRED) - target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) - target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) else() # using VCPKG for TBB find_package(TBB CONFIG REQUIRED) diff --git a/cmake/macros/TargetVHACD.cmake b/cmake/macros/TargetVHACD.cmake new file mode 100644 index 0000000000..35e5f8b9c4 --- /dev/null +++ b/cmake/macros/TargetVHACD.cmake @@ -0,0 +1,6 @@ +macro(TARGET_VHACD) + find_library(VHACD_LIBRARY_RELEASE VHACD PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + find_library(VHACD_LIBRARY_DEBUG VHACD PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + select_library_configurations(VHACD) + target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES}) +endmacro() diff --git a/cmake/ports/.gitattributes b/cmake/ports/.gitattributes new file mode 100644 index 0000000000..44b4224b17 --- /dev/null +++ b/cmake/ports/.gitattributes @@ -0,0 +1 @@ +* eol=lf \ No newline at end of file diff --git a/cmake/ports/aristo/CONTROL b/cmake/ports/aristo/CONTROL new file mode 100644 index 0000000000..a022c6ea9f --- /dev/null +++ b/cmake/ports/aristo/CONTROL @@ -0,0 +1,3 @@ +Source: aristo +Version: 0.8.1 +Description: Aristo diff --git a/cmake/ports/aristo/portfile.cmake b/cmake/ports/aristo/portfile.cmake new file mode 100644 index 0000000000..94efe6f7ea --- /dev/null +++ b/cmake/ports/aristo/portfile.cmake @@ -0,0 +1,22 @@ +include(vcpkg_common_functions) +set(ARISTO_VERSION 0.8.1) +set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src) + +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + +if (WIN32) + vcpkg_download_distfile( + ARISTO_SOURCE_ARCHIVE + URLS "${EXTERNAL_BUILD_ASSETS}/seth/aristo-0.8.1-windows.zip" + SHA512 05179c63b72a1c9f5be8a7a2b7389025da683400dbf819e5a6199dd6473c56774d2885182dc5a11cb6324058d228a4ead832222e8b3e1bebaa4c61982e85f0a8 + FILENAME aristo-0.8.1-windows.zip + ) + + vcpkg_extract_source_archive(${ARISTO_SOURCE_ARCHIVE}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/aristo/include DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/aristo/lib DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/aristo/debug DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/aristo/bin DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/aristo/share DESTINATION ${CURRENT_PACKAGES_DIR}) + +endif () diff --git a/cmake/ports/glad/CONTROL b/cmake/ports/glad/CONTROL new file mode 100644 index 0000000000..e243d3c6f8 --- /dev/null +++ b/cmake/ports/glad/CONTROL @@ -0,0 +1,3 @@ +Source: glad +Version: 20191029 +Description: OpenGL function loader diff --git a/cmake/ports/glad/copyright b/cmake/ports/glad/copyright new file mode 100644 index 0000000000..3b9a7d61a6 --- /dev/null +++ b/cmake/ports/glad/copyright @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2018 David Herberth + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/cmake/ports/glad/portfile.cmake b/cmake/ports/glad/portfile.cmake new file mode 100644 index 0000000000..54b1d91c89 --- /dev/null +++ b/cmake/ports/glad/portfile.cmake @@ -0,0 +1,37 @@ +include(vcpkg_common_functions) +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + +if (ANDROID) + vcpkg_download_distfile( + SOURCE_ARCHIVE + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad32es.zip + SHA512 2e02ac633eed8f2ba2adbf96ea85d08998f48dd2e9ec9a88ec3c25f48eaf1405371d258066327c783772fcb3793bdb82bd7375fdabb2ba5e2ce0835468b17f65 + ) +else() + # else Linux desktop + vcpkg_download_distfile( + SOURCE_ARCHIVE + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad45.zip + SHA512 653a7b873f9fbc52e0ab95006cc3143bc7b6f62c6e032bc994e87669273468f37978525c9af5efe36f924cb4acd221eb664ad9af0ce4bf711b4f1be724c0065e + FILENAME glad45.zip + ) +endif() + +vcpkg_extract_source_archive_ex( + OUT_SOURCE_PATH SOURCE_PATH + ARCHIVE ${SOURCE_ARCHIVE} + NO_REMOVE_ONE_LEVEL +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE=ON +) + +vcpkg_install_cmake() + +file(COPY ${CMAKE_CURRENT_LIST_DIR}/copyright DESTINATION ${CURRENT_PACKAGES_DIR}/share/glad) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) diff --git a/cmake/ports/glm/disable_warnings_as_error.patch b/cmake/ports/glm/disable_warnings_as_error.patch index f87616b1ec..5dabaa6323 100644 --- a/cmake/ports/glm/disable_warnings_as_error.patch +++ b/cmake/ports/glm/disable_warnings_as_error.patch @@ -1,7 +1,16 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 756673a3..5fbc8906 100644 +index 756673a..9b3aa07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt +@@ -194,7 +194,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message("GLM: Clang - ${CMAKE_CXX_COMPILER_ID} compiler") + endif() + +- add_compile_options(-Werror -Weverything) ++ add_compile_options(-Weverything) + add_compile_options(-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-c++11-long-long -Wno-padded -Wno-gnu-anonymous-struct -Wno-nested-anon-types) + add_compile_options(-Wno-undefined-reinterpret-cast -Wno-sign-conversion -Wno-unused-variable -Wno-missing-prototypes -Wno-unreachable-code -Wno-missing-variable-declarations -Wno-sign-compare -Wno-global-constructors -Wno-unused-macros -Wno-format-nonliteral) + @@ -216,7 +216,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") message("GLM: Visual C++ - ${CMAKE_CXX_COMPILER_ID} compiler") endif() diff --git a/cmake/ports/hifi-client-deps/CONTROL b/cmake/ports/hifi-client-deps/CONTROL index 7d4727b364..4a8a2bc44e 100644 --- a/cmake/ports/hifi-client-deps/CONTROL +++ b/cmake/ports/hifi-client-deps/CONTROL @@ -1,4 +1,4 @@ Source: hifi-client-deps -Version: 0 +Version: 0.1 Description: Collected dependencies for High Fidelity applications -Build-Depends: hifi-deps, glslang, nlohmann-json, openvr (windows), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), vulkanmemoryallocator +Build-Depends: hifi-deps, aristo (windows), glslang, liblo (windows), nlohmann-json, openvr (linux|windows), quazip (!android), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), sranipal (windows), vulkanmemoryallocator diff --git a/cmake/ports/hifi-deps/CONTROL b/cmake/ports/hifi-deps/CONTROL index b1a7f96a00..ff689c7a2a 100644 --- a/cmake/ports/hifi-deps/CONTROL +++ b/cmake/ports/hifi-deps/CONTROL @@ -1,4 +1,4 @@ Source: hifi-deps -Version: 0.4 +Version: 0.1.5-github-actions Description: Collected dependencies for High Fidelity applications -Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), opus, tbb (!android&!osx), zlib, webrtc (!android) +Build-Depends: bullet3, draco, etc2comp, glad, glm, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android), zlib diff --git a/cmake/ports/liblo/CONTROL b/cmake/ports/liblo/CONTROL new file mode 100644 index 0000000000..5e05c95e3e --- /dev/null +++ b/cmake/ports/liblo/CONTROL @@ -0,0 +1,3 @@ +Source: liblo +Version: 0.30 +Description: liblo is an implementation of the Open Sound Control protocol for POSIX systems \ No newline at end of file diff --git a/cmake/ports/liblo/portfile.cmake b/cmake/ports/liblo/portfile.cmake new file mode 100644 index 0000000000..27e41af186 --- /dev/null +++ b/cmake/ports/liblo/portfile.cmake @@ -0,0 +1,36 @@ +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO radarsat1/liblo + REF 0.30 + SHA512 d36c141c513f869e6d1963bd0d584030038019b8be0b27bb9a684722b6e7a38e942ad2ee7c2e67ac13b965560937aad97259435ed86034aa2dc8cb92d23845d8 + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH}/cmake + PREFER_NINJA # Disable this option if project cannot be built with Ninja + OPTIONS -DTHREADING=1 +) + +vcpkg_install_cmake() + +# Install needed files into package directory +vcpkg_fixup_cmake_targets(CONFIG_PATH lib/cmake/liblo) + +file(INSTALL ${CURRENT_PACKAGES_DIR}/bin/oscsend.exe DESTINATION ${CURRENT_PACKAGES_DIR}/tools/liblo) +file(INSTALL ${CURRENT_PACKAGES_DIR}/bin/oscdump.exe DESTINATION ${CURRENT_PACKAGES_DIR}/tools/liblo) +vcpkg_copy_tool_dependencies(${CURRENT_PACKAGES_DIR}/tools/liblo) + +# Remove unnecessary files +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/oscsend.exe ${CURRENT_PACKAGES_DIR}/bin/oscdump.exe) +file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/oscsend.exe ${CURRENT_PACKAGES_DIR}/debug/bin/oscdump.exe) + +if(VCPKG_LIBRARY_LINKAGE STREQUAL static) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin) +endif() + +# Handle copyright +file(INSTALL ${SOURCE_PATH}/COPYING DESTINATION ${CURRENT_PACKAGES_DIR}/share/liblo RENAME copyright) diff --git a/cmake/ports/openssl-android/portfile.cmake b/cmake/ports/openssl-android/portfile.cmake index 372a9fceef..fd74637d00 100644 --- a/cmake/ports/openssl-android/portfile.cmake +++ b/cmake/ports/openssl-android/portfile.cmake @@ -7,7 +7,7 @@ file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSE message("MASTER_COPY_SOURCE_PATH ${MASTER_COPY_SOURCE_PATH}") vcpkg_download_distfile( OPENSSL_SOURCE_ARCHIVE - URLS "${EXTERNAL_BUILD_ASSETS}/dependencies/android/openssl-1.1.0g_armv8.tgz?versionId=AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW" + URLS "${EXTERNAL_BUILD_ASSETS}/dependencies/android/openssl-1.1.0g_armv8.tgz%3FversionId=AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW" SHA512 5d7bb6e5d3db2340449e2789bcd72da821f0e57483bac46cf06f735dffb5d73c1ca7cc53dd48f3b3979d0fe22b3ae61997c516fc0c4611af4b4b7f480e42b992 FILENAME openssl-1.1.0g_armv8.tgz ) diff --git a/cmake/ports/openssl-unix/CMakeLists.txt b/cmake/ports/openssl-unix/CMakeLists.txt index 7757a4fe78..3badf07eef 100644 --- a/cmake/ports/openssl-unix/CMakeLists.txt +++ b/cmake/ports/openssl-unix/CMakeLists.txt @@ -26,6 +26,7 @@ get_filename_component(COMPILER_ROOT "${CMAKE_C_COMPILER}" DIRECTORY) message("CMAKE_C_COMPILER=${CMAKE_C_COMPILER}") message("COMPILER_ROOT=${COMPILER_ROOT}") message("CMAKE_SYSROOT=${CMAKE_SYSROOT}") +message("CMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}") message("CMAKE_C_FLAGS=${CMAKE_C_FLAGS}") message("CMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}") message("CMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}") @@ -43,6 +44,8 @@ if(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN) endif() if(CMAKE_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT) set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") +elseif(CMAKE_OSX_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT) + set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_OSX_SYSROOT}") endif() string(REGEX REPLACE "^ " "" CFLAGS "${CFLAGS}") @@ -73,9 +76,27 @@ file(WRITE "${BUILDDIR}/Configure" "${_contents}") if(BUILD_SHARED_LIBS) set(SHARED shared) + file(STRINGS "${BUILDDIR}/crypto/opensslv.h" SHLIB_VERSION + REGEX "^#[\t ]*define[\t ]+SHLIB_VERSION_NUMBER[\t ]+\".*\".*") + string(REGEX REPLACE "^.*SHLIB_VERSION_NUMBER[\t ]+\"([^\"]*)\".*$" "\\1" + SHLIB_VERSION "${SHLIB_VERSION}") + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(LIB_EXT dylib) + set(LIB_EXTS ${SHLIB_VERSION}.${LIB_EXT}) + else() + set(LIB_EXT so) + set(LIB_EXTS ${LIB_EXT}.${SHLIB_VERSION}) + endif() + list(APPEND LIB_EXTS ${LIB_EXT}) else() set(SHARED no-shared) + set(LIB_EXTS a) endif() +foreach(lib ssl crypto) + foreach(ext ${LIB_EXTS}) + list(APPEND INSTALL_LIBS "${BUILDDIR}/lib${lib}.${ext}") + endforeach() +endforeach() if(CMAKE_HOST_WIN32) set(ENV_COMMAND set) @@ -127,10 +148,10 @@ add_custom_target(build_libs ALL VERBATIM WORKING_DIRECTORY "${BUILDDIR}" DEPENDS depend - BYPRODUCTS "${BUILDDIR}/libssl.a" "${BUILDDIR}/libcrypto.a" + BYPRODUCTS ${INSTALL_LIBS} ) install( - FILES "${BUILDDIR}/libssl.a" "${BUILDDIR}/libcrypto.a" + FILES ${INSTALL_LIBS} DESTINATION lib ) diff --git a/cmake/ports/openssl-unix/CONTROL b/cmake/ports/openssl-unix/CONTROL index 6413eb3712..873046ca85 100644 --- a/cmake/ports/openssl-unix/CONTROL +++ b/cmake/ports/openssl-unix/CONTROL @@ -1,3 +1,3 @@ Source: openssl-unix -Version: 1.0.2p +Version: 1.0.2s-1 Description: OpenSSL is an open source project that provides a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library. diff --git a/cmake/ports/openssl-unix/portfile.cmake b/cmake/ports/openssl-unix/portfile.cmake index 1484fc66c6..1047bdeecb 100644 --- a/cmake/ports/openssl-unix/portfile.cmake +++ b/cmake/ports/openssl-unix/portfile.cmake @@ -1,25 +1,37 @@ +include(vcpkg_common_functions) + if(VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" OR NOT VCPKG_CMAKE_SYSTEM_NAME) message(FATAL_ERROR "This port is only for openssl on Unix-like systems") endif() -include(vcpkg_common_functions) -set(OPENSSL_VERSION 1.0.2p) -set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/openssl-${OPENSSL_VERSION}) +if(EXISTS "${CURRENT_INSTALLED_DIR}/include/openssl/ssl.h") + message(WARNING "Can't build openssl if libressl is installed. Please remove libressl, and try install openssl again if you need it. Build will continue but there might be problems since libressl is only a subset of openssl") + set(VCPKG_POLICY_EMPTY_PACKAGE enabled) + return() +endif() + +if (VCPKG_LIBRARY_LINKAGE STREQUAL static) + set(VCPKG_LIBRARY_LINKAGE dynamic) +endif() vcpkg_find_acquire_program(PERL) +set(OPENSSL_VERSION 1.0.2s) + vcpkg_download_distfile(OPENSSL_SOURCE_ARCHIVE URLS "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" "https://www.openssl.org/source/old/1.0.2/openssl-${OPENSSL_VERSION}.tar.gz" FILENAME "openssl-${OPENSSL_VERSION}.tar.gz" - SHA512 958c5a7c3324bbdc8f07dfb13e11329d9a1b4452c07cf41fbd2d42b5fe29c95679332a3476d24c2dc2b88be16e4a24744aba675a05a388c0905756c77a8a2f16 + SHA512 9f745452c4f777df694158e95003cde78a2cf8199bc481a563ec36644664c3c1415a774779b9791dd18f2aeb57fa1721cb52b3db12d025955e970071d5b66d2a ) -vcpkg_extract_source_archive(${OPENSSL_SOURCE_ARCHIVE}) -vcpkg_apply_patches( - SOURCE_PATH ${MASTER_COPY_SOURCE_PATH} - PATCHES ${CMAKE_CURRENT_LIST_DIR}/ConfigureIncludeQuotesFix.patch - ${CMAKE_CURRENT_LIST_DIR}/STRINGIFYPatch.patch - ${CMAKE_CURRENT_LIST_DIR}/EmbedSymbolsInStaticLibsZ7.patch +vcpkg_extract_source_archive_ex( + OUT_SOURCE_PATH MASTER_COPY_SOURCE_PATH + ARCHIVE ${OPENSSL_SOURCE_ARCHIVE} + REF ${OPENSSL_VERSION} + PATCHES + ConfigureIncludeQuotesFix.patch + STRINGIFYPatch.patch + EmbedSymbolsInStaticLibsZ7.patch ) if(CMAKE_HOST_WIN32) diff --git a/cmake/ports/openvr/portfile.cmake b/cmake/ports/openvr/portfile.cmake index 5e4211907d..bcbac8613a 100644 --- a/cmake/ports/openvr/portfile.cmake +++ b/cmake/ports/openvr/portfile.cmake @@ -3,23 +3,31 @@ include(vcpkg_common_functions) vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO ValveSoftware/openvr - REF v1.0.16 - SHA512 967356563ba4232da5361510c7519d3058e09eced4571aadc00d8a75ab1f299a0aebda2b0b10b0ffb6c6a443fd718634d0c0103964e289961449c93e8d7b9d02 + REF v1.11.11 + SHA512 25bddb0e82eea091fe5101d0d3de1de7bb81b4504adc0c8d8e687d2502c0167bc5a11e68bc343d7563fb4db7c917e9d0e2ea99bc1d8016d479874b0c6bd7f121 HEAD_REF master ) set(VCPKG_LIBRARY_LINKAGE dynamic) if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - set(ARCH_PATH "win64") + if(WIN32) + set(ARCH_PATH "win64") + else() + set(ARCH_PATH "linux64") + endif() elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") - set(ARCH_PATH "win32") + if(WIN32) + set(ARCH_PATH "win32") + else() + set(ARCH_PATH "linux32") + endif() else() - message(FATAL_ERROR "Package only supports x64 and x86 windows.") + message(FATAL_ERROR "Package only supports x64 and x86 Windows and Linux.") endif() -if(VCPKG_CMAKE_SYSTEM_NAME) - message(FATAL_ERROR "Package only supports windows desktop.") +if(VCPKG_CMAKE_SYSTEM_NAME AND NOT (VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Linux")) + message(FATAL_ERROR "Package only supports Windows or Linux desktop.") endif() file(MAKE_DIRECTORY @@ -28,18 +36,35 @@ file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/lib ${CURRENT_PACKAGES_DIR}/debug/bin ) -file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/lib) -file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib) -file(COPY - ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll - ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb - DESTINATION ${CURRENT_PACKAGES_DIR}/bin -) -file(COPY - ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll - ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb - DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin -) + +if(WIN32) + file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/lib) + file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib) + file(COPY + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb + DESTINATION ${CURRENT_PACKAGES_DIR}/bin + ) + file(COPY + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb + DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin + ) +else() + file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/libopenvr_api.so DESTINATION ${CURRENT_PACKAGES_DIR}/lib) + file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/libopenvr_api.so DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib) + file(COPY + ${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so + ${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so.dbg + DESTINATION ${CURRENT_PACKAGES_DIR}/bin + ) + file(COPY + ${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so + ${SOURCE_PATH}/bin/${ARCH_PATH}/libopenvr_api.so.dbg + DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin + ) +endif() + file(COPY ${SOURCE_PATH}/headers DESTINATION ${CURRENT_PACKAGES_DIR}) file(RENAME ${CURRENT_PACKAGES_DIR}/headers ${CURRENT_PACKAGES_DIR}/include) diff --git a/cmake/ports/opus/portfile.cmake b/cmake/ports/opus/portfile.cmake index bf23718df8..e5518351f8 100644 --- a/cmake/ports/opus/portfile.cmake +++ b/cmake/ports/opus/portfile.cmake @@ -6,9 +6,9 @@ vcpkg_from_github( REPO xiph/opus REF - e85ed7726db5d677c9c0677298ea0cb9c65bdd23 + 72a3a6c13329869000b34a12ba27d8bfdfbc22b3 SHA512 - a8c7e5bf383c06f1fdffd44d9b5f658f31eb4800cb59d12da95ddaeb5646f7a7b03025f4663362b888b1374d4cc69154f006ba07b5840ec61ddc1a1af01d6c54 + 590b852e966a497e33d129b58bc07d1205fe8fea9b158334cd8a3c7f539332ef9702bba4a37bd0be83eb5f04a218cef87645251899f099695d01c1eb8ea6e2fd HEAD_REF master) diff --git a/cmake/ports/polyvox/CONTROL b/cmake/ports/polyvox/CONTROL new file mode 100644 index 0000000000..89a01813c7 --- /dev/null +++ b/cmake/ports/polyvox/CONTROL @@ -0,0 +1,3 @@ +Source: polyvox +Version: 20150715 +Description: Polyvox diff --git a/cmake/ports/polyvox/portfile.cmake b/cmake/ports/polyvox/portfile.cmake new file mode 100644 index 0000000000..9204b26dbd --- /dev/null +++ b/cmake/ports/polyvox/portfile.cmake @@ -0,0 +1,104 @@ +include(vcpkg_common_functions) + +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + +# else Linux desktop +vcpkg_download_distfile( + SOURCE_ARCHIVE + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/polyvox-master-2015-7-15.zip + SHA512 cc04cd43ae74b9c7bb065953540c0048053fcba6b52dc4218b3d9431fba178d65ad4f6c53cc1122ba61d0ab4061e99a7ebbb15db80011d607c5070ebebf8eddc + FILENAME polyvox.zip +) + +vcpkg_extract_source_archive_ex( + OUT_SOURCE_PATH SOURCE_PATH + ARCHIVE ${SOURCE_ARCHIVE} +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF +) + +vcpkg_install_cmake() + +file(INSTALL ${SOURCE_PATH}/LICENSE.TXT DESTINATION ${CURRENT_PACKAGES_DIR}/share/polyvox RENAME copyright) +file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/include) +if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/lib) +endif() +if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/lib) +endif() +if(WIN32) + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxCore/lib/Release/PolyVoxCore.lib ${CURRENT_PACKAGES_DIR}/lib/PolyVoxCore.lib) + file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxUtil/lib/PolyVoxUtil.lib ${CURRENT_PACKAGES_DIR}/lib/PolyVoxUtil.lib) + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/PolyVoxCore/lib/Debug/PolyVoxCore.lib ${CURRENT_PACKAGES_DIR}/debug/lib/PolyVoxCore.lib) + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/PolyVoxUtil/lib/PolyVoxUtil.lib ${CURRENT_PACKAGES_DIR}/debug/lib/PolyVoxUtil.lib) + endif() + file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxCore/include/PolyVoxCore ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore) + file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxUtil/include/PolyVoxUtil ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil) + file(RENAME ${CURRENT_PACKAGES_DIR}/cmake/PolyVoxConfig.cmake ${CURRENT_PACKAGES_DIR}/share/polyvox/polyvoxConfig.cmake) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/cmake) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/PolyVoxCore) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/PolyVoxUtil) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/cmake) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/PolyVoxCore) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/PolyVoxUtil) +else() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(GLOB LIBS ${CURRENT_PACKAGES_DIR}/lib/Release/*) + foreach(_lib ${LIBS}) + file(RELATIVE_PATH _libName ${CURRENT_PACKAGES_DIR}/lib/Release ${_lib}) + file(RENAME ${_lib} ${CURRENT_PACKAGES_DIR}/lib/${_libName}) + endforeach() + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(GLOB LIBS ${CURRENT_PACKAGES_DIR}/debug/lib/Debug/*) + foreach(_lib ${LIBS}) + file(RELATIVE_PATH _libName ${CURRENT_PACKAGES_DIR}/debug/lib/Debug ${_lib}) + file(RENAME ${_lib} ${CURRENT_PACKAGES_DIR}/debug/lib/${_libName}) + endforeach() + endif() + file(RENAME ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore.temp) + file(RENAME ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore.temp/PolyVoxCore ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore.temp) + file(RENAME ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil.temp) + file(RENAME ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil.temp/PolyVoxUtil ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil.temp) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/share/doc) +endif() +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/Release) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/RelWithDebInfo) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/Debug) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/Release) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/RelWithDebInfo) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/Debug) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) + +# if (APPLE) +# set(INSTALL_NAME_LIBRARY_DIR ${INSTALL_DIR}/lib) +# ExternalProject_Add_Step( +# ${EXTERNAL_NAME} +# change-install-name-debug +# COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" +# COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR}/Debug -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake +# DEPENDEES install +# WORKING_DIRECTORY +# LOG 1 +# ) +# ExternalProject_Add_Step( +# ${EXTERNAL_NAME} +# change-install-name-release +# COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" +# COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR}/Release -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake +# DEPENDEES install +# WORKING_DIRECTORY +# LOG 1 +# ) +# endif () diff --git a/cmake/ports/quazip/CONTROL b/cmake/ports/quazip/CONTROL new file mode 100644 index 0000000000..392c47d7c1 --- /dev/null +++ b/cmake/ports/quazip/CONTROL @@ -0,0 +1,4 @@ +Source: quazip +Version: 0.7.3 +Description: Zip file manipulation for Qt +Build-Depends: zlib diff --git a/cmake/ports/quazip/portfile.cmake b/cmake/ports/quazip/portfile.cmake new file mode 100644 index 0000000000..0789062892 --- /dev/null +++ b/cmake/ports/quazip/portfile.cmake @@ -0,0 +1,96 @@ +include(vcpkg_common_functions) + +file(READ "${VCPKG_ROOT_DIR}/_env/QT_CMAKE_PREFIX_PATH.txt" QT_CMAKE_PREFIX_PATH) +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + +vcpkg_download_distfile( + SOURCE_ARCHIVE + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/quazip-0.7.3.zip + SHA512 b2d812b6346317fd6d8f4f1344ad48b721d697c429acc8b7e7cb776ce5cba15a59efd64b2c5ae1f31b5a3c928014f084aa1379fd55d8a452a6cf4fd510b3afcc + FILENAME quazip.zip +) + +vcpkg_extract_source_archive_ex( + OUT_SOURCE_PATH SOURCE_PATH + ARCHIVE ${SOURCE_ARCHIVE} + NO_REMOVE_ONE_LEVEL +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DBUILD_WITH_QT4=OFF +) + +vcpkg_install_cmake() + +if (WIN32) + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/bin) + file(RENAME ${CURRENT_PACKAGES_DIR}/lib/quazip5.dll ${CURRENT_PACKAGES_DIR}/bin/quazip5.dll) + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/bin) + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/quazip5d.dll ${CURRENT_PACKAGES_DIR}/debug/bin/quazip5.dll) + endif() +elseif(DEFINED VCPKG_TARGET_IS_LINUX) + # We only want static libs. + file(GLOB QUAZIP5_DYNAMIC_LIBS ${CURRENT_PACKAGES_DIR}/lib/libquazip5.so* ${CURRENT_PACKAGES_DIR}/debug/lib/libquazip5d.so*) + file(REMOVE ${QUAZIP5_DYNAMIC_LIBS}) +endif() +file(INSTALL ${SOURCE_PATH}/COPYING DESTINATION ${CURRENT_PACKAGES_DIR}/share/quazip RENAME copyright) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) + +# set(QUAZIP_CMAKE_ARGS +# -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} +# -DCMAKE_INSTALL_NAME_DIR:PATH=/lib +# -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} +# -DZLIB_ROOT=${VCPKG_INSTALL_ROOT} +# -DCMAKE_POSITION_INDEPENDENT_CODE=ON) + +# if (NOT APPLE) +# set(QUAZIP_CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} -DCMAKE_CXX_STANDARD=11) +# endif () + +# ExternalProject_Add( +# ${EXTERNAL_NAME} +# URL +# URL_MD5 ed03754d39b9da1775771819b8001d45 +# BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build +# CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} +# LOG_DOWNLOAD 1 +# LOG_CONFIGURE 1 +# LOG_BUILD 1 +# ) + +# # Hide this external target (for ide users) +# set_target_properties(${EXTERNAL_NAME} PROPERTIES +# FOLDER "hidden/externals" +# INSTALL_NAME_DIR ${INSTALL_DIR}/lib +# BUILD_WITH_INSTALL_RPATH True) + +# ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) +# set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of QuaZip include directories") +# set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of QuaZip include directories") +# set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location of QuaZip DLL") + +# if (APPLE) +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") +# elseif (WIN32) +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library") +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library") +# elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library") +# else () +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") +# set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") +# endif () + +# include(SelectLibraryConfigurations) +# select_library_configurations(${EXTERNAL_NAME_UPPER}) + +# # Force selected libraries into the cache +# set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of QuaZip libraries") +# set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") diff --git a/cmake/ports/sdl2/disable-hidapi-for-uwp.patch b/cmake/ports/sdl2/disable-hidapi-for-uwp.patch index 13d1ca8d1f..670aa801a5 100644 --- a/cmake/ports/sdl2/disable-hidapi-for-uwp.patch +++ b/cmake/ports/sdl2/disable-hidapi-for-uwp.patch @@ -1,11 +1,11 @@ ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -172,7 +172,7 @@ - # requires root permissions to open devices, so that's not generally - # useful, and we'll disable this by default on Unix. Windows and macOS - # can use it without root access, though, so enable by default there. --if(WINDOWS OR APPLE OR ANDROID) -+if((WINDOWS AND NOT WINDOWS_STORE) OR APPLE OR ANDROID) - set(HIDAPI_SKIP_LIBUSB TRUE) - else() - set(HIDAPI_SKIP_LIBUSB FALSE) +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -172,7 +172,7 @@ + # requires root permissions to open devices, so that's not generally + # useful, and we'll disable this by default on Unix. Windows and macOS + # can use it without root access, though, so enable by default there. +-if(WINDOWS OR APPLE OR ANDROID) ++if((WINDOWS AND NOT WINDOWS_STORE) OR APPLE OR ANDROID) + set(HIDAPI_SKIP_LIBUSB TRUE) + else() + set(HIDAPI_SKIP_LIBUSB FALSE) diff --git a/cmake/ports/sdl2/enable-winrt-cmake.patch b/cmake/ports/sdl2/enable-winrt-cmake.patch index 836ffcdd70..93aeda0b84 100644 --- a/cmake/ports/sdl2/enable-winrt-cmake.patch +++ b/cmake/ports/sdl2/enable-winrt-cmake.patch @@ -1,175 +1,175 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 0128c7a..bd534e4 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -5,6 +5,18 @@ endif() - cmake_minimum_required(VERSION 2.8.11) - project(SDL2 C CXX) - -+if(WINDOWS_STORE) -+ enable_language(CXX) -+ cmake_minimum_required(VERSION 3.11) -+ add_definitions(-DSDL_BUILDING_WINRT=1 -ZW) -+ link_libraries( -+ -nodefaultlib:vccorlib$<$:d> -+ -nodefaultlib:msvcrt$<$:d> -+ vccorlib$<$:d>.lib -+ msvcrt$<$:d>.lib -+ ) -+endif() -+ - # !!! FIXME: this should probably do "MACOSX_RPATH ON" as a target property - # !!! FIXME: for the SDL2 shared library (so you get an - # !!! FIXME: install_name ("soname") of "@rpath/libSDL-whatever.dylib" -@@ -1209,6 +1221,11 @@ elseif(WINDOWS) - file(GLOB CORE_SOURCES ${SDL2_SOURCE_DIR}/src/core/windows/*.c) - set(SOURCE_FILES ${SOURCE_FILES} ${CORE_SOURCES}) - -+ if(WINDOWS_STORE) -+ file(GLOB WINRT_SOURCE_FILES ${SDL2_SOURCE_DIR}/src/core/winrt/*.c ${SDL2_SOURCE_DIR}/src/core/winrt/*.cpp) -+ list(APPEND SOURCE_FILES ${WINRT_SOURCE_FILES}) -+ endif() -+ - if(MSVC) - # Prevent codegen that would use the VC runtime libraries. - set_property(DIRECTORY . APPEND PROPERTY COMPILE_OPTIONS "/GS-") -@@ -1254,6 +1271,9 @@ elseif(WINDOWS) - check_include_file(ddraw.h HAVE_DDRAW_H) - check_include_file(dsound.h HAVE_DSOUND_H) - check_include_file(dinput.h HAVE_DINPUT_H) -+ if(WINDOWS_STORE OR VCPKG_TARGET_TRIPLET MATCHES "arm-windows") -+ set(HAVE_DINPUT_H 0) -+ endif() - check_include_file(dxgi.h HAVE_DXGI_H) - if(HAVE_D3D_H OR HAVE_D3D11_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H) - set(HAVE_DIRECTX TRUE) -@@ -1272,18 +1292,20 @@ elseif(WINDOWS) - check_include_file(endpointvolume.h HAVE_ENDPOINTVOLUME_H) - - if(SDL_AUDIO) -+ if(NOT WINDOWS_STORE) - set(SDL_AUDIO_DRIVER_WINMM 1) - file(GLOB WINMM_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/winmm/*.c) - set(SOURCE_FILES ${SOURCE_FILES} ${WINMM_AUDIO_SOURCES}) -+ endif() - set(HAVE_SDL_AUDIO TRUE) - -- if(HAVE_DSOUND_H) -+ if(HAVE_DSOUND_H AND NOT WINDOWS_STORE) - set(SDL_AUDIO_DRIVER_DSOUND 1) - file(GLOB DSOUND_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/directsound/*.c) - set(SOURCE_FILES ${SOURCE_FILES} ${DSOUND_AUDIO_SOURCES}) - endif() - -- if(WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H) -+ if(WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H AND NOT WINDOWS_STORE) - set(SDL_AUDIO_DRIVER_WASAPI 1) - file(GLOB WASAPI_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/wasapi/*.c) - set(SOURCE_FILES ${SOURCE_FILES} ${WASAPI_AUDIO_SOURCES}) -@@ -1295,11 +1317,20 @@ elseif(WINDOWS) - if(NOT SDL_LOADSO) - message_error("SDL_VIDEO requires SDL_LOADSO, which is not enabled") - endif() -+ if(WINDOWS_STORE) -+ set(SDL_VIDEO_DRIVER_WINRT 1) -+ file(GLOB WIN_VIDEO_SOURCES -+ ${SDL2_SOURCE_DIR}/src/video/winrt/*.c -+ ${SDL2_SOURCE_DIR}/src/video/winrt/*.cpp -+ ${SDL2_SOURCE_DIR}/src/render/direct3d11/*.cpp -+ ) -+ else() - set(SDL_VIDEO_DRIVER_WINDOWS 1) - file(GLOB WIN_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/windows/*.c) -+ endif() - set(SOURCE_FILES ${SOURCE_FILES} ${WIN_VIDEO_SOURCES}) - -- if(RENDER_D3D AND HAVE_D3D_H) -+ if(RENDER_D3D AND HAVE_D3D_H AND NOT WINDOWS_STORE) - set(SDL_VIDEO_RENDER_D3D 1) - set(HAVE_RENDER_D3D TRUE) - endif() -@@ -1322,20 +1353,31 @@ elseif(WINDOWS) - endif() - - if(SDL_POWER) -+ if(WINDOWS_STORE) -+ set(SDL_POWER_WINRT 1) -+ set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/winrt/SDL_syspower.cpp) -+ else() - set(SDL_POWER_WINDOWS 1) - set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/windows/SDL_syspower.c) -+ endif() - set(HAVE_SDL_POWER TRUE) - endif() - - if(SDL_FILESYSTEM) - set(SDL_FILESYSTEM_WINDOWS 1) -+ if(WINDOWS_STORE) -+ file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/winrt/*.cpp) -+ else() - file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/windows/*.c) -+ endif() - set(SOURCE_FILES ${SOURCE_FILES} ${FILESYSTEM_SOURCES}) - set(HAVE_SDL_FILESYSTEM TRUE) - endif() - - # Libraries for Win32 native and MinGW -+ if(NOT WINDOWS_STORE) - list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32) -+ endif() - - # TODO: in configure.ac the check for timers is set on - # cygwin | mingw32* - does this include mingw32CE? -@@ -1357,7 +1399,7 @@ elseif(WINDOWS) - set(SOURCE_FILES ${SOURCE_FILES} ${CORE_SOURCES}) - - if(SDL_VIDEO) -- if(VIDEO_OPENGL) -+ if(VIDEO_OPENGL AND NOT WINDOWS_STORE) - set(SDL_VIDEO_OPENGL 1) - set(SDL_VIDEO_OPENGL_WGL 1) - set(SDL_VIDEO_RENDER_OGL 1) -@@ -1788,12 +1830,14 @@ endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") - - # Always build SDLmain -+if(NOT WINDOWS_STORE) - add_library(SDL2main STATIC ${SDLMAIN_SOURCES}) - target_include_directories(SDL2main PUBLIC "$" $) - set(_INSTALL_LIBS "SDL2main") - if (NOT ANDROID) - set_target_properties(SDL2main PROPERTIES DEBUG_POSTFIX ${SDL_CMAKE_DEBUG_POSTFIX}) - endif() -+endif() - - if(SDL_SHARED) - add_library(SDL2 SHARED ${SOURCE_FILES} ${VERSION_SOURCES}) -diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake -index 48dd2d4..0c4fa28 100644 ---- a/include/SDL_config.h.cmake -+++ b/include/SDL_config.h.cmake -@@ -326,6 +326,7 @@ - #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC @SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC@ - #cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@ - #cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@ -+#cmakedefine SDL_VIDEO_DRIVER_WINRT @SDL_VIDEO_DRIVER_WINRT@ - #cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@ - #cmakedefine SDL_VIDEO_DRIVER_RPI @SDL_VIDEO_DRIVER_RPI@ - #cmakedefine SDL_VIDEO_DRIVER_VIVANTE @SDL_VIDEO_DRIVER_VIVANTE@ -@@ -391,6 +392,7 @@ - #cmakedefine SDL_POWER_ANDROID @SDL_POWER_ANDROID@ - #cmakedefine SDL_POWER_LINUX @SDL_POWER_LINUX@ - #cmakedefine SDL_POWER_WINDOWS @SDL_POWER_WINDOWS@ -+#cmakedefine SDL_POWER_WINRT @SDL_POWER_WINRT@ - #cmakedefine SDL_POWER_MACOSX @SDL_POWER_MACOSX@ - #cmakedefine SDL_POWER_HAIKU @SDL_POWER_HAIKU@ - #cmakedefine SDL_POWER_EMSCRIPTEN @SDL_POWER_EMSCRIPTEN@ -@@ -413,7 +415,7 @@ - #cmakedefine SDL_LIBSAMPLERATE_DYNAMIC @SDL_LIBSAMPLERATE_DYNAMIC@ - - /* Platform specific definitions */ --#if !defined(__WIN32__) -+#if !defined(__WIN32__) && !defined(__WINRT__) - # if !defined(_STDINT_H_) && !defined(_STDINT_H) && !defined(HAVE_STDINT_H) && !defined(_HAVE_STDINT_H) - typedef unsigned int size_t; - typedef signed char int8_t; +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0128c7a..bd534e4 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -5,6 +5,18 @@ endif() + cmake_minimum_required(VERSION 2.8.11) + project(SDL2 C CXX) + ++if(WINDOWS_STORE) ++ enable_language(CXX) ++ cmake_minimum_required(VERSION 3.11) ++ add_definitions(-DSDL_BUILDING_WINRT=1 -ZW) ++ link_libraries( ++ -nodefaultlib:vccorlib$<$:d> ++ -nodefaultlib:msvcrt$<$:d> ++ vccorlib$<$:d>.lib ++ msvcrt$<$:d>.lib ++ ) ++endif() ++ + # !!! FIXME: this should probably do "MACOSX_RPATH ON" as a target property + # !!! FIXME: for the SDL2 shared library (so you get an + # !!! FIXME: install_name ("soname") of "@rpath/libSDL-whatever.dylib" +@@ -1209,6 +1221,11 @@ elseif(WINDOWS) + file(GLOB CORE_SOURCES ${SDL2_SOURCE_DIR}/src/core/windows/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${CORE_SOURCES}) + ++ if(WINDOWS_STORE) ++ file(GLOB WINRT_SOURCE_FILES ${SDL2_SOURCE_DIR}/src/core/winrt/*.c ${SDL2_SOURCE_DIR}/src/core/winrt/*.cpp) ++ list(APPEND SOURCE_FILES ${WINRT_SOURCE_FILES}) ++ endif() ++ + if(MSVC) + # Prevent codegen that would use the VC runtime libraries. + set_property(DIRECTORY . APPEND PROPERTY COMPILE_OPTIONS "/GS-") +@@ -1254,6 +1271,9 @@ elseif(WINDOWS) + check_include_file(ddraw.h HAVE_DDRAW_H) + check_include_file(dsound.h HAVE_DSOUND_H) + check_include_file(dinput.h HAVE_DINPUT_H) ++ if(WINDOWS_STORE OR VCPKG_TARGET_TRIPLET MATCHES "arm-windows") ++ set(HAVE_DINPUT_H 0) ++ endif() + check_include_file(dxgi.h HAVE_DXGI_H) + if(HAVE_D3D_H OR HAVE_D3D11_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H) + set(HAVE_DIRECTX TRUE) +@@ -1272,18 +1292,20 @@ elseif(WINDOWS) + check_include_file(endpointvolume.h HAVE_ENDPOINTVOLUME_H) + + if(SDL_AUDIO) ++ if(NOT WINDOWS_STORE) + set(SDL_AUDIO_DRIVER_WINMM 1) + file(GLOB WINMM_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/winmm/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${WINMM_AUDIO_SOURCES}) ++ endif() + set(HAVE_SDL_AUDIO TRUE) + +- if(HAVE_DSOUND_H) ++ if(HAVE_DSOUND_H AND NOT WINDOWS_STORE) + set(SDL_AUDIO_DRIVER_DSOUND 1) + file(GLOB DSOUND_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/directsound/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${DSOUND_AUDIO_SOURCES}) + endif() + +- if(WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H) ++ if(WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H AND NOT WINDOWS_STORE) + set(SDL_AUDIO_DRIVER_WASAPI 1) + file(GLOB WASAPI_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/wasapi/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${WASAPI_AUDIO_SOURCES}) +@@ -1295,11 +1317,20 @@ elseif(WINDOWS) + if(NOT SDL_LOADSO) + message_error("SDL_VIDEO requires SDL_LOADSO, which is not enabled") + endif() ++ if(WINDOWS_STORE) ++ set(SDL_VIDEO_DRIVER_WINRT 1) ++ file(GLOB WIN_VIDEO_SOURCES ++ ${SDL2_SOURCE_DIR}/src/video/winrt/*.c ++ ${SDL2_SOURCE_DIR}/src/video/winrt/*.cpp ++ ${SDL2_SOURCE_DIR}/src/render/direct3d11/*.cpp ++ ) ++ else() + set(SDL_VIDEO_DRIVER_WINDOWS 1) + file(GLOB WIN_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/windows/*.c) ++ endif() + set(SOURCE_FILES ${SOURCE_FILES} ${WIN_VIDEO_SOURCES}) + +- if(RENDER_D3D AND HAVE_D3D_H) ++ if(RENDER_D3D AND HAVE_D3D_H AND NOT WINDOWS_STORE) + set(SDL_VIDEO_RENDER_D3D 1) + set(HAVE_RENDER_D3D TRUE) + endif() +@@ -1322,20 +1353,31 @@ elseif(WINDOWS) + endif() + + if(SDL_POWER) ++ if(WINDOWS_STORE) ++ set(SDL_POWER_WINRT 1) ++ set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/winrt/SDL_syspower.cpp) ++ else() + set(SDL_POWER_WINDOWS 1) + set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/windows/SDL_syspower.c) ++ endif() + set(HAVE_SDL_POWER TRUE) + endif() + + if(SDL_FILESYSTEM) + set(SDL_FILESYSTEM_WINDOWS 1) ++ if(WINDOWS_STORE) ++ file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/winrt/*.cpp) ++ else() + file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/windows/*.c) ++ endif() + set(SOURCE_FILES ${SOURCE_FILES} ${FILESYSTEM_SOURCES}) + set(HAVE_SDL_FILESYSTEM TRUE) + endif() + + # Libraries for Win32 native and MinGW ++ if(NOT WINDOWS_STORE) + list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32) ++ endif() + + # TODO: in configure.ac the check for timers is set on + # cygwin | mingw32* - does this include mingw32CE? +@@ -1357,7 +1399,7 @@ elseif(WINDOWS) + set(SOURCE_FILES ${SOURCE_FILES} ${CORE_SOURCES}) + + if(SDL_VIDEO) +- if(VIDEO_OPENGL) ++ if(VIDEO_OPENGL AND NOT WINDOWS_STORE) + set(SDL_VIDEO_OPENGL 1) + set(SDL_VIDEO_OPENGL_WGL 1) + set(SDL_VIDEO_RENDER_OGL 1) +@@ -1788,12 +1830,14 @@ endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + + # Always build SDLmain ++if(NOT WINDOWS_STORE) + add_library(SDL2main STATIC ${SDLMAIN_SOURCES}) + target_include_directories(SDL2main PUBLIC "$" $) + set(_INSTALL_LIBS "SDL2main") + if (NOT ANDROID) + set_target_properties(SDL2main PROPERTIES DEBUG_POSTFIX ${SDL_CMAKE_DEBUG_POSTFIX}) + endif() ++endif() + + if(SDL_SHARED) + add_library(SDL2 SHARED ${SOURCE_FILES} ${VERSION_SOURCES}) +diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake +index 48dd2d4..0c4fa28 100644 +--- a/include/SDL_config.h.cmake ++++ b/include/SDL_config.h.cmake +@@ -326,6 +326,7 @@ + #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC @SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC@ + #cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@ + #cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@ ++#cmakedefine SDL_VIDEO_DRIVER_WINRT @SDL_VIDEO_DRIVER_WINRT@ + #cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@ + #cmakedefine SDL_VIDEO_DRIVER_RPI @SDL_VIDEO_DRIVER_RPI@ + #cmakedefine SDL_VIDEO_DRIVER_VIVANTE @SDL_VIDEO_DRIVER_VIVANTE@ +@@ -391,6 +392,7 @@ + #cmakedefine SDL_POWER_ANDROID @SDL_POWER_ANDROID@ + #cmakedefine SDL_POWER_LINUX @SDL_POWER_LINUX@ + #cmakedefine SDL_POWER_WINDOWS @SDL_POWER_WINDOWS@ ++#cmakedefine SDL_POWER_WINRT @SDL_POWER_WINRT@ + #cmakedefine SDL_POWER_MACOSX @SDL_POWER_MACOSX@ + #cmakedefine SDL_POWER_HAIKU @SDL_POWER_HAIKU@ + #cmakedefine SDL_POWER_EMSCRIPTEN @SDL_POWER_EMSCRIPTEN@ +@@ -413,7 +415,7 @@ + #cmakedefine SDL_LIBSAMPLERATE_DYNAMIC @SDL_LIBSAMPLERATE_DYNAMIC@ + + /* Platform specific definitions */ +-#if !defined(__WIN32__) ++#if !defined(__WIN32__) && !defined(__WINRT__) + # if !defined(_STDINT_H_) && !defined(_STDINT_H) && !defined(HAVE_STDINT_H) && !defined(_HAVE_STDINT_H) + typedef unsigned int size_t; + typedef signed char int8_t; diff --git a/cmake/ports/sdl2/fix-arm64-headers.patch b/cmake/ports/sdl2/fix-arm64-headers.patch index 862b65f0b5..3caaf493cd 100644 --- a/cmake/ports/sdl2/fix-arm64-headers.patch +++ b/cmake/ports/sdl2/fix-arm64-headers.patch @@ -1,14 +1,14 @@ ---- a/include/SDL_cpuinfo.h Tue Jul 23 21:41:00 2019 -0400 -+++ b/include/SDL_cpuinfo.h Tue Aug 13 20:26:27 2019 -0700 -@@ -73,8 +73,8 @@ - # define __ARM_NEON 1 /* Set __ARM_NEON so that it can be used elsewhere, at compile time */ - # endif - # if defined (_M_ARM64) --# include --# include -+# include -+# include - # define __ARM_NEON 1 /* Set __ARM_NEON so that it can be used elsewhere, at compile time */ - # endif - # endif +--- a/include/SDL_cpuinfo.h Tue Jul 23 21:41:00 2019 -0400 ++++ b/include/SDL_cpuinfo.h Tue Aug 13 20:26:27 2019 -0700 +@@ -73,8 +73,8 @@ + # define __ARM_NEON 1 /* Set __ARM_NEON so that it can be used elsewhere, at compile time */ + # endif + # if defined (_M_ARM64) +-# include +-# include ++# include ++# include + # define __ARM_NEON 1 /* Set __ARM_NEON so that it can be used elsewhere, at compile time */ + # endif + # endif \ No newline at end of file diff --git a/cmake/ports/sdl2/fix-x86-windows.patch b/cmake/ports/sdl2/fix-x86-windows.patch index 853b68722c..c83773b765 100644 --- a/cmake/ports/sdl2/fix-x86-windows.patch +++ b/cmake/ports/sdl2/fix-x86-windows.patch @@ -1,15 +1,15 @@ -diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c -index ff23c5e..fc90bba 100644 ---- a/src/events/SDL_mouse.c -+++ b/src/events/SDL_mouse.c -@@ -20,6 +20,10 @@ - */ - #include "../SDL_internal.h" - -+#ifdef __WIN32__ -+#include "../core/windows/SDL_windows.h" -+#endif -+ - /* General mouse handling code for SDL */ - - #include "SDL_assert.h" +diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c +index ff23c5e..fc90bba 100644 +--- a/src/events/SDL_mouse.c ++++ b/src/events/SDL_mouse.c +@@ -20,6 +20,10 @@ + */ + #include "../SDL_internal.h" + ++#ifdef __WIN32__ ++#include "../core/windows/SDL_windows.h" ++#endif ++ + /* General mouse handling code for SDL */ + + #include "SDL_assert.h" diff --git a/cmake/ports/sdl2/vcpkg-cmake-wrapper.cmake b/cmake/ports/sdl2/vcpkg-cmake-wrapper.cmake index c99178db1a..067828055f 100644 --- a/cmake/ports/sdl2/vcpkg-cmake-wrapper.cmake +++ b/cmake/ports/sdl2/vcpkg-cmake-wrapper.cmake @@ -1,8 +1,8 @@ -_find_package(${ARGS}) -if(TARGET SDL2::SDL2 AND NOT TARGET SDL2::SDL2-static) - add_library( SDL2::SDL2-static INTERFACE IMPORTED) - set_target_properties(SDL2::SDL2-static PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2::SDL2") -elseif(TARGET SDL2::SDL2-static AND NOT TARGET SDL2::SDL2) - add_library( SDL2::SDL2 INTERFACE IMPORTED) - set_target_properties(SDL2::SDL2 PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2::SDL2-static") -endif() +_find_package(${ARGS}) +if(TARGET SDL2::SDL2 AND NOT TARGET SDL2::SDL2-static) + add_library( SDL2::SDL2-static INTERFACE IMPORTED) + set_target_properties(SDL2::SDL2-static PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2::SDL2") +elseif(TARGET SDL2::SDL2-static AND NOT TARGET SDL2::SDL2) + add_library( SDL2::SDL2 INTERFACE IMPORTED) + set_target_properties(SDL2::SDL2 PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2::SDL2-static") +endif() diff --git a/cmake/ports/sranipal/CONTROL b/cmake/ports/sranipal/CONTROL new file mode 100644 index 0000000000..b7d510595e --- /dev/null +++ b/cmake/ports/sranipal/CONTROL @@ -0,0 +1,3 @@ +Source: sranipal +Version: 1.1.0.1 +Description: super reality animation pal! diff --git a/cmake/ports/sranipal/portfile.cmake b/cmake/ports/sranipal/portfile.cmake new file mode 100644 index 0000000000..4c4d88e567 --- /dev/null +++ b/cmake/ports/sranipal/portfile.cmake @@ -0,0 +1,22 @@ +include(vcpkg_common_functions) +set(SRANIPAL_VERSION 1.1.0.1) +set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src) + +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + +if (WIN32) + vcpkg_download_distfile( + SRANIPAL_SOURCE_ARCHIVE + URLS "${EXTERNAL_BUILD_ASSETS}/seth/sranipal-1.1.0.1-2-windows.zip" + SHA512 f1f68f6beef52ae5e034bc3f44932ae0800ee187b75d80e76ae7b17b8ddd7bc54c039ce5594d231035e3caf3a61fed36f38621a860b4fb20170cb0176d9c28f0 + FILENAME sranipal-1.1.0.1-2-windows.zip + ) + + vcpkg_extract_source_archive(${SRANIPAL_SOURCE_ARCHIVE}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/sranipal/include DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/sranipal/lib DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/sranipal/debug DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/sranipal/bin DESTINATION ${CURRENT_PACKAGES_DIR}) + file(COPY ${MASTER_COPY_SOURCE_PATH}/sranipal/share DESTINATION ${CURRENT_PACKAGES_DIR}) + +endif () diff --git a/cmake/ports/tbb/CMakeLists.txt b/cmake/ports/tbb/CMakeLists.txt index edfaf63200..863abb52de 100644 --- a/cmake/ports/tbb/CMakeLists.txt +++ b/cmake/ports/tbb/CMakeLists.txt @@ -4,7 +4,14 @@ file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*) file(COPY ${SOURCES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/src) include(${CMAKE_CURRENT_BINARY_DIR}/src/cmake/TBBBuild.cmake REQUIRED) -tbb_build(TBB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/src MAKE_ARGS extra_inc=big_iron.inc) +if(NOT BUILD_SHARED_LIBS) + set(TBB_STATIC_INCLUDE extra_inc=big_iron.inc) +endif() +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(FORWARD_SDK_ROOT "SDKROOT=${CMAKE_OSX_SYSROOT}") +endif() + +tbb_build(TBB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/src MAKE_ARGS ${TBB_STATIC_INCLUDE} ${FORWARD_SDK_ROOT}) set(SUBDIR ${CMAKE_CURRENT_BINARY_DIR}/tbb_cmake_build/tbb_cmake_build_subdir) if(CMAKE_BUILD_TYPE STREQUAL "Release") diff --git a/cmake/ports/tbb/CONTROL b/cmake/ports/tbb/CONTROL index e87106dc3c..73569fe661 100644 --- a/cmake/ports/tbb/CONTROL +++ b/cmake/ports/tbb/CONTROL @@ -1,3 +1,4 @@ Source: tbb -Version: 2018_U5-4 +Version: 2019_U8-1 +Homepage: https://github.com/oneapi-src/oneTBB Description: Intel's Threading Building Blocks. diff --git a/cmake/ports/tbb/fix-static-build.patch b/cmake/ports/tbb/fix-static-build.patch new file mode 100644 index 0000000000..18f3e2b493 --- /dev/null +++ b/cmake/ports/tbb/fix-static-build.patch @@ -0,0 +1,13 @@ +diff --git a/src/tbb/cilk-tbb-interop.h b/src/tbb/cilk-tbb-interop.h +index 295734b..f35531e 100644 +--- a/src/tbb/cilk-tbb-interop.h ++++ b/src/tbb/cilk-tbb-interop.h +@@ -29,6 +29,8 @@ + #else + #ifdef IN_CILK_RUNTIME + #define CILK_EXPORT __declspec(dllexport) ++#elif defined(IN_CILK_STATIC) ++#define CILK_EXPORT /* nothing */ + #else + #define CILK_EXPORT __declspec(dllimport) + #endif // IN_CILK_RUNTIME diff --git a/cmake/ports/tbb/portfile.cmake b/cmake/ports/tbb/portfile.cmake index 9d0eb70ac1..4e81df37d1 100644 --- a/cmake/ports/tbb/portfile.cmake +++ b/cmake/ports/tbb/portfile.cmake @@ -1,20 +1,17 @@ include(vcpkg_common_functions) -if(NOT VCPKG_CMAKE_SYSTEM_NAME OR VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") - vcpkg_check_linkage(ONLY_DYNAMIC_LIBRARY) -endif() - vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH - REPO 01org/tbb - REF 2018_U5 - SHA512 d86a110df9e55654c3638af8107fdfed2284434158cb5b3a38b3fc7cf143aa2346ee15da4e141e03fcfed864865462e6893c535b8dc227ebdd6ccd584c8a1e9b - HEAD_REF tbb_2018 + REPO oneapi-src/oneTBB + REF 4bdba61bafc6ba2d636f31564f1de5702d365cf7 + SHA512 f2a8d7e0476f846039390f4a79af3fe13770e23b01bf4741e738136f7ddb401357a0e50f35212e8d0fa5fc4cf1563418337309227d7243fc3676edd406ae652d + HEAD_REF tbb_2019 + PATCHES fix-static-build.patch ) file(COPY ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt DESTINATION ${SOURCE_PATH}) -if(VCPKG_CMAKE_SYSTEM_NAME AND NOT VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") +if (NOT VCPKG_TARGET_IS_WINDOWS) vcpkg_configure_cmake( SOURCE_PATH ${SOURCE_PATH} PREFER_NINJA @@ -22,7 +19,7 @@ if(VCPKG_CMAKE_SYSTEM_NAME AND NOT VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStor vcpkg_install_cmake() - # Settings for TBBConfigForSource.cmake.in + # Settings for TBBConfigInternal.cmake.in set(TBB_LIB_EXT a) set(TBB_LIB_PREFIX lib) else() @@ -33,6 +30,32 @@ else() set(RELEASE_CONFIGURATION Release) set(DEBUG_CONFIGURATION Debug) endif() + + macro(CONFIGURE_PROJ_FILE arg) + set(CONFIGURE_FILE_NAME ${arg}) + set(CONFIGURE_BAK_FILE_NAME ${arg}.bak) + if (NOT EXISTS ${CONFIGURE_BAK_FILE_NAME}) + configure_file(${CONFIGURE_FILE_NAME} ${CONFIGURE_BAK_FILE_NAME} COPYONLY) + endif() + configure_file(${CONFIGURE_BAK_FILE_NAME} ${CONFIGURE_FILE_NAME} COPYONLY) + if (VCPKG_LIBRARY_LINKAGE STREQUAL static) + file(READ ${CONFIGURE_FILE_NAME} SLN_CONFIGURE) + string(REPLACE "DynamicLibrary<\/ConfigurationType>" + "StaticLibrary<\/ConfigurationType>" SLN_CONFIGURE "${SLN_CONFIGURE}") + string(REPLACE "\/D_CRT_SECURE_NO_DEPRECATE" + "\/D_CRT_SECURE_NO_DEPRECATE \/DIN_CILK_STATIC" SLN_CONFIGURE "${SLN_CONFIGURE}") + file(WRITE ${CONFIGURE_FILE_NAME} "${SLN_CONFIGURE}") + else() + file(READ ${CONFIGURE_FILE_NAME} SLN_CONFIGURE) + string(REPLACE "\/D_CRT_SECURE_NO_DEPRECATE" + "\/D_CRT_SECURE_NO_DEPRECATE \/DIN_CILK_RUNTIME" SLN_CONFIGURE "${SLN_CONFIGURE}") + file(WRITE ${CONFIGURE_FILE_NAME} "${SLN_CONFIGURE}") + endif() + endmacro() + + CONFIGURE_PROJ_FILE(${SOURCE_PATH}/build/vs2013/tbb.vcxproj) + CONFIGURE_PROJ_FILE(${SOURCE_PATH}/build/vs2013/tbbmalloc.vcxproj) + CONFIGURE_PROJ_FILE(${SOURCE_PATH}/build/vs2013/tbbmalloc_proxy.vcxproj) vcpkg_install_msbuild( SOURCE_PATH ${SOURCE_PATH} @@ -40,7 +63,7 @@ else() RELEASE_CONFIGURATION ${RELEASE_CONFIGURATION} DEBUG_CONFIGURATION ${DEBUG_CONFIGURATION} ) - # Settings for TBBConfigForSource.cmake.in + # Settings for TBBConfigInternal.cmake.in set(TBB_LIB_EXT lib) set(TBB_LIB_PREFIX) endif() @@ -50,12 +73,13 @@ file(COPY ${SOURCE_PATH}/include/serial DESTINATION ${CURRENT_PACKAGES_DIR}/include) -# Settings for TBBConfigForSource.cmake.in +# Settings for TBBConfigInternal.cmake.in if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") set(TBB_DEFAULT_COMPONENTS tbb tbbmalloc) else() set(TBB_DEFAULT_COMPONENTS tbb tbbmalloc tbbmalloc_proxy) endif() + file(READ "${SOURCE_PATH}/include/tbb/tbb_stddef.h" _tbb_stddef) string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" _tbb_ver_major "${_tbb_stddef}") string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" _tbb_ver_minor "${_tbb_stddef}") @@ -65,7 +89,7 @@ set(TBB_RELEASE_DIR "\${_tbb_root}/lib") set(TBB_DEBUG_DIR "\${_tbb_root}/debug/lib") configure_file( - ${SOURCE_PATH}/cmake/templates/TBBConfigForSource.cmake.in + ${SOURCE_PATH}/cmake/templates/TBBConfigInternal.cmake.in ${CURRENT_PACKAGES_DIR}/share/tbb/TBBConfig.cmake @ONLY ) @@ -76,6 +100,18 @@ string(REPLACE _contents "${_contents}" ) +string(REPLACE + "set(_tbb_release_lib \"/${TBB_LIB_PREFIX}" + "set(_tbb_release_lib \"\${_tbb_root}/lib/${TBB_LIB_PREFIX}" + _contents + "${_contents}" +) +string(REPLACE + "set(_tbb_debug_lib \"/${TBB_LIB_PREFIX}" + "set(_tbb_debug_lib \"\${_tbb_root}/debug/lib/${TBB_LIB_PREFIX}" + _contents + "${_contents}" +) string(REPLACE "SHARED IMPORTED)" "UNKNOWN IMPORTED)" _contents "${_contents}") file(WRITE ${CURRENT_PACKAGES_DIR}/share/tbb/TBBConfig.cmake "${_contents}") @@ -84,3 +120,4 @@ file(COPY ${SOURCE_PATH}/LICENSE ${CMAKE_CURRENT_LIST_DIR}/usage DESTINATION ${C file(RENAME ${CURRENT_PACKAGES_DIR}/share/tbb/LICENSE ${CURRENT_PACKAGES_DIR}/share/tbb/copyright) vcpkg_test_cmake(PACKAGE_NAME TBB) +# diff --git a/cmake/ports/vhacd/CONTROL b/cmake/ports/vhacd/CONTROL new file mode 100644 index 0000000000..a324451b00 --- /dev/null +++ b/cmake/ports/vhacd/CONTROL @@ -0,0 +1,3 @@ +Source: vhacd +Version: 20191029 +Description: vhacd diff --git a/cmake/ports/vhacd/copyright b/cmake/ports/vhacd/copyright new file mode 100644 index 0000000000..6706bf78c2 --- /dev/null +++ b/cmake/ports/vhacd/copyright @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2011, Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/cmake/ports/vhacd/portfile.cmake b/cmake/ports/vhacd/portfile.cmake new file mode 100644 index 0000000000..4c74bb6532 --- /dev/null +++ b/cmake/ports/vhacd/portfile.cmake @@ -0,0 +1,40 @@ +include(vcpkg_common_functions) +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + +# else Linux desktop +vcpkg_download_distfile( + SOURCE_ARCHIVE + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/v-hacd-master.zip + SHA512 5d9bd4872ead9eb3574e4806d6c4f490353a04036fd5f571e1e44f47cb66b709e311abcd53af30bae0015a690152170aeed93209a626c28ebcfd6591f3bb036f + FILENAME vhacd.zip +) + +vcpkg_extract_source_archive_ex( + OUT_SOURCE_PATH SOURCE_PATH + ARCHIVE ${SOURCE_ARCHIVE} +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA +) + +vcpkg_install_cmake() + +file(COPY ${CMAKE_CURRENT_LIST_DIR}/copyright DESTINATION ${CURRENT_PACKAGES_DIR}/share/vhacd) +if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +endif() + +if (WIN32) + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(RENAME ${CURRENT_PACKAGES_DIR}/lib/Release/VHACD_LIB.lib ${CURRENT_PACKAGES_DIR}/lib/VHACD.lib) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/Release) + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/Debug/VHACD_LIB.lib ${CURRENT_PACKAGES_DIR}/debug/lib/VHACD.lib) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/Debug) + endif() +endif() diff --git a/cmake/ports/zlib/CONTROL b/cmake/ports/zlib/CONTROL index c559637834..601fb1bc0e 100644 --- a/cmake/ports/zlib/CONTROL +++ b/cmake/ports/zlib/CONTROL @@ -1,3 +1,4 @@ Source: zlib -Version: 1.2.11-3 +Version: 1.2.11-6 +Homepage: https://www.zlib.net/ Description: A compression library diff --git a/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch b/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch index 229a2d055b..a374f76d62 100644 --- a/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch +++ b/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch @@ -1,5 +1,5 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 0fe939d..8d2f5f1 100644 +index 0fe939d..a1291d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(VERSION "1.2.11") @@ -10,24 +10,56 @@ index 0fe939d..8d2f5f1 100644 set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables") set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries") -@@ -211,7 +212,15 @@ elseif(BUILD_SHARED_LIBS AND WIN32) +@@ -124,9 +125,11 @@ set(ZLIB_SRCS + ) + + if(NOT MINGW) +- set(ZLIB_DLL_SRCS +- win32/zlib1.rc # If present will override custom build rule below. +- ) ++ if(BUILD_SHARED_LIBS) ++ set(ZLIB_DLL_SRCS ++ win32/zlib1.rc # If present will override custom build rule below. ++ ) ++ endif() + endif() + + if(CMAKE_COMPILER_IS_GNUCC) +@@ -180,11 +183,12 @@ if(MINGW) + -I ${CMAKE_CURRENT_BINARY_DIR} + -o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj + -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc) +- set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) ++ if(BUILD_SHARED_LIBS) ++ set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) ++ endif() + endif(MINGW) + +-add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) +-add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) ++add_library(zlib ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) + set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) + set_target_properties(zlib PROPERTIES SOVERSION 1) + +@@ -201,7 +205,7 @@ endif() + + if(UNIX) + # On unix-like platforms the library is almost always called libz +- set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z) ++ set_target_properties(zlib PROPERTIES OUTPUT_NAME z) + if(NOT APPLE) + set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"") + endif() +@@ -211,7 +215,7 @@ elseif(BUILD_SHARED_LIBS AND WIN32) endif() if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) - install(TARGETS zlib zlibstatic -+ if (BUILD_SHARED_LIBS) -+ set(ZLIB_TARGETS zlib) -+ set_target_properties(zlibstatic PROPERTIES EXCLUDE_FROM_ALL ON) -+ else() -+ set(ZLIB_TARGETS zlibstatic) -+ set_target_properties(zlib PROPERTIES EXCLUDE_FROM_ALL ON) -+ endif() -+ -+ install(TARGETS ${ZLIB_TARGETS} ++ install(TARGETS zlib RUNTIME DESTINATION "${INSTALL_BIN_DIR}" ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ) -@@ -230,6 +239,7 @@ endif() +@@ -230,6 +234,7 @@ endif() # Example binaries #============================================================================ @@ -35,7 +67,7 @@ index 0fe939d..8d2f5f1 100644 add_executable(example test/example.c) target_link_libraries(example zlib) add_test(example example) -@@ -247,3 +257,4 @@ if(HAVE_OFF64_T) +@@ -247,3 +252,4 @@ if(HAVE_OFF64_T) target_link_libraries(minigzip64 zlib) set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") endif() diff --git a/cmake/ports/zlib/portfile.cmake b/cmake/ports/zlib/portfile.cmake index f2cc68b7a3..78030309b1 100644 --- a/cmake/ports/zlib/portfile.cmake +++ b/cmake/ports/zlib/portfile.cmake @@ -1,16 +1,19 @@ include(vcpkg_common_functions) -set(SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/zlib-1.2.11) + +set(VERSION 1.2.11) + vcpkg_download_distfile(ARCHIVE_FILE - URLS "http://www.zlib.net/zlib-1.2.11.tar.gz" "https://downloads.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gz" + URLS "http://www.zlib.net/zlib-${VERSION}.tar.gz" "https://downloads.sourceforge.net/project/libpng/zlib/${VERSION}/zlib-${VERSION}.tar.gz" FILENAME "zlib1211.tar.gz" SHA512 73fd3fff4adeccd4894084c15ddac89890cd10ef105dd5e1835e1e9bbb6a49ff229713bd197d203edfa17c2727700fce65a2a235f07568212d820dca88b528ae ) -vcpkg_extract_source_archive(${ARCHIVE_FILE}) -vcpkg_apply_patches( - SOURCE_PATH ${SOURCE_PATH} +vcpkg_extract_source_archive_ex( + OUT_SOURCE_PATH SOURCE_PATH + ARCHIVE ${ARCHIVE_FILE} + REF ${VERSION} PATCHES - ${CMAKE_CURRENT_LIST_DIR}/cmake_dont_build_more_than_needed.patch + "cmake_dont_build_more_than_needed.patch" ) # This is generated during the cmake build @@ -44,4 +47,4 @@ vcpkg_copy_pdbs() file(COPY ${CMAKE_CURRENT_LIST_DIR}/usage DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT}) -#vcpkg_test_cmake(PACKAGE_NAME ZLIB MODULE) +vcpkg_test_cmake(PACKAGE_NAME ZLIB MODULE) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index ded90494bf..a7b1ba91c8 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -720,7 +720,7 @@ Function InstallTypesPage StrCpy $OffsetUnits u StrCpy $Express "0" - ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Project Athena Interface and Project Athena Sandbox" + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface and Vircadia Sandbox" pop $ExpressInstallRadioButton ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel IntOp $CurrentOffset $CurrentOffset + 15 @@ -871,7 +871,7 @@ Function PostInstallOptionsPage Pop $LaunchConsoleNowCheckbox ; set the checkbox state depending on what is present in the registry - !insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} + !insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_UNCHECKED} ${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE ${IfNot} $substringResult == "" ${NSD_SetState} $LaunchConsoleNowCheckbox ${BST_UNCHECKED} @@ -887,7 +887,7 @@ Function PostInstallOptionsPage IntOp $CurrentOffset $CurrentOffset + 15 ; set the checkbox state depending on what is present in the registry - !insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED} + !insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_UNCHECKED} ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)" Pop $CleanInstallCheckbox @@ -940,8 +940,8 @@ Function ReadInstallTypes StrCpy $Express "1" StrCpy $DesktopClientState ${BST_CHECKED} - StrCpy $ConsoleStartupState ${BST_CHECKED} - StrCpy $LaunchConsoleNowState ${BST_CHECKED} + StrCpy $ConsoleStartupState ${BST_UNCHECKED} + StrCpy $LaunchConsoleNowState ${BST_UNCHECKED} StrCpy $LaunchClientNowState ${BST_CHECKED} StrCpy $CleanInstallState ${BST_UNCHECKED} StrCpy $DesktopConsoleState ${BST_UNCHECKED} @@ -973,7 +973,7 @@ Function ReadPostInstallOptions ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${LogText} "Option: Install Client" - ; check if the user asked for a desktop shortcut to Project Athena + ; check if the user asked for a desktop shortcut to Vircadia ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState ${LogText} "Option: Create Client Desktop Shortcut: $DesktopClientState" ${EndIf} @@ -1027,7 +1027,7 @@ Function HandlePostInstallOptions ${EndIf} ${If} @CLIENT_COMPONENT_CONDITIONAL@ - ; check if the user asked for a desktop shortcut to Project Athena + ; check if the user asked for a desktop shortcut to Vircadia ${If} $DesktopClientState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" !insertmacro WriteInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES @@ -1088,7 +1088,7 @@ Function HandlePostInstallOptions ClearErrors ; copy the data from production build to this PR build - CopyFiles "$APPDATA\Project Athena\*" $0 + CopyFiles "$APPDATA\Vircadia\*" $0 ; handle an error in copying files IfErrors 0 NoError diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index cdf92918c6..76924d8165 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -3,7 +3,7 @@ "settings": [ { "name": "metaverse", - "label": "Metaverse / Networking", + "label": "Networking / Metaverse", "settings": [ { "name": "access_token", @@ -57,6 +57,62 @@ } ] }, + { + "name": "authentication", + "label": "Networking / WordPress OAuth2", + "settings": [ + { + "name": "enable_oauth2", + "label": "Enable OAuth2 Authentication", + "help": "Allow a WordPress-based (miniOrange) OAuth2 service to assign users to groups based on their role with the service.", + "default": false, + "type": "checkbox", + "advanced": true + }, + { + "name": "oauth2_url_path", + "label": "Authentication URL", + "help": "The URL that the Interface will use to login via OAuth2.", + "advanced": true + }, + { + "name": "wordpress_url_base", + "label": "WordPress API URL Base", + "help": "The URL base that the domain server will use to make WordPress API calls. Typically \"https://oursite.com/wp-json/\". However, if using non-pretty permalinks or otherwise get a 404 error then try \"https://oursite.com/?rest_route=/\".", + "advanced": true + }, + { + "name": "plugin_client_id", + "label": "WordPress Plugin Client ID", + "help": "This is the client ID from the WordPress plugin configuration.", + "advanced": true, + "backup": false + } + ] + }, + { + "label": "Monitoring", + "name": "monitoring", + "restart": false, + "settings": [ + { + "name": "enable_prometheus_exporter", + "label": "Enable Prometheus Exporter", + "help": "Enable a Prometheus exporter to make it possible to gather the stats that are available at Nodes tab with a Prometheus server. This makes it possible to keep track of long-term domain statistics for graphing, troubleshooting, and performance monitoring.", + "default": false, + "type": "checkbox", + "advanced": true + }, + { + "name": "prometheus_exporter_port", + "label": "Prometheus TCP Port", + "help": "This is the port where the Prometheus exporter accepts connections. It listens both on IPv4 and IPv6 and can be accessed remotely, so you should make sure to restrict access with a firewall as needed.", + "default": "9703", + "type": "int", + "advanced": true + } + ] + }, { "label": "Paths", "html_id": "paths", @@ -224,7 +280,7 @@ "name": "standard_permissions", "type": "table", "label": "Domain-Wide User Permissions", - "help": "Indicate which types of users can have which domain-wide permissions.", + "help": "Indicate which types of users can have which domain-wide permissions.", "caption": "Standard Permissions", "can_add_new_rows": false, "groups": [ @@ -233,7 +289,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -314,7 +370,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false @@ -358,6 +414,7 @@ "name": "group_permissions", "type": "table", "caption": "Permissions for Users in Groups", + "help": "For groups that are provided from WordPress you need to denote them by putting an \"@\" symbol in front of each item, e.g., \"@silver\".", "categorize_by_key": "permissions_id", "can_add_new_categories": true, "can_add_new_rows": false, @@ -369,7 +426,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -475,7 +532,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false @@ -486,6 +543,7 @@ "name": "group_forbiddens", "type": "table", "caption": "Permissions Denied to Users in Groups", + "help": "For groups that are provided from WordPress you need to denote them by putting an \"@\" symbol in front of each item, e.g., \"@silver\".", "categorize_by_key": "permissions_id", "can_add_new_categories": true, "can_add_new_rows": false, @@ -497,7 +555,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -600,7 +658,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false @@ -618,7 +676,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -699,7 +757,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false @@ -717,7 +775,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -798,7 +856,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false @@ -816,7 +874,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -897,7 +955,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false @@ -915,7 +973,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 11 } ], @@ -996,7 +1054,7 @@ }, { "name": "id_can_get_and_set_private_user_data", - "label": "Can Get and Set Private User Data", + "label": "Get and Set Private User Data", "type": "checkbox", "editable": true, "default": false diff --git a/domain-server/resources/prometheus_exporter/index.html b/domain-server/resources/prometheus_exporter/index.html new file mode 100644 index 0000000000..5a23c78858 --- /dev/null +++ b/domain-server/resources/prometheus_exporter/index.html @@ -0,0 +1,14 @@ + + + Vircadia Prometheus exporter + + + +

Vircadia Prometheus exporter

+ +

This is the Prometheus exporter, used to export stats about the domain server for graphing and analysis.

+

+ Metrics +

+ + diff --git a/domain-server/resources/web/favicon.ico b/domain-server/resources/web/favicon.ico index becc1b8e8b..cbcf364902 100644 Binary files a/domain-server/resources/web/favicon.ico and b/domain-server/resources/web/favicon.ico differ diff --git a/domain-server/resources/web/js/shared.js b/domain-server/resources/web/js/shared.js index f4053ebddc..3c7dd2705c 100644 --- a/domain-server/resources/web/js/shared.js +++ b/domain-server/resources/web/js/shared.js @@ -52,6 +52,7 @@ var URLs = { // STABLE METAVERSE_URL: https://metaverse.highfidelity.com // STAGING METAVERSE_URL: https://staging.highfidelity.com METAVERSE_URL: 'https://metaverse.highfidelity.com', + CDN_URL: 'https://cdn.highfidelity.com', PLACE_URL: 'https://hifi.place', }; diff --git a/domain-server/resources/web/wizard/index.shtml b/domain-server/resources/web/wizard/index.shtml index 3bc7503b44..37f9d9e813 100644 --- a/domain-server/resources/web/wizard/index.shtml +++ b/domain-server/resources/web/wizard/index.shtml @@ -24,7 +24,7 @@
- Place names are similar to web addresses. Users who want to visit your domain can + Place names are similar to web addresses. Users who want to visit your domain can enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.
Your domain may also be reachable by IP address.
diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 09a0446468..ead4002334 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2015-08-24. // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -94,6 +95,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointergetBytesLeftToRead() > 0) { // read username from packet packetStream >> username; @@ -101,10 +105,25 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointergetBytesLeftToRead() > 0) { // read user signature from packet packetStream >> usernameSignature; + + if (message->getBytesLeftToRead() > 0) { + // Read domain username from packet. + packetStream >> domainUsername; + domainUsername = domainUsername.toLower(); // Domain usernames are case-insensitive; internally lower-case. + + if (message->getBytesLeftToRead() > 0) { + // Read domain tokens from packet. + + QString domainTokensString; + packetStream >> domainTokensString; + domainTokens = domainTokensString.split(":"); + } + } } } - node = processAgentConnectRequest(nodeConnection, username, usernameSignature); + node = processAgentConnectRequest(nodeConnection, username, usernameSignature, + domainUsername, domainTokens.value(0), domainTokens.value(1)); } if (node) { @@ -142,7 +161,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer_settingsManager.getDomainServerGroupNames() + .filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$", + QRegularExpression::CaseInsensitiveOption)); + foreach(QString domainGroup, domainGroups) { + userPerms |= _server->_settingsManager.getPermissionsForGroup(domainGroup, QUuid()); // No rank for domain groups. +#ifdef WANT_DEBUG + qDebug() << "| user-permissions: domain user " << verifiedDomainUserName << "is in group:" << domainGroup + << "so:" << userPerms; +#endif + } + } + } + if (verifiedUsername.isEmpty()) { userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous); #ifdef WANT_DEBUG @@ -256,6 +297,27 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin userPerms.setVerifiedUserName(verifiedUsername); } + // If this user is a known member of an domain group that is blacklisted, remove the implied permissions. + if (!verifiedDomainUserName.isEmpty()) { + auto userGroups = _domainGroupMemberships[verifiedDomainUserName]; + foreach(QString userGroup, userGroups) { + // A domain group is signified by a leading special character, "@". + // Multiple domain groups may be specified in one domain server setting as a comma- and/or space-separated lists of + // domain group names. For example, "@silver @Gold, @platinum". + auto domainGroups = _server->_settingsManager.getDomainServerBlacklistGroupNames() + .filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$", + QRegularExpression::CaseInsensitiveOption)); + foreach(QString domainGroup, domainGroups) { + userPerms &= ~_server->_settingsManager.getForbiddensForGroup(domainGroup, QUuid()); +#ifdef WANT_DEBUG + qDebug() << "| user-permissions: domain user is in blacklist group:" << domainGroup << "so:" << userPerms; +#endif + } + } + + userPerms.setVerifiedDomainUserName(verifiedDomainUserName); + } + #ifdef WANT_DEBUG qDebug() << "| user-permissions: final:" << userPerms; #endif @@ -275,6 +337,7 @@ void DomainGatekeeper::updateNodePermissions() { // the id and the username in NodePermissions will often be the same, but id is set before // authentication and verifiedUsername is only set once they user's key has been confirmed. QString verifiedUsername = node->getPermissions().getVerifiedUserName(); + QString verifiedDomainUserName = node->getPermissions().getVerifiedDomainUserName(); NodePermissions userPerms(NodePermissionsKey(verifiedUsername, 0)); if (node->getPermissions().isAssignment) { @@ -309,7 +372,8 @@ void DomainGatekeeper::updateNodePermissions() { sendingAddress == QHostAddress::LocalHost); } - userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint); + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUserName, + connectingAddr.getAddress(), hardwareAddress, machineFingerprint); } node->setPermissions(userPerms); @@ -387,12 +451,19 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo return newNode; } +const QString AUTHENTICATION_ENABLE_OAUTH2 = "authentication.enable_oauth2"; +const QString AUTHENTICATION_OAUTH2_URL_PATH = "authentication.oauth2_url_path"; +const QString AUTHENTICATION_WORDPRESS_URL_BASE = "authentication.wordpress_url_base"; +const QString AUTHENTICATION_PLUGIN_CLIENT_ID = "authentication.plugin_client_id"; const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; const QString MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION = "security.maximum_user_capacity_redirect_location"; SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection, const QString& username, - const QByteArray& usernameSignature) { + const QByteArray& usernameSignature, + const QString& domainUsername, + const QString& domainAccessToken, + const QString& domainRefreshToken) { auto limitedNodeList = DependencyManager::get(); @@ -419,7 +490,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect #ifdef WANT_DEBUG qDebug() << "stalling login because we have no username-signature:" << username; #endif - return SharedNodePointer(); + if (!domainHasLogin() || domainUsername.isEmpty()) { + return SharedNodePointer(); + } } else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) { // they sent us a username and the signature verifies it getGroupMemberships(username); @@ -430,16 +503,70 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect #ifdef WANT_DEBUG qDebug() << "stalling login because signature verification failed:" << username; #endif - return SharedNodePointer(); + if (!domainHasLogin() || domainUsername.isEmpty()) { + return SharedNodePointer(); + } } } - userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(), - nodeConnection.hardwareAddress, nodeConnection.machineFingerprint); + // The domain may have its own users and groups. + QString verifiedDomainUsername; + QStringList verifiedDomainUserGroups; + if (domainHasLogin() && !domainUsername.isEmpty()) { + + if (domainAccessToken.isEmpty()) { + // User is attempting to prove their domain identity. +#ifdef WANT_DEBUG + qDebug() << "Stalling login because we have no domain OAuth2 tokens:" << domainUsername; +#endif + return SharedNodePointer(); + + } else if (needToVerifyDomainUserIdentity(domainUsername, domainAccessToken, domainRefreshToken)) { + // User's domain identity needs to be confirmed. + requestDomainUser(domainUsername, domainAccessToken, domainRefreshToken); +#ifdef WANT_DEBUG + qDebug() << "Stalling login because we haven't authenticated user yet:" << domainUsername; +#endif + + } else if (verifyDomainUserIdentity(domainUsername, domainAccessToken, domainRefreshToken, + nodeConnection.senderSockAddr)) { + // User's domain identity is confirmed. + verifiedDomainUsername = domainUsername; + + } else { + // User's domain identity didn't check out. +#ifdef WANT_DEBUG + qDebug() << "Stalling login because domain user verification failed:" << domainUsername; +#endif + return SharedNodePointer(); + + } + } + + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUsername, + nodeConnection.senderSockAddr.getAddress(), nodeConnection.hardwareAddress, + nodeConnection.machineFingerprint); if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) { - sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.", - nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorized); + if (domainHasLogin()) { + QString domainAuthURL; + auto domainAuthURLVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_OAUTH2_URL_PATH); + if (domainAuthURLVariant.canConvert()) { + domainAuthURL = domainAuthURLVariant.toString(); + } + QString domainAuthClientID; + auto domainAuthClientIDVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID); + if (domainAuthClientIDVariant.canConvert()) { + domainAuthClientID = domainAuthClientIDVariant.toString(); + } + + sendConnectionDeniedPacket("You lack the required domain permissions to connect to this domain.", + nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedDomain, + domainAuthURL + "|" + domainAuthClientID); + } else { + sendConnectionDeniedPacket("You lack the required metaverse permissions to connect to this domain.", + nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedMetaverse); + } #ifdef WANT_DEBUG qDebug() << "stalling login due to permissions:" << username; #endif @@ -600,15 +727,15 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, return true; } else { - // we only send back a LoginError if this wasn't an "optimistic" key + // we only send back a LoginErrorMetaverse if this wasn't an "optimistic" key // (a key that we hoped would work but is probably stale) if (!senderSockAddr.isNull() && !isOptimisticKey) { - qDebug() << "Error decrypting username signature for" << username << "- denying connection."; + qDebug() << "Error decrypting metaverse username signature for" << username << "- denying connection."; sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr, - DomainHandler::ConnectionRefusedReason::LoginError); + DomainHandler::ConnectionRefusedReason::LoginErrorMetaverse); } else if (!senderSockAddr.isNull()) { - qDebug() << "Error decrypting username signature for" << username << "with optimisitic key -" + qDebug() << "Error decrypting metaverse username signature for" << username << "with optimistic key -" << "re-requesting public key and delaying connection"; } @@ -622,7 +749,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, if (!senderSockAddr.isNull()) { qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; sendConnectionDeniedPacket("Couldn't convert data to RSA key.", senderSockAddr, - DomainHandler::ConnectionRefusedReason::LoginError); + DomainHandler::ConnectionRefusedReason::LoginErrorMetaverse); } } } else { @@ -635,6 +762,25 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, return false; } + +bool DomainGatekeeper::needToVerifyDomainUserIdentity(const QString& username, const QString& accessToken, + const QString& refreshToken) { + return !_verifiedDomainUserIdentities.contains(username) + || _verifiedDomainUserIdentities.value(username) != QPair(accessToken, refreshToken); +} + +bool DomainGatekeeper::verifyDomainUserIdentity(const QString& username, const QString& accessToken, + const QString& refreshToken, const HifiSockAddr& senderSockAddr) { + if (_verifiedDomainUserIdentities.contains(username) + && _verifiedDomainUserIdentities.value(username) == QPair(accessToken, refreshToken)) { + return true; + } + + sendConnectionDeniedPacket("Error verifying domain user.", senderSockAddr, + DomainHandler::ConnectionRefusedReason::LoginErrorDomain); + return false; +} + bool DomainGatekeeper::isWithinMaxCapacity() { // find out what our maximum capacity is QVariant maximumUserCapacityVariant = @@ -907,7 +1053,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { AccountManagerAuth::Required, QNetworkAccessManager::PostOperation, callbackParams, QJsonDocument(json).toJson()); - } QString extractUsernameFromGroupMembershipsReply(QNetworkReply* requestReply) { @@ -962,6 +1107,7 @@ void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply* requestReply _inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply)); } + void DomainGatekeeper::getDomainOwnerFriendsList() { JSONCallbackParameters callbackParams; callbackParams.callbackReceiver = this; @@ -1010,6 +1156,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply* req qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply->error(); } +// ####### TODO: Domain equivalent or addition void DomainGatekeeper::refreshGroupsCache() { // if agents are connected to this domain, refresh our cached information about groups and memberships in such. getDomainOwnerFriendsList(); @@ -1029,7 +1176,7 @@ void DomainGatekeeper::refreshGroupsCache() { updateNodePermissions(); -#if WANT_DEBUG +#ifdef WANT_DEBUG _server->_settingsManager.debugDumpGroupsState(); #endif } @@ -1061,3 +1208,91 @@ Node::LocalID DomainGatekeeper::findOrCreateLocalID(const QUuid& uuid) { _localIDs.insert(newLocalID); return newLocalID; } + + +bool DomainGatekeeper::domainHasLogin() { + // The domain may have its own users and groups in a WordPress site. + return _server->_settingsManager.valueForKeyPath(AUTHENTICATION_ENABLE_OAUTH2).toBool() + && !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_OAUTH2_URL_PATH).toString().isEmpty() + && !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_WORDPRESS_URL_BASE).toString().isEmpty() + && !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID).toString().isEmpty(); +} + +void DomainGatekeeper::requestDomainUser(const QString& username, const QString& accessToken, const QString& refreshToken) { + + if (_inFlightDomainUserIdentityRequests.contains(username)) { + // Domain identify request for this username is already in progress. + return; + } + _inFlightDomainUserIdentityRequests.insert(username, QPair(accessToken, refreshToken)); + + if (_verifiedDomainUserIdentities.contains(username)) { + _verifiedDomainUserIdentities.remove(username); + } + + QString apiBase = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_WORDPRESS_URL_BASE).toString(); + if (!apiBase.endsWith("/")) { + apiBase += "/"; + } + + // Get data pertaining to "me", the user who generated the access token. + const QString WORDPRESS_USER_ROUTE = "wp/v2/users/me"; + const QString WORDPRESS_USER_QUERY = "_fields=username,roles"; + QUrl domainUserURL = apiBase + WORDPRESS_USER_ROUTE + (apiBase.contains("?") ? "&" : "?") + WORDPRESS_USER_QUERY; + + QNetworkRequest request; + + request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + request.setRawHeader(QByteArray("Authorization"), QString("Bearer " + accessToken).toUtf8()); + + QByteArray formData; // No data to send. + + request.setUrl(domainUserURL); + + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* requestReply = networkAccessManager.post(request, formData); + connect(requestReply, &QNetworkReply::finished, this, &DomainGatekeeper::requestDomainUserFinished); +} + +void DomainGatekeeper::requestDomainUserFinished() { + + QNetworkReply* requestReply = reinterpret_cast(sender()); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); + const QJsonObject& rootObject = jsonResponse.object(); + + auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (200 <= httpStatus && httpStatus < 300) { + + QString username = rootObject.value("username").toString().toLower(); + if (_inFlightDomainUserIdentityRequests.contains(username)) { + // Success! Verified user. + _verifiedDomainUserIdentities.insert(username, _inFlightDomainUserIdentityRequests.value(username)); + _inFlightDomainUserIdentityRequests.remove(username); + + // User user's WordPress roles as domain groups. + QStringList domainUserGroups; + auto userRoles = rootObject.value("roles").toArray(); + foreach (auto role, userRoles) { + // Distinguish domain groups from metaverse groups by adding a leading special character. + domainUserGroups.append(DOMAIN_GROUP_CHAR + role.toString().toLower()); + } + _domainGroupMemberships[username] = domainUserGroups; + + } else { + // Failure. + qDebug() << "Unexpected username in response for user details -" << username; + } + + } else { + // Failure. + qDebug() << "Error in response for user details -" << httpStatus << requestReply->error() + << "-" << rootObject["error"].toString() << rootObject["error_description"].toString(); + + _inFlightDomainUserIdentityRequests.clear(); + } +} diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 92b400882e..cb42baa7e3 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2015-08-24. // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -29,6 +30,8 @@ #include "NodeConnectionData.h" #include "PendingAssignedNodeData.h" +const QString DOMAIN_GROUP_CHAR = "@"; + class DomainServer; class DomainGatekeeper : public QObject { @@ -71,16 +74,28 @@ public slots: private slots: void handlePeerPingTimeout(); + + // Login and groups for domain, separate from metaverse. + void requestDomainUserFinished(); + private: SharedNodePointer processAssignmentConnectRequest(const NodeConnectionData& nodeConnection, const PendingAssignedNodeData& pendingAssignment); SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection, const QString& username, - const QByteArray& usernameSignature); + const QByteArray& usernameSignature, + const QString& domainUsername, + const QString& domainAccessToken, + const QString& domainRefreshToken); SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection); bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr); + + bool needToVerifyDomainUserIdentity(const QString& username, const QString& accessToken, const QString& refreshToken); + bool verifyDomainUserIdentity(const QString& username, const QString& accessToken, const QString& refreshToken, + const HifiSockAddr& senderSockAddr); + bool isWithinMaxCapacity(); bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, @@ -120,8 +135,9 @@ private: QSet _domainOwnerFriends; // keep track of friends of the domain owner QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for - NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress, - const QString& hardwareAddress, const QUuid& machineFingerprint); + NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, QString verifiedDomainUsername, + const QHostAddress& senderAddress, const QString& hardwareAddress, + const QUuid& machineFingerprint); void getGroupMemberships(const QString& username); // void getIsGroupMember(const QString& username, const QUuid groupID); @@ -133,9 +149,18 @@ private: using LocalIDs = std::unordered_set; LocalIDs _localIDs; UUIDToLocalID _uuidToLocalID; - Node::LocalID _currentLocalID; Node::LocalID _idIncrement; + + // Login and groups for domain, separate from metaverse. + bool domainHasLogin(); + void requestDomainUser(const QString& username, const QString& accessToken, const QString& refreshToken); + + typedef QHash> DomainUserIdentities; // > + DomainUserIdentities _inFlightDomainUserIdentityRequests; // Domain user identity requests currently in progress. + DomainUserIdentities _verifiedDomainUserIdentities; // Verified domain users. + + QHash _domainGroupMemberships; // }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index a9bc24c483..41b0e98ec5 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -67,16 +67,13 @@ 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 int MIN_PORT = 1; +const int MAX_PORT = 65535; + int const DomainServer::EXIT_CODE_REBOOT = 234923; -#if USE_STABLE_GLOBAL_SERVICES -const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; -#else -const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com"; -#endif - -QString DomainServer::_iceServerAddr { ICE_SERVER_DEFAULT_HOSTNAME }; +QString DomainServer::_iceServerAddr { NetworkingConstants::ICE_SERVER_DEFAULT_HOSTNAME }; int DomainServer::_iceServerPort { ICE_SERVER_DEFAULT_PORT }; bool DomainServer::_overrideDomainID { false }; QUuid DomainServer::_overridingDomainID; @@ -125,7 +122,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection, QUrl url{ MetaverseAPI::getCurrentMetaverseServerURL().toString() + metaversePath }; QNetworkRequest req(url); - req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); if (accessTokenVariant.isValid()) { @@ -229,7 +226,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : this, &DomainServer::updateDownstreamNodes); connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated, this, &DomainServer::updateUpstreamNodes); - setupGroupCacheRefresh(); optionallySetupOAuth(); @@ -330,6 +326,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _nodePingMonitorTimer = new QTimer{ this }; connect(_nodePingMonitorTimer, &QTimer::timeout, this, &DomainServer::nodePingMonitor); _nodePingMonitorTimer->start(NODE_PING_MONITOR_INTERVAL_MSECS); + + initializeExporter(); } void DomainServer::parseCommandLine(int argc, char* argv[]) { @@ -424,6 +422,11 @@ DomainServer::~DomainServer() { _contentManager->terminate(); } + if (_httpExporterManager) { + _httpExporterManager->close(); + delete _httpExporterManager; + } + DependencyManager::destroy(); // cleanup the AssetClient thread @@ -767,6 +770,7 @@ void DomainServer::setupNodeListAndAssignments() { packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket"); + packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacket"); // NodeList won't be available to the settings manager when it is created, so call registerListener here packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket"); @@ -1976,7 +1980,6 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_API_BACKUPS_ID = "/api/backups/"; const QString URI_API_BACKUPS_DOWNLOAD_ID = "/api/backups/download/"; const QString URI_API_BACKUPS_RECOVER = "/api/backups/recover/"; - const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; QPointer connectionPtr { connection }; @@ -2455,7 +2458,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url url.setQuery("access_token=" + accessTokenVariant.toString()); QNetworkRequest req(url); - req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply* reply = NetworkAccessManager::getInstance().put(req, doc.toJson()); @@ -2556,7 +2559,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u QNetworkRequest tokenRequest(tokenRequestUrl); tokenRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit()); @@ -2868,7 +2871,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR QNetworkRequest profileRequest(profileURL); profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - profileRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + profileRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); return NetworkAccessManager::getInstance().get(profileRequest); } @@ -3036,6 +3039,27 @@ void DomainServer::updateUpstreamNodes() { updateReplicationNodes(Upstream); } +void DomainServer::initializeExporter() +{ + static const QString ENABLE_EXPORTER = "monitoring.enable_prometheus_exporter"; + static const QString EXPORTER_PORT = "monitoring.prometheus_exporter_port"; + + bool isExporterEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_EXPORTER).toBool(); + int exporterPort = _settingsManager.valueOrDefaultValueForKeyPath(EXPORTER_PORT).toInt(); + + if (exporterPort < MIN_PORT || exporterPort > MAX_PORT) { + qCWarning(domain_server) << "Prometheus exporter port " << exporterPort << " is out of range."; + isExporterEnabled = false; + } + + qCDebug(domain_server) << "Setting up Prometheus exporter"; + + if (isExporterEnabled && !_httpExporterManager) { + qCInfo(domain_server) << "Starting Prometheus exporter on port " << exporterPort; + _httpExporterManager = new HTTPManager(QHostAddress::Any, (quint16)exporterPort, QString("%1/resources/prometheus_exporter/").arg(QCoreApplication::applicationDirPath()), &_exporter); + } +} + void DomainServer::updateReplicatedNodes() { // Make sure we have downstream nodes in our list static const QString REPLICATED_USERS_KEY = "users"; @@ -3614,3 +3638,81 @@ void DomainServer::handleOctreeFileReplacementRequest(QSharedPointerreadAll(), QString(), QString(), username); } } + +void DomainServer::processAvatarZonePresencePacket(QSharedPointer message) { + QUuid avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + QUuid zoneID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (avatarID.isNull()) { + qCWarning(domain_server) << "Ignoring null avatar presence"; + return; + } + static const int SCREENSHARE_EXPIRATION_SECONDS = 24 * 60 * 60; + screensharePresence(zoneID.isNull() ? "" : zoneID.toString(), avatarID, SCREENSHARE_EXPIRATION_SECONDS); +} + +void DomainServer::screensharePresence(QString roomname, QUuid avatarID, int expirationSeconds) { + if (!DependencyManager::get()->hasValidAccessToken()) { + static std::once_flag presenceAuthorityWarning; + std::call_once(presenceAuthorityWarning, [] { + qCDebug(domain_server) << "No authority to send screensharePresence."; + }); + return; + } + + auto limitedNodeList = DependencyManager::get(); + auto matchingNode = limitedNodeList->nodeWithUUID(avatarID); + if (!matchingNode) { + qCWarning(domain_server) << "Ignoring avatar presence for unknown avatar ID" << avatarID; + return; + } + QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName(); + if (verifiedUsername.isEmpty()) { // Silently bail for users who are not logged in. + return; + } + + JSONCallbackParameters callbackParams; + callbackParams.callbackReceiver = this; + callbackParams.jsonCallbackMethod = "handleSuccessfulScreensharePresence"; + callbackParams.errorCallbackMethod = "handleFailedScreensharePresence"; + // Construct `callbackData`, which is data that will be available to the callback functions. + // In this case, the "success" callback needs access to the "roomname" (the zone ID) and the + // relevant avatar's UUID. + QJsonObject callbackData; + callbackData.insert("roomname", roomname); + callbackData.insert("avatarID", avatarID.toString()); + callbackParams.callbackData = callbackData; + const QString PATH = "api/v1/domains/%1/screenshare"; + QString domain_id = uuidStringWithoutCurlyBraces(getID()); + QJsonObject json, screenshare; + screenshare["username"] = verifiedUsername; + screenshare["roomname"] = roomname; + if (expirationSeconds > 0) { + screenshare["expiration"] = expirationSeconds; + } + json["screenshare"] = screenshare; + DependencyManager::get()->sendRequest( + PATH.arg(domain_id), + AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, + callbackParams, QJsonDocument(json).toJson() + ); +} + +void DomainServer::handleSuccessfulScreensharePresence(QNetworkReply* requestReply, QJsonObject callbackData) { + QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object(); + if (jsonObject["status"].toString() != "success") { + qCWarning(domain_server) << "screensharePresence api call failed:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact); + return; + } + + // Tell the client that we just authorized to screenshare which zone ID in which they are authorized to screenshare. + auto nodeList = DependencyManager::get(); + auto packet = NLPacket::create(PacketType::AvatarZonePresence, NUM_BYTES_RFC4122_UUID, true); + packet->write(QUuid(callbackData["roomname"].toString()).toRfc4122()); + nodeList->sendPacket(std::move(packet), *(nodeList->nodeWithUUID(QUuid(callbackData["avatarID"].toString())))); +} + +void DomainServer::handleFailedScreensharePresence(QNetworkReply* requestReply) { + qCWarning(domain_server) << "screensharePresence api call failed:" << requestReply->error(); +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index c725688b67..7c0fa5fb15 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -36,6 +36,7 @@ #include "DomainContentBackupManager.h" #include "PendingAssignedNodeData.h" +#include "DomainServerExporter.h" #include @@ -78,6 +79,8 @@ public: bool isAssetServerEnabled(); + void screensharePresence(QString roomname, QUuid avatarID, int expiration_seconds = 0); + public slots: /// Called by NodeList to inform us a node has been added void nodeAdded(SharedNodePointer node); @@ -96,6 +99,7 @@ private slots: void processNodeDisconnectRequestPacket(QSharedPointer message); void processICEServerHeartbeatDenialPacket(QSharedPointer message); void processICEServerHeartbeatACK(QSharedPointer message); + void processAvatarZonePresencePacket(QSharedPointer packet); void handleDomainContentReplacementFromURLRequest(QSharedPointer message); void handleOctreeFileReplacementRequest(QSharedPointer message); @@ -112,7 +116,7 @@ private slots: void sendHeartbeatToIceServer(); void nodePingMonitor(); - void handleConnectedNode(SharedNodePointer newNode, quint64 requestReceiveTime); + void handleConnectedNode(SharedNodePointer newNode, quint64 requestReceiveTime); void handleTempDomainSuccess(QNetworkReply* requestReply); void handleTempDomainError(QNetworkReply* requestReply); @@ -129,9 +133,13 @@ private slots: void handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply); void handleFailedICEServerAddressUpdate(QNetworkReply* requestReply); + void handleSuccessfulScreensharePresence(QNetworkReply* requestReply, QJsonObject callbackData); + void handleFailedScreensharePresence(QNetworkReply* requestReply); + void updateReplicatedNodes(); void updateDownstreamNodes(); void updateUpstreamNodes(); + void initializeExporter(); void tokenGrantFinished(); void profileRequestFinished(); @@ -228,8 +236,10 @@ private: std::vector _replicatedUsernames; DomainGatekeeper _gatekeeper; + DomainServerExporter _exporter; HTTPManager _httpManager; + HTTPManager* _httpExporterManager { nullptr }; std::unique_ptr _httpsManager; QHash _allAssignments; diff --git a/domain-server/src/DomainServerExporter.cpp b/domain-server/src/DomainServerExporter.cpp new file mode 100644 index 0000000000..a7affaf4dd --- /dev/null +++ b/domain-server/src/DomainServerExporter.cpp @@ -0,0 +1,471 @@ +// +// DomainServerExporter.cpp +// domain-server/src +// +// Created by Dale Glass on 3 Apr 2020. +// Copyright 2020 Dale Glass +// +// Prometheus exporter +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// TODO: +// +// Look into the data provided by OctreeServer::handleHTTPRequest in assignment-client/src/octree/OctreeServer.cpp +// Turns out the octree server (entity server) can optionally deliver additional statistics via another HTTP server +// that is disabled by default. This functionality can be enabled by setting statusPort to a port number. +// +// Look into what appears in Audio Mixer -> z_listeners -> jitter -> injectors, so far it's been an empty list. + +#include +#include +#include +#include +#include +#include + +#include "DomainServerExporter.h" +#include "DependencyManager.h" +#include "LimitedNodeList.h" +#include "HTTPConnection.h" +#include "DomainServerNodeData.h" + +Q_LOGGING_CATEGORY(domain_server_exporter, "hifi.domain_server.prometheus_exporter") + +static const QMap TYPE_MAP { + { "asset_server_assignment_stats_num_queued_check_ins" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_cw_p" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_down_mb_s" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_est_max_p_s" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_last_heard_ago_msecs" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_last_heard_time_msecs" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_period_us" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_rtt_ms" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_connection_stats_up_mb_s" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_downstream_stats_duplicates" , DomainServerExporter::MetricType::Counter }, + { "asset_server_downstream_stats_recvd_p_s" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_downstream_stats_recvd_packets" , DomainServerExporter::MetricType::Counter }, + { "asset_server_downstream_stats_sent_ack" , DomainServerExporter::MetricType::Counter }, + { "asset_server_io_stats_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_io_stats_inbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_io_stats_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_io_stats_outbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_upstream_stats_procd_ack" , DomainServerExporter::MetricType::Counter }, + { "asset_server_upstream_stats_recvd_ack" , DomainServerExporter::MetricType::Counter }, + { "asset_server_upstream_stats_retransmitted" , DomainServerExporter::MetricType::Counter }, + { "asset_server_upstream_stats_sent_p_s" , DomainServerExporter::MetricType::Gauge }, + { "asset_server_upstream_stats_sent_packets" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_assignment_stats_num_queued_check_ins" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_listeners_per_frame" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_listeners_silent_per_frame" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_streams_per_frame" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_check_time" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_check_time_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_events" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_events_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_frame" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_frame_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_mix" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_mix_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_packets" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_packets_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_sleep" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_sleep_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_tic" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_avg_timing_stats_us_per_tic_trailing" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_io_stats_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_io_stats_inbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_io_stats_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_io_stats_outbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_available" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_available_avg_10s" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_avg_gap_30s_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_avg_gap_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_desired" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_lost_percent" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_lost_percent_30s" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_max_gap_30s_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_max_gap_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_min_gap_30s_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_min_gap_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_downstream_not_mixed" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_downstream_overflows" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_downstream_starves" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_downstream_unplayed" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_injectors" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_upstream_available" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_available_avg_10s" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_avg_gap_30s_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_avg_gap_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_desired_calc" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_lost_percent" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_lost_percent_30s" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_max_gap_30s_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_max_gap_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_mic_desired" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_min_gap_30s_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_min_gap_usecs" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_listeners_jitter_upstream_not_mixed" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_upstream_overflows" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_upstream_silents_dropped" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_upstream_starves" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_jitter_upstream_unplayed" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_listeners_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_active_streams" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_active_to_inactive" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_active_to_skippped" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_avg_mixes_per_block" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_hrtf_renders" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_hrtf_resets" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_hrtf_updates" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_inactive_streams" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_inactive_to_active" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_inactive_to_skippped" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_percent_hrtf_mixes" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_percent_manual_echo_mixes" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_percent_manual_stereo_mixes" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_mix_stats_skipped_streams" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_skippped_to_active" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_skippped_to_inactive" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_mix_stats_total_mixes" , DomainServerExporter::MetricType::Counter }, + { "audio_mixer_silent_packets_per_frame" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_threads" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_throttling_ratio" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_trailing_mix_ratio" , DomainServerExporter::MetricType::Gauge }, + { "audio_mixer_use_dynamic_jitter_buffers" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_assignment_stats_num_queued_check_ins" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_av_data_receive_rate" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_avg_other_av_skips_per_second" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_avg_other_av_starves_per_second" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_delta_full_vs_avatar_data_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_inbound_av_data_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_num_avs_sent_last_frame" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_outbound_av_data_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_outbound_av_traits_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_recent_other_av_in_view" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_recent_other_av_out_of_view" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_avatars_total_num_out_of_order_sends" , DomainServerExporter::MetricType::Counter }, + { "avatar_mixer_average_listeners_last_second" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_broadcast_loop_rate" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_io_stats_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_io_stats_inbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_io_stats_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_io_stats_outbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_parallel_tasks_broadcast_avatar_data_functor" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_parallel_tasks_broadcast_avatar_data_innner" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_parallel_tasks_broadcast_avatar_data_lock_wait" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_parallel_tasks_broadcast_avatar_data_node_transform" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_parallel_tasks_broadcast_avatar_data_total" , DomainServerExporter::MetricType::Counter }, + { "avatar_mixer_parallel_tasks_display_name_management_total" , DomainServerExporter::MetricType::Counter }, + { "avatar_mixer_parallel_tasks_process_queued_avatar_data_packets_lock_wait" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_parallel_tasks_process_queued_avatar_data_packets_total" , DomainServerExporter::MetricType::Counter }, + { "avatar_mixer_single_core_tasks_incoming_packets_handle_avatar_identity_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_incoming_packets_handle_avatar_query_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_incoming_packets_handle_kill_avatar_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_incoming_packets_handle_node_ignore_request_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_incoming_packets_handle_radius_ignore_request_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_incoming_packets_handle_requests_domain_list_data_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_process_events" , DomainServerExporter::MetricType::Counter }, + { "avatar_mixer_single_core_tasks_queue_incoming_packet" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_single_core_tasks_send_stats" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_received_1_nodes_processed" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_1_nodes_broadcasted_to" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_2_average_others_included" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_3_average_over_budget_avatars" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_4_average_data_bytes" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_5_average_traits_bytes" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_6_average_identity_bytes" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_sent_7_average_hero_avatars" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_timing_1_process_incoming_packets" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_timing_2_ignore_calculation" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_timing_3_to_byte_array" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_timing_4_avatar_data_packing" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_timing_5_packet_sending" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_slaves_aggregate_per_frame_timing_6_job_elapsed_time" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_threads" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_throttling_ratio" , DomainServerExporter::MetricType::Gauge }, + { "avatar_mixer_trailing_mix_ratio" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_assignment_stats_num_queued_check_ins" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_io_stats_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_io_stats_inbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_io_stats_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_io_stats_outbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_nodes_inbound_kbit_s" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_nodes_outbound_kbit_s" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_nodes_reliable_packet_s" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_nodes_unreliable_packet_s" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_octree_stats_element_count" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_octree_stats_internal_element_count" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_octree_stats_leaf_element_count" , DomainServerExporter::MetricType::Gauge }, + { "entity_script_server_script_engine_stats_number_running_scripts" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_assignment_stats_num_queued_check_ins" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_data_packet_queue" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_data_total_elements" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_data_total_packets" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_timing_avg_lock_wait_time_per_element" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_timing_avg_lock_wait_time_per_packet" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_timing_avg_process_time_per_element" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_timing_avg_process_time_per_packet" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_inbound_timing_avg_transit_time_per_packet" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_clients" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_persist_file_load_time_seconds" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_threads_handle_pacekt_send" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_threads_packet_distributor" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_threads_processing" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_threads_write_datagram" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_misc_uptime_seconds" , DomainServerExporter::MetricType::Counter }, + { "entity_server_entity_server_octree_element_count" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_octree_internal_element_count" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_octree_leaf_element_count" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_data_total_bytes" , DomainServerExporter::MetricType::Counter }, + { "entity_server_entity_server_outbound_data_total_bytes_bit_masks" , DomainServerExporter::MetricType::Counter }, + { "entity_server_entity_server_outbound_data_total_bytes_octal_codes" , DomainServerExporter::MetricType::Counter }, + { "entity_server_entity_server_outbound_data_total_bytes_wasted" , DomainServerExporter::MetricType::Counter }, + { "entity_server_entity_server_outbound_data_total_packets" , DomainServerExporter::MetricType::Counter }, + { "entity_server_entity_server_outbound_timing_avg_compress_and_write_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_timing_avg_encode_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_timing_avg_inside_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_timing_avg_loop_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_timing_avg_send_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_timing_avg_tree_traverse_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_entity_server_outbound_timing_node_wait_time" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_io_stats_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_io_stats_inbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_io_stats_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "entity_server_io_stats_outbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_assignment_stats_num_queued_check_ins" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_io_stats_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_io_stats_inbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_io_stats_outbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_io_stats_outbound_pps" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_messages_inbound_kbps" , DomainServerExporter::MetricType::Gauge }, + { "messages_mixer_messages_outbound_kbps" , DomainServerExporter::MetricType::Gauge } +}; + + +// Things we're not going to convert for various reasons, such as containing text, +// or having a value followed by an unit ("5.2 seconds"). +// +// Things like text like usernames have no place in the Prometheus model, so they can be skipped. +// +// For numeric values with an unit, instead of trying to parse it, the stats will just need to +// have a second copy of the metric added, with the value expressed as a number, with the original +// being blacklisted here. + +static const QSet BLACKLIST = { + "asset_server_connection_stats_last_heard", // Timestamp as a string + "asset_server_username", // Username + "audio_mixer_listeners_jitter_downstream_avg_gap", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_downstream_avg_gap_30s", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_downstream_max_gap", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_downstream_max_gap_30s", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_downstream_min_gap", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_downstream_min_gap_30s", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_injectors", // Array, empty. TODO: check if this ever contains anything. + "audio_mixer_listeners_jitter_upstream", // Only exists in the absence of a connection + "audio_mixer_listeners_jitter_upstream_avg_gap", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_upstream_avg_gap_30s", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_upstream_max_gap", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_upstream_max_gap_30s", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_upstream_min_gap", // Number as string with unit name, alternative added + "audio_mixer_listeners_jitter_upstream_min_gap_30s", // Number as string with unit name, alternative added + "audio_mixer_listeners_username", // Username + "avatar_mixer_avatars_display_name", // Username + "avatar_mixer_avatars_username", // Username + "entity_script_server_nodes_node_type", // Username + "entity_script_server_nodes_username", // Username + "entity_server_entity_server_misc_configuration", // Text + "entity_server_entity_server_misc_detailed_stats_url", // URL + "entity_server_entity_server_misc_persist_file_load_time", // Number as string with unit name, alternative added + "entity_server_entity_server_misc_uptime", // Number as string with unit name, alternative added + "messages_mixer_messages_username" // Username +}; + +DomainServerExporter::DomainServerExporter() { +} + +bool DomainServerExporter::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { + const QString URI_METRICS = "/metrics"; + const QString EXPORTER_MIME_TYPE = "text/plain"; + + qCDebug(domain_server_exporter) << "Request on URL " << url; + + if (url.path() == URI_METRICS) { + auto nodeList = DependencyManager::get(); + QString output = ""; + QTextStream outStream(&output); + + nodeList->eachNode([this, &outStream](const SharedNodePointer& node) { generateMetricsForNode(outStream, node); }); + + connection->respond(HTTPConnection::StatusCode200, output.toUtf8(), qPrintable(EXPORTER_MIME_TYPE)); + return true; + } + + return false; +} + +QString DomainServerExporter::escapeName(const QString& name) { + QRegularExpression invalidCharacters("[^A-Za-z0-9_]"); + + QString result = name; + + // If a key is named something like: "6. threads", turn it into just "threads" + result.replace(QRegularExpression("^\\d+\\. "), ""); + result.replace(QRegularExpression("^\\d+_"), ""); + + // If a key is named something like "z_listeners", turn it into just "listeners" + result.replace(QRegularExpression("^z_"), ""); + + // If a key is named something like "lost%", change it to "lost_percent_". + // redundant underscores will be removed below. + result.replace(QRegularExpression("%"), "_percent_"); + + // change mixedCaseNames to mixed_case_names + result.replace(QRegularExpression("([a-z])([A-Z])"), "\\1_\\2"); + + // Replace all invalid characters with a _ + result.replace(invalidCharacters, "_"); + + // Remove any "_" characters at the beginning or end + result.replace(QRegularExpression("^_+"), ""); + result.replace(QRegularExpression("_+$"), ""); + + // Replace any duplicated _ characters with a single one + result.replace(QRegularExpression("_+"), "_"); + + result = result.toLower(); + + return result; +} + +void DomainServerExporter::generateMetricsForNode(QTextStream& stream, const SharedNodePointer& node) { + QJsonObject statsObject = static_cast(node->getLinkedData())->getStatsJSONObject(); + QString nodeType = NodeType::getNodeTypeName(static_cast(node->getType())); + + stream << "\n\n\n"; + stream << "###############################################################\n"; + stream << "# " << nodeType << "\n"; + stream << "###############################################################\n"; + + generateMetricsFromJson(stream, nodeType, escapeName(nodeType), QHash(), statsObject); +} + +void DomainServerExporter::generateMetricsFromJson(QTextStream& stream, + QString originalPath, + QString path, + QHash labels, + const QJsonObject& root) { + for (auto iter = root.constBegin(); iter != root.constEnd(); ++iter) { + auto escapedKey = escapeName(iter.key()); + auto metricValue = iter.value(); + auto metricName = path + "_" + escapedKey; + auto origMetricName = originalPath + " -> " + iter.key(); + + if (metricValue.isObject()) { + QUuid possible_uuid = QUuid::fromString(iter.key()); + + if (possible_uuid.isNull()) { + generateMetricsFromJson(stream, originalPath + " -> " + iter.key(), path + "_" + escapedKey, labels, + iter.value().toObject()); + } else { + labels.insert("uuid", possible_uuid.toString(QUuid::WithoutBraces)); + generateMetricsFromJson(stream, originalPath, path, labels, iter.value().toObject()); + } + + continue; + } + + if (BLACKLIST.contains(metricName)) { + continue; + } + + bool conversionOk = false; + double converted = 0; + + if (metricValue.isString()) { + // Prometheus only deals with numeric values. See if this string contains a valid one + + QString tmp = metricValue.toString(); + converted = tmp.toDouble(&conversionOk); + + if (!conversionOk) { + qCWarning(domain_server_exporter) << "Failed to convert value of " << origMetricName << " (" << metricName + << ") to double: " << tmp << "'"; + continue; + } + } + + stream << QString("\n# HELP %1 %2 -> %3\n").arg(metricName).arg(originalPath).arg(iter.key()); + + if (TYPE_MAP.contains(metricName)) { + stream << "# TYPE " << metricName << " "; + switch (TYPE_MAP[metricName]) { + case DomainServerExporter::MetricType::Untyped: + stream << "untyped"; + break; + case DomainServerExporter::MetricType::Counter: + stream << "counter"; + break; + case DomainServerExporter::MetricType::Gauge: + stream << "gauge"; + break; + case DomainServerExporter::MetricType::Histogram: + stream << "histogram"; + break; + case DomainServerExporter::MetricType::Summary: + stream << "summary"; + break; + } + stream << "\n"; + } else { + qCWarning(domain_server_exporter) + << "Type for metric " << origMetricName << " (" << metricName << ") not known."; + } + + stream << path << "_" << escapedKey; + if (!labels.isEmpty()) { + stream << "{"; + + bool isFirst = true; + QHashIterator iter(labels); + + while (iter.hasNext()) { + iter.next(); + + if (!isFirst) { + stream << ","; + } + + QString escapedValue = iter.value(); + escapedValue.replace("\\", "\\\\"); + escapedValue.replace("\"", "\\\""); + escapedValue.replace("\n", "\\\n"); + + stream << iter.key() << "=\"" << escapedValue << "\""; + + isFirst = false; + } + stream << "}"; + } + + stream << " "; + + if (metricValue.isBool()) { + stream << (iter.value().toBool() ? "1" : "0"); + } else if (metricValue.isDouble()) { + stream << metricValue.toDouble(); + } else if (metricValue.isString()) { + // Converted above + stream << converted; + } else { + qCWarning(domain_server_exporter) + << "Can't convert metric " << origMetricName << "(" << metricName << ") with value " << metricValue; + } + + stream << "\n"; + } +} diff --git a/domain-server/src/DomainServerExporter.h b/domain-server/src/DomainServerExporter.h new file mode 100644 index 0000000000..500532621a --- /dev/null +++ b/domain-server/src/DomainServerExporter.h @@ -0,0 +1,55 @@ +// +// DomainServerExporter.h +// domain-server/src +// +// Created by Dale Glass on 3 Apr 2020. +// Copyright 2020 Dale Glass +// +// Prometheus exporter +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef DOMAINSERVEREXPORTER_H +#define DOMAINSERVEREXPORTER_H + +#include +#include "HTTPManager.h" +#include "Node.h" +#include +#include +#include +#include + + + +/** + * @brief Prometheus exporter for domain stats + * + * This class exportors the statistics that can be seen on the domain's page in + * a format that can be parsed by Prometheus. This is useful for troubleshooting, + * monitoring performance, and making pretty graphs. + */ +class DomainServerExporter : public HTTPRequestHandler +{ +public: + typedef enum { + Untyped, /* Works the same as Gauge, with the difference of signalling that the actual type is unknown */ + Counter, /* Value only goes up. Eg, number of packets received */ + Gauge, /* Current numerical value that can go up or down. Current temperature, memory usage, etc */ + Histogram, /* Samples sorted in buckets gathered over time */ + Summary + } MetricType; + + DomainServerExporter(); + ~DomainServerExporter() = default; + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; + +private: + QString escapeName(const QString &name); + void generateMetricsForNode(QTextStream& stream, const SharedNodePointer& node); + void generateMetricsFromJson(QTextStream& stream, QString originalPath, QString path, QHash labels, const QJsonObject& obj); +}; + +#endif // DOMAINSERVEREXPORTER_H diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 73d78a5c70..95bf4adc65 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -1966,6 +1966,10 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() { QStringList groupNames = getAllKnownGroupNames(); foreach (QString groupName, groupNames) { QString lowerGroupName = groupName.toLower(); + if (lowerGroupName.startsWith(DOMAIN_GROUP_CHAR)) { + // Ignore domain groups. (Assumption: metaverse group names can't start with a "@".) + return; + } if (_groupIDs.contains(lowerGroupName)) { // we already know about this one. recall setGroupID in case the group has been // added to another section (the same group is found in both groups and blacklists). @@ -2185,6 +2189,24 @@ QList DomainServerSettingsManager::getBlacklistGroupIDs() { return result.toList(); } +QStringList DomainServerSettingsManager::getDomainServerGroupNames() { + // All names as listed in the domain server settings; both metaverse groups and domain groups + QSet result; + foreach(NodePermissionsKey groupKey, _groupPermissions.keys()) { + result += _groupPermissions[groupKey]->getID(); + } + return result.toList(); +} + +QStringList DomainServerSettingsManager::getDomainServerBlacklistGroupNames() { + // All names as listed in the domain server settings; not necessarily mnetaverse groups. + QSet result; + foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) { + result += _groupForbiddens[groupKey]->getID(); + } + return result.toList(); +} + void DomainServerSettingsManager::debugDumpGroupsState() { qDebug() << "--------- GROUPS ---------"; diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index e28b9f6cd1..294e441ba4 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -19,11 +19,11 @@ #include #include - -#include -#include "NodePermissions.h" - #include +#include + +#include "DomainGatekeeper.h" +#include "NodePermissions.h" const QString SETTINGS_PATHS_KEY = "paths"; @@ -105,6 +105,9 @@ public: QList getGroupIDs(); QList getBlacklistGroupIDs(); + QStringList getDomainServerGroupNames(); + QStringList getDomainServerBlacklistGroupNames(); + // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); } void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID); diff --git a/hifi_android.py b/hifi_android.py index 0c2ea07cc7..a8fec01035 100644 --- a/hifi_android.py +++ b/hifi_android.py @@ -10,87 +10,74 @@ import zipfile print = functools.partial(print, flush=True) -ANDROID_PACKAGE_URL = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' +ANDROID_PACKAGE_URL = 'https://content.vircadia.com/eu-c-1/vircadia-public/dependencies/android/' ANDROID_PACKAGES = { 'qt' : { 'file': 'qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz', - 'versionId': '3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN', 'checksum': 'aa449d4bfa963f3bc9a9dfe558ba29df', }, 'bullet': { 'file': 'bullet-2.88_armv8-libcpp.tgz', - 'versionId': 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg', 'checksum': '81642779ccb110f8c7338e8739ac38a0', }, 'draco': { 'file': 'draco_armv8-libcpp.tgz', - 'versionId': '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8', 'checksum': '617a80d213a5ec69fbfa21a1f2f738cd', }, 'glad': { 'file': 'glad_armv8-libcpp.zip', - 'versionId': 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY', 'checksum': 'a8ee8584cf1ccd34766c7ddd9d5e5449', }, 'gvr': { 'file': 'gvrsdk_v1.101.0.tgz', - 'versionId': 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r', 'checksum': '57fd02baa069176ba18597a29b6b4fc7', }, 'nvtt': { 'file': 'nvtt_armv8-libcpp.zip', - 'versionId': 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO', 'checksum': 'eb46d0b683e66987190ed124aabf8910', 'sharedLibFolder': 'lib', 'includeLibs': ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'] }, 'oculus_1.22': { 'file': 'ovr_sdk_mobile_1.22.zip', - 'versionId': 'InhomR5gwkzyiLAelH3X9k4nvV3iIpA_', 'checksum': '1ac3c5b0521e5406f287f351015daff8', 'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release', 'includeLibs': ['libvrapi.so'] }, 'oculusPlatform': { 'file': 'OVRPlatformSDK_v1.34.0.zip', - 'versionId': 'vbRUkkyzUAXfTGSEtuiUr_7.Fm5h5BZk', 'checksum': '16e4c5f39520f122bc49cb6d5bb88289', 'sharedLibFolder': 'Android/libs/arm64-v8a', 'includeLibs': ['libovrplatformloader.so'] }, 'openssl': { 'file': 'openssl-1.1.0g_armv8.tgz', - 'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW', 'checksum': 'cabb681fbccd79594f65fcc266e02f32' }, 'polyvox': { 'file': 'polyvox_armv8-libcpp.tgz', - 'versionId': 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92', 'checksum': 'dba88b3a098747af4bb169e9eb9af57e', 'sharedLibFolder': 'lib', 'includeLibs': ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], }, 'tbb': { 'file': 'tbb-2018_U1_armv8_libcpp.tgz', - 'versionId': 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB', 'checksum': '20768f298f53b195e71b414b0ae240c4', 'sharedLibFolder': 'lib/release', 'includeLibs': ['libtbb.so', 'libtbbmalloc.so'], }, 'hifiAC': { - 'baseUrl': 'http://s3.amazonaws.com/hifi-public/dependencies/', + 'baseUrl': 'https://content.vircadia.com/eu-c-1/vircadia-public/dependencies/', 'file': 'codecSDK-android_armv8-2.0.zip', 'checksum': '1cbef929675818fc64c4101b72f84a6a' }, 'etc2comp': { 'file': 'etc2comp-patched-armv8-libcpp.tgz', - 'versionId': 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU', 'checksum': '14b02795d774457a33bbc60e00a786bc' }, 'breakpad': { 'file': 'breakpad.tgz', - 'versionId': '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI', 'checksum': 'ddcb23df336b08017042ba4786db1d9e', 'sharedLibFolder': 'lib', 'includeLibs': {'libbreakpad_client.a'} @@ -105,14 +92,12 @@ ANDROID_PLATFORM_PACKAGES = { 'Darwin' : { 'qt': { 'file': 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz', - 'versionId': 'OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup', 'checksum': 'c83cc477c08a892e00c71764dca051a0' }, }, 'Windows' : { 'qt': { 'file': 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz', - 'versionId': 'JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT', 'checksum': '0582191cc55431aa4f660848a542883e' }, } diff --git a/hifi_qt.py b/hifi_qt.py new file mode 100644 index 0000000000..10708e4bc9 --- /dev/null +++ b/hifi_qt.py @@ -0,0 +1,105 @@ +import hifi_utils +import hifi_android +import hashlib +import os +import platform +import re +import shutil +import tempfile +import json +import xml.etree.ElementTree as ET +import functools + +print = functools.partial(print, flush=True) + +# Encapsulates the vcpkg system +class QtDownloader: + CMAKE_TEMPLATE = """ +# this file auto-generated by hifi_qt.py +get_filename_component(QT_CMAKE_PREFIX_PATH "{}" ABSOLUTE CACHE) +get_filename_component(QT_CMAKE_PREFIX_PATH_UNCACHED "{}" ABSOLUTE) + +# If the cached cmake toolchain path is different from the computed one, exit +if(NOT (QT_CMAKE_PREFIX_PATH_UNCACHED STREQUAL QT_CMAKE_PREFIX_PATH)) + message(FATAL_ERROR "QT_CMAKE_PREFIX_PATH has changed, please wipe the build directory and rerun cmake") +endif() +""" + def __init__(self, args): + self.args = args + self.configFilePath = os.path.join(args.build_root, 'qt.cmake') + self.version = os.getenv('VIRCADIA_USE_QT_VERSION', '5.12.3') + + self.assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS') + + defaultBasePath = os.path.expanduser('~/hifi/qt') + self.basePath = os.getenv('HIFI_QT_BASE', defaultBasePath) + if (not os.path.isdir(self.basePath)): + os.makedirs(self.basePath) + self.path = os.path.join(self.basePath, self.version) + self.fullPath = os.path.join(self.path, 'qt5-install') + self.cmakePath = os.path.join(self.fullPath, 'lib/cmake') + + print("Using qt path {}".format(self.path)) + lockDir, lockName = os.path.split(self.path) + lockName += '.lock' + if not os.path.isdir(lockDir): + os.makedirs(lockDir) + + self.lockFile = os.path.join(lockDir, lockName) + + if (os.getenv('VIRCADIA_USE_PREBUILT_QT')): + print("Using pre-built Qt5") + return + + # OS dependent information + system = platform.system() + + if 'Windows' == system: + self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-windows3.tar.gz%3FversionId=5ADqP0M0j5ZfimUHrx4zJld6vYceHEsI' + elif 'Darwin' == system: + self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-macos.tar.gz%3FversionId=bLAgnoJ8IMKpqv8NFDcAu8hsyQy3Rwwz' + elif 'Linux' == system: + import distro + dist = distro.linux_distribution() + + if distro.id() == 'ubuntu': + u_major = int( distro.major_version() ) + u_minor = int( distro.minor_version() ) + + if u_major == 16: + self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-16.04-with-symbols.tar.gz' + elif u_major == 18: + self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-18.04.tar.gz' + elif u_major == 19 and u_minor == 10: + self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.6-ubuntu-19.10.tar.xz' + elif u_major > 18 and ( u_major != 19 and u_minor != 4): + print("We don't support " + distro.name(pretty=True) + " yet. Perhaps consider helping us out?") + raise Exception('LINUX DISTRO IS NOT SUPPORTED YET!!!') + else: + print("Sorry, " + distro.name(pretty=True) + " is old and won't be officially supported. Please consider upgrading."); + raise Exception('UNKNOWN LINUX DISTRO VERSION!!!') + else: + print("Sorry, " + distro.name(pretty=True) + " is not supported. Please consider helping us out.") + print("It's also possible to build Qt for your distribution, please see the documentation at:") + print("https://github.com/kasenvr/project-athena/tree/kasen/core/tools/qt-builder") + raise Exception('UNKNOWN LINUX VERSION!!!') + else: + print("System : " + platform.system()) + print("Architecture: " + platform.architecture()) + print("Machine : " + platform.machine()) + raise Exception('UNKNOWN OPERATING SYSTEM!!!') + + def writeConfig(self): + print("Writing cmake config to {}".format(self.configFilePath)) + # Write out the configuration for use by CMake + cmakeConfig = QtDownloader.CMAKE_TEMPLATE.format(self.cmakePath, self.cmakePath).replace('\\', '/') + with open(self.configFilePath, 'w') as f: + f.write(cmakeConfig) + + def installQt(self): + if not os.path.isdir(self.fullPath): + print ('Downloading Qt from AWS') + print('Extracting ' + self.qtUrl + ' to ' + self.path) + hifi_utils.downloadAndExtract(self.qtUrl, self.path) + else: + print ('Qt has already been downloaded') diff --git a/hifi_utils.py b/hifi_utils.py index d117011c99..157e5858a8 100644 --- a/hifi_utils.py +++ b/hifi_utils.py @@ -113,7 +113,7 @@ def downloadFile(url, hash=None, hasher=hashlib.sha512(), retries=3): def downloadAndExtract(url, destPath, hash=None, hasher=hashlib.sha512(), isZip=False): tempFileName = downloadFile(url, hash, hasher) - if isZip: + if isZip or ".zip" in url: with zipfile.ZipFile(tempFileName) as zip: zip.extractall(destPath) else: @@ -121,3 +121,7 @@ def downloadAndExtract(url, destPath, hash=None, hasher=hashlib.sha512(), isZip= with tarfile.open(tempFileName, 'r:*') as tgz: tgz.extractall(destPath) os.remove(tempFileName) + +def readEnviromentVariableFromFile(buildRootDir, var): + with open(os.path.join(buildRootDir, '_env', var + ".txt")) as fp: + return fp.read() diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py index e6ffeb8dbb..1b9976da6e 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -9,7 +9,6 @@ import tempfile import json import xml.etree.ElementTree as ET import functools -import distro from os import path print = functools.partial(print, flush=True) @@ -22,7 +21,7 @@ get_filename_component(CMAKE_TOOLCHAIN_FILE "{}" ABSOLUTE CACHE) get_filename_component(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}" ABSOLUTE) set(VCPKG_INSTALL_ROOT "{}") set(VCPKG_TOOLS_DIR "{}") -set(VCPKG_QT_CMAKE_PREFIX_PATH "{}") +set(VCPKG_TARGET_TRIPLET "{}") """ CMAKE_TEMPLATE_NON_ANDROID = """ @@ -36,23 +35,32 @@ endif() self.args = args # our custom ports, relative to the script location self.sourcePortsPath = args.ports_path - self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] + self.vcpkgBuildType = args.vcpkg_build_type + if (self.vcpkgBuildType): + self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] + "-" + self.vcpkgBuildType + else: + self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] self.configFilePath = os.path.join(args.build_root, 'vcpkg.cmake') self.assets_url = self.readVar('EXTERNAL_BUILD_ASSETS') + # The noClean flag indicates we're doing weird dependency maintenance stuff + # i.e. we've got an explicit checkout of vcpkg and we don't want the script to + # do stuff it might otherwise do. It typically indicates that we're using our + # own git checkout of vcpkg and manually managing it + self.noClean = False + # OS dependent information system = platform.system() - if self.args.vcpkg_root is not None: + if 'HIFI_VCPKG_PATH' in os.environ: + self.path = os.environ['HIFI_VCPKG_PATH'] + self.noClean = True + elif self.args.vcpkg_root is not None: self.path = args.vcpkg_root + self.noClean = True else: - if 'Darwin' == system: - defaultBasePath = os.path.expanduser('~/hifi/vcpkg') - else: - defaultBasePath = os.path.join(tempfile.gettempdir(), 'hifi', 'vcpkg') + defaultBasePath = os.path.expanduser('~/hifi/vcpkg') self.basePath = os.getenv('HIFI_VCPKG_BASE', defaultBasePath) - if self.basePath == defaultBasePath: - print("Warning: Environment variable HIFI_VCPKG_BASE not set, using {}".format(defaultBasePath)) if self.args.android: self.basePath = os.path.join(self.basePath, 'android') if (not os.path.isdir(self.basePath)): @@ -67,27 +75,40 @@ endif() self.lockFile = os.path.join(lockDir, lockName) self.tagFile = os.path.join(self.path, '.id') + self.prebuildTagFile = os.path.join(self.path, '.prebuild') # A format version attached to the tag file... increment when you want to force the build systems to rebuild # without the contents of the ports changing self.version = 1 self.tagContents = "{}_{}".format(self.id, self.version) + self.bootstrapEnv = os.environ.copy() + self.buildEnv = os.environ.copy() + self.prebuiltArchive = None + usePrebuilt = False + # usePrebuild Disabled, to re-enabled using the prebuilt archives for GitHub action builds uncomment the following line: + # usePrebuilt = ('CI_BUILD' in os.environ) and os.environ["CI_BUILD"] == "Github" and (not self.noClean) if 'Windows' == system: self.exe = os.path.join(self.path, 'vcpkg.exe') - self.bootstrapCmd = 'bootstrap-vcpkg.bat' - self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-win32.tar.gz%3FversionId=YZYkDejDRk7L_hrK_WVFthWvisAhbDzZ' - self.vcpkgHash = '3e0ff829a74956491d57666109b3e6b5ce4ed0735c24093884317102387b2cb1b2cd1ff38af9ed9173501f6e32ffa05cc6fe6d470b77a71ca1ffc3e0aa46ab9e' + 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.hostTriplet = 'x64-windows' + if usePrebuilt: + self.prebuiltArchive = self.assets_url + "/dependencies/vcpkg/builds/vcpkg-win32.zip%3FversionId=3SF3mDC8dkQH1JP041m88xnYmWNzZflx" elif 'Darwin' == system: self.exe = os.path.join(self.path, 'vcpkg') - self.bootstrapCmd = 'bootstrap-vcpkg.sh' - self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-osx.tar.gz%3FversionId=_fhqSxjfrtDJBvEsQ8L_ODcdUjlpX9cc' + self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '--allowAppleClang', '-disableMetrics' ] + self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/builds/vcpkg-osx-client.tgz%3FversionId=j0b4azo_zTlH_Q9DElEWOz1UMYZ2nqQw' self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d' self.hostTriplet = 'x64-osx' + # Potential fix for a vcpkg build issue on OSX (see https://github.com/microsoft/vcpkg/issues/9029) + self.bootstrapEnv['CXXFLAGS'] = '-D_CTERMID_H_' + if usePrebuilt: + self.prebuiltArchive = self.assets_url + "/dependencies/vcpkg/builds/vcpkg-osx.tgz%3FversionId=6JrIMTdvpBF3MAsjA92BMkO79Psjzs6Z" else: self.exe = os.path.join(self.path, 'vcpkg') - self.bootstrapCmd = 'bootstrap-vcpkg.sh' - self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-linux.tar.gz%3FversionId=97Nazh24etEVKWz33XwgLY0bvxEfZgMU' + self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '-disableMetrics' ] + self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/builds/vcpkg-linux-client.tgz%3FversionId=y7mct0gFicEXz5hJy3KROBugcLR56YWf' self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d' self.hostTriplet = 'x64-linux' @@ -101,9 +122,13 @@ endif() with open(os.path.join(self.args.build_root, '_env', var + ".txt")) as fp: return fp.read() + def writeVar(self, var, value): + with open(os.path.join(self.args.build_root, '_env', var + ".txt"), 'w') as fp: + fp.write(value) + def upToDate(self): # Prevent doing a clean if we've explcitly set a directory for vcpkg - if self.args.vcpkg_root is not None: + if self.noClean: return True if self.args.force_build: @@ -145,8 +170,10 @@ endif() self.copyEnv() return - self.clean() + if self.prebuiltArchive is not None: + return + self.clean() downloadVcpkg = False if self.args.force_bootstrap: print("Forcing bootstrap") @@ -167,10 +194,10 @@ endif() print("Cloning vcpkg from github to {}".format(self.path)) hifi_utils.executeSubprocess(['git', 'clone', 'https://github.com/microsoft/vcpkg', self.path]) print("Bootstrapping vcpkg") - hifi_utils.executeSubprocess([self.bootstrapCmd], folder=self.path) + hifi_utils.executeSubprocess(self.bootstrapCmds, folder=self.path, env=self.bootstrapEnv) else: print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path)) - hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash) + hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path) print("Replacing port files") portsPath = os.path.join(self.path, 'ports') @@ -186,9 +213,32 @@ endif() actualCommands.extend(commands) print("Running command") print(actualCommands) - hifi_utils.executeSubprocess(actualCommands, folder=self.path) + hifi_utils.executeSubprocess(actualCommands, folder=self.path, env=self.buildEnv) + + def copyTripletForBuildType(self, triplet): + print('Copying triplet ' + triplet + ' to have build type ' + self.vcpkgBuildType) + tripletPath = os.path.join(self.path, 'triplets', triplet + '.cmake') + tripletForBuildTypePath = os.path.join(self.path, 'triplets', self.getTripletWithBuildType(triplet) + '.cmake') + shutil.copy(tripletPath, tripletForBuildTypePath) + with open(tripletForBuildTypePath, "a") as tripletForBuildTypeFile: + tripletForBuildTypeFile.write("set(VCPKG_BUILD_TYPE " + self.vcpkgBuildType + ")\n") + + def getTripletWithBuildType(self, triplet): + if (not self.vcpkgBuildType): + return triplet + return triplet + '-' + self.vcpkgBuildType + + def setupDependencies(self, qt=None): + if self.prebuiltArchive: + if not os.path.isfile(self.prebuildTagFile): + print('Extracting ' + self.prebuiltArchive + ' to ' + self.path) + hifi_utils.downloadAndExtract(self.prebuiltArchive, self.path) + self.writePrebuildTag() + return + + if qt is not None: + self.buildEnv['QT_CMAKE_PREFIX_PATH'] = qt - def setupDependencies(self): # Special case for android, grab a bunch of binaries # FIXME remove special casing for android builds eventually if self.args.android: @@ -196,25 +246,32 @@ endif() self.setupAndroidDependencies() print("Installing host tools") - self.run(['install', '--triplet', self.hostTriplet, 'hifi-host-tools']) + if (self.vcpkgBuildType): + self.copyTripletForBuildType(self.hostTriplet) + self.run(['install', '--triplet', self.getTripletWithBuildType(self.hostTriplet), 'hifi-host-tools']) # If not android, install the hifi-client-deps libraries if not self.args.android: print("Installing build dependencies") - self.run(['install', '--triplet', self.triplet, 'hifi-client-deps']) - - # If not android, install our Qt build - if not self.args.android: - print("Installing Qt") - self.installQt() + if (self.vcpkgBuildType): + self.copyTripletForBuildType(self.triplet) + self.run(['install', '--triplet', self.getTripletWithBuildType(self.triplet), 'hifi-client-deps']) def cleanBuilds(self): + if self.noClean: + return # Remove temporary build artifacts builddir = os.path.join(self.path, 'buildtrees') if os.path.isdir(builddir): print("Wiping build trees") shutil.rmtree(builddir, ignore_errors=True) + # Removes large files used to build the vcpkg, for CI purposes. + def cleanupDevelopmentFiles(self): + shutil.rmtree(os.path.join(self.path, "downloads"), ignore_errors=True) + shutil.rmtree(os.path.join(self.path, "packages"), ignore_errors=True) + + def setupAndroidDependencies(self): # vcpkg prebuilt if not os.path.isdir(os.path.join(self.path, 'installed', 'arm64-android')): @@ -238,23 +295,45 @@ endif() hifi_utils.downloadAndExtract(url, dest, isZip=zipFile, hash=package['checksum'], hasher=hashlib.md5()) def writeTag(self): + if self.noClean: + return print("Writing tag {} to {}".format(self.tagContents, self.tagFile)) + if not os.path.isdir(self.path): + os.makedirs(self.path) with open(self.tagFile, 'w') as f: f.write(self.tagContents) - def getQt5InstallPath(self): - qt5InstallPath = os.path.join(self.path, 'installed', 'qt5-install') - if self.args.android: - precompiled = os.path.realpath(self.androidPackagePath) - qt5InstallPath = os.path.realpath(os.path.join(precompiled, 'qt')) - return qt5InstallPath + def writePrebuildTag(self): + print("Writing tag {} to {}".format(self.tagContents, self.tagFile)) + with open(self.prebuildTagFile, 'w') as f: + f.write(self.tagContents) + + def fixupCmakeScript(self): + cmakeScript = os.path.join(self.path, 'scripts/buildsystems/vcpkg.cmake') + newCmakeScript = cmakeScript + '.new' + isFileChanged = False + removalPrefix = "set(VCPKG_TARGET_TRIPLET " + # Open original file in read only mode and dummy file in write mode + with open(cmakeScript, 'r') as read_obj, open(newCmakeScript, 'w') as write_obj: + # Line by line copy data from original file to dummy file + for line in read_obj: + if not line.startswith(removalPrefix): + write_obj.write(line) + else: + isFileChanged = True + + if isFileChanged: + shutil.move(newCmakeScript, cmakeScript) + else: + os.remove(newCmakeScript) + def writeConfig(self): print("Writing cmake config to {}".format(self.configFilePath)) # Write out the configuration for use by CMake cmakeScript = os.path.join(self.path, 'scripts/buildsystems/vcpkg.cmake') - installPath = os.path.join(self.path, 'installed', self.triplet) - toolsPath = os.path.join(self.path, 'installed', self.hostTriplet, 'tools') + installPath = os.path.join(self.path, 'installed', self.getTripletWithBuildType(self.triplet)) + toolsPath = os.path.join(self.path, 'installed', self.getTripletWithBuildType(self.hostTriplet), 'tools') cmakeTemplate = VcpkgRepo.CMAKE_TEMPLATE if self.args.android: @@ -262,9 +341,7 @@ endif() cmakeTemplate += 'set(HIFI_ANDROID_PRECOMPILED "{}")\n'.format(precompiled) else: cmakeTemplate += VcpkgRepo.CMAKE_TEMPLATE_NON_ANDROID - - qtCmakePrefixPath = os.path.join(self.getQt5InstallPath(), "lib/cmake") - cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath, qtCmakePrefixPath).replace('\\', '/') + cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath, self.getTripletWithBuildType(self.hostTriplet)).replace('\\', '/') with open(self.configFilePath, 'w') as f: f.write(cmakeConfig) @@ -273,49 +350,3 @@ endif() # update the tag file on every run, we can scan the base dir for sub directories containing # a tag file that is older than N days, and if found, delete the directory, recovering space print("Not implemented") - - - def installQt(self): - qt5InstallPath = self.getQt5InstallPath() - if not os.path.isdir(qt5InstallPath): - print ('Downloading Qt from AWS') - dest, tail = os.path.split(qt5InstallPath) - - url = 'NOT DEFINED' - if platform.system() == 'Windows': - url = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-windows3.tar.gz' - elif platform.system() == 'Darwin': - url = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-macos.tar.gz%3FversionId=bLAgnoJ8IMKpqv8NFDcAu8hsyQy3Rwwz' - elif platform.system() == 'Linux': - dist = distro.linux_distribution() - - if distro.id() == 'ubuntu': - u_major = int( distro.major_version() ) - u_minor = int( distro.minor_version() ) - - if u_major == 16: - url = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-16.04-with-symbols.tar.gz' - elif u_major == 18: - url = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-18.04.tar.gz' - elif u_major == 19 and u_minor == 10: - url = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.6-ubuntu-19.10.tar.xz' - elif u_major > 18 and ( u_major != 19 and u_minor != 4): - print("We don't support " + distro.name(pretty=True) + " yet. Perhaps consider helping us out?") - else: - print("Sorry, " + distro.name(pretty=True) + " is old and won't be officially supported. Please consider upgrading."); - else: - print("Sorry, " + distro.name(pretty=True) + " is not supported. Please consider helping us out.") - print("It's also possible to build Qt for your distribution, please see the documentation at:") - print("https://github.com/kasenvr/project-athena/tree/kasen/core/tools/qt-builder") - return; - else: - print('UNKNOWN OPERATING SYSTEM!!!') - print("System : " + platform.system()) - print("Architecture: " + platform.architecture()) - print("Machine : " + platform.machine()) - return; - - print('Extracting ' + url + ' to ' + dest) - hifi_utils.downloadAndExtract(url, dest) - else: - print ('Qt has already been downloaded') diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 062a5538c7..608bbb19cc 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -121,14 +121,14 @@ if (APPLE) # configure CMake to use a custom Info.plist set_target_properties(${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in) - set(MACOSX_BUNDLE_BUNDLE_NAME "High Fidelity") + set(MACOSX_BUNDLE_BUNDLE_NAME "Vircadia") if (PRODUCTION_BUILD) - set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface) + set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface) else () if (DEV_BUILD) - set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface-dev) + set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface-dev) elseif (PR_BUILD) - set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface-pr) + set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface-pr) endif () endif () @@ -159,7 +159,7 @@ elseif (WIN32) set(CONFIGURE_ICON_RC_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Icon.rc") configure_file("${HF_CMAKE_DIR}/templates/Icon.rc.in" ${CONFIGURE_ICON_RC_OUTPUT}) - set(APP_FULL_NAME "Project Athena") + set(APP_FULL_NAME "Vircadia") set(CONFIGURE_VERSION_INFO_RC_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.rc") configure_file("${HF_CMAKE_DIR}/templates/VersionInfo.rc.in" ${CONFIGURE_VERSION_INFO_RC_OUTPUT}) @@ -190,7 +190,10 @@ if (BUILD_TOOLS AND NPM_EXECUTABLE) add_dependencies(resources jsdoc) endif() -add_dependencies(${TARGET_NAME} resources) +if (WIN32 OR APPLE) + add_dependencies(${TARGET_NAME} resources screenshare) +endif() + if (WIN32) # These are external plugins, but we need to do the 'add dependency' here so that their @@ -328,6 +331,10 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "${RESOURCES_DEV_DIR}/scripts" + # copy screenshare app to the resource folder + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_BINARY_DIR}/../screenshare/hifi-screenshare-darwin-x64/hifi-screenshare.app" + "${RESOURCES_DEV_DIR}/hifi-screenshare.app" # copy JSDoc files beside the executable COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" @@ -352,7 +359,7 @@ if (APPLE) COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json" "${RESOURCES_DEV_DIR}/serverless/redirect.json" - ) + ) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() diff --git a/interface/icon/interface-beta.icns b/interface/icon/interface-beta.icns index 1fe162f88e..9044dbfbf7 100644 Binary files a/interface/icon/interface-beta.icns and b/interface/icon/interface-beta.icns differ diff --git a/interface/icon/interface-beta.ico b/interface/icon/interface-beta.ico index 63a3eff482..5c279fd5c1 100644 Binary files a/interface/icon/interface-beta.ico and b/interface/icon/interface-beta.ico differ diff --git a/interface/icon/interface.icns b/interface/icon/interface.icns index 1fe162f88e..4e4df39d69 100644 Binary files a/interface/icon/interface.icns and b/interface/icon/interface.icns differ diff --git a/interface/icon/interface.ico b/interface/icon/interface.ico index 63a3eff482..d0d5c4ac96 100644 Binary files a/interface/icon/interface.ico and b/interface/icon/interface.ico differ diff --git a/interface/resources/avatar/animations/afk_texting.fbx b/interface/resources/avatar/animations/afk_texting.fbx new file mode 100644 index 0000000000..b4ac55c01c Binary files /dev/null and b/interface/resources/avatar/animations/afk_texting.fbx differ diff --git a/interface/resources/avatar/animations/settle_sitturnleft_to_sitidle.fbx b/interface/resources/avatar/animations/settle_sitturnleft_to_sitidle.fbx new file mode 100644 index 0000000000..8e6c3fc007 Binary files /dev/null and b/interface/resources/avatar/animations/settle_sitturnleft_to_sitidle.fbx differ diff --git a/interface/resources/avatar/animations/settle_sitturnright_to_sitidle.fbx b/interface/resources/avatar/animations/settle_sitturnright_to_sitidle.fbx new file mode 100644 index 0000000000..e997a913da Binary files /dev/null and b/interface/resources/avatar/animations/settle_sitturnright_to_sitidle.fbx differ diff --git a/interface/resources/avatar/animations/settle_to_idle.fbx b/interface/resources/avatar/animations/settle_to_idle.fbx new file mode 100644 index 0000000000..7b801d2c54 Binary files /dev/null and b/interface/resources/avatar/animations/settle_to_idle.fbx differ diff --git a/interface/resources/avatar/animations/settle_to_idle02.fbx b/interface/resources/avatar/animations/settle_to_idle02.fbx new file mode 100644 index 0000000000..be7429091d Binary files /dev/null and b/interface/resources/avatar/animations/settle_to_idle02.fbx differ diff --git a/interface/resources/avatar/animations/settle_to_idle03.fbx b/interface/resources/avatar/animations/settle_to_idle03.fbx new file mode 100644 index 0000000000..e7b48309bb Binary files /dev/null and b/interface/resources/avatar/animations/settle_to_idle03.fbx differ diff --git a/interface/resources/avatar/animations/settle_to_idle04.fbx b/interface/resources/avatar/animations/settle_to_idle04.fbx new file mode 100644 index 0000000000..cc31a240cb Binary files /dev/null and b/interface/resources/avatar/animations/settle_to_idle04.fbx differ diff --git a/interface/resources/avatar/animations/settle_to_idle_small.fbx b/interface/resources/avatar/animations/settle_to_idle_small.fbx index 9161a95d95..21e2ee8478 100644 Binary files a/interface/resources/avatar/animations/settle_to_idle_small.fbx and b/interface/resources/avatar/animations/settle_to_idle_small.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle04.fbx b/interface/resources/avatar/animations/sitting_idle04.fbx index 7012481b2a..7967627daf 100644 Binary files a/interface/resources/avatar/animations/sitting_idle04.fbx and b/interface/resources/avatar/animations/sitting_idle04.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle05.fbx b/interface/resources/avatar/animations/sitting_idle05.fbx index 1b89d26f15..bef1e3f73b 100644 Binary files a/interface/resources/avatar/animations/sitting_idle05.fbx and b/interface/resources/avatar/animations/sitting_idle05.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx b/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx index 75a4603335..66be22f4c4 100644 Binary files a/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx and b/interface/resources/avatar/animations/sitting_idle_once_leanforward.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx b/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx index b3ab378c26..ce0f4861cc 100644 Binary files a/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx and b/interface/resources/avatar/animations/sitting_idle_once_lookfidget.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx b/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx index a020e20044..2a6db1cf3f 100644 Binary files a/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx and b/interface/resources/avatar/animations/sitting_idle_once_shakelegs.fbx differ diff --git a/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx b/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx index 90d2bd220b..9016403b96 100644 Binary files a/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx and b/interface/resources/avatar/animations/sitting_idle_once_shiftweight.fbx differ diff --git a/interface/resources/avatar/animations/sitting_turn_left.fbx b/interface/resources/avatar/animations/sitting_turn_left.fbx new file mode 100644 index 0000000000..e0863327f4 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_turn_left.fbx differ diff --git a/interface/resources/avatar/animations/sitting_turn_right.fbx b/interface/resources/avatar/animations/sitting_turn_right.fbx new file mode 100644 index 0000000000..54b1d9608e Binary files /dev/null and b/interface/resources/avatar/animations/sitting_turn_right.fbx differ diff --git a/interface/resources/avatar/avatar-animation-optimized-ik.json b/interface/resources/avatar/avatar-animation-optimized-ik.json new file mode 100644 index 0000000000..2aad89228a --- /dev/null +++ b/interface/resources/avatar/avatar-animation-optimized-ik.json @@ -0,0 +1,6864 @@ +{ + "root": { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + }, + "id": "defaultPose", + "type": "defaultPose" + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 0, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/hydra_pose_open_right.fbx" + }, + "id": "rightHandGraspOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 0, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/hydra_pose_closed_right.fbx" + }, + "id": "rightHandGraspClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "rightHandGraspAlpha" + }, + "id": "rightHandGrasp", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_point_open_right.fbx" + }, + "id": "rightIndexPointOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_point_closed_right.fbx" + }, + "id": "rightIndexPointClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "rightHandGraspAlpha" + }, + "id": "rightIndexPoint", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_open_right.fbx" + }, + "id": "rightThumbRaiseOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_closed_right.fbx" + }, + "id": "rightThumbRaiseClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "rightHandGraspAlpha" + }, + "id": "rightThumbRaise", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx" + }, + "id": "rightIndexPointAndThumbRaiseOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_point_closed_right.fbx" + }, + "id": "rightIndexPointAndThumbRaiseClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "rightHandGraspAlpha" + }, + "id": "rightIndexPointAndThumbRaise", + "type": "blendLinear" + } + ], + "data": { + "currentState": "rightHandGrasp", + "states": [ + { + "id": "rightHandGrasp", + "interpDuration": 3, + "interpTarget": 3, + "transitions": [ + { + "state": "rightIndexPoint", + "var": "isRightIndexPoint" + }, + { + "state": "rightThumbRaise", + "var": "isRightThumbRaise" + }, + { + "state": "rightIndexPointAndThumbRaise", + "var": "isRightIndexPointAndThumbRaise" + } + ] + }, + { + "id": "rightIndexPoint", + "interpDuration": 3, + "interpTarget": 15, + "transitions": [ + { + "state": "rightHandGrasp", + "var": "isRightHandGrasp" + }, + { + "state": "rightThumbRaise", + "var": "isRightThumbRaise" + }, + { + "state": "rightIndexPointAndThumbRaise", + "var": "isRightIndexPointAndThumbRaise" + } + ] + }, + { + "id": "rightThumbRaise", + "interpDuration": 3, + "interpTarget": 15, + "transitions": [ + { + "state": "rightHandGrasp", + "var": "isRightHandGrasp" + }, + { + "state": "rightIndexPoint", + "var": "isRightIndexPoint" + }, + { + "state": "rightIndexPointAndThumbRaise", + "var": "isRightIndexPointAndThumbRaise" + } + ] + }, + { + "id": "rightIndexPointAndThumbRaise", + "interpDuration": 3, + "interpTarget": 15, + "transitions": [ + { + "state": "rightHandGrasp", + "var": "isRightHandGrasp" + }, + { + "state": "rightIndexPoint", + "var": "isRightIndexPoint" + }, + { + "state": "rightThumbRaise", + "var": "isRightThumbRaise" + } + ] + } + ] + }, + "id": "rightHandStateMachine", + "type": "stateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 0, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/hydra_pose_open_left.fbx" + }, + "id": "leftHandGraspOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 10, + "loopFlag": true, + "startFrame": 10, + "timeScale": 1, + "url": "qrc:///avatar/animations/hydra_pose_closed_left.fbx" + }, + "id": "leftHandGraspClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "leftHandGraspAlpha" + }, + "id": "leftHandGrasp", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_point_open_left.fbx" + }, + "id": "leftIndexPointOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_point_closed_left.fbx" + }, + "id": "leftIndexPointClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "leftHandGraspAlpha" + }, + "id": "leftIndexPoint", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_open_left.fbx" + }, + "id": "leftThumbRaiseOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_closed_left.fbx" + }, + "id": "leftThumbRaiseClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "leftHandGraspAlpha" + }, + "id": "leftThumbRaise", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx" + }, + "id": "leftIndexPointAndThumbRaiseOpen", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": true, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/touch_thumb_point_closed_left.fbx" + }, + "id": "leftIndexPointAndThumbRaiseClosed", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "leftHandGraspAlpha" + }, + "id": "leftIndexPointAndThumbRaise", + "type": "blendLinear" + } + ], + "data": { + "currentState": "leftHandGrasp", + "states": [ + { + "id": "leftHandGrasp", + "interpDuration": 3, + "interpTarget": 3, + "transitions": [ + { + "state": "leftIndexPoint", + "var": "isLeftIndexPoint" + }, + { + "state": "leftThumbRaise", + "var": "isLeftThumbRaise" + }, + { + "state": "leftIndexPointAndThumbRaise", + "var": "isLeftIndexPointAndThumbRaise" + } + ] + }, + { + "id": "leftIndexPoint", + "interpDuration": 3, + "interpTarget": 15, + "transitions": [ + { + "state": "leftHandGrasp", + "var": "isLeftHandGrasp" + }, + { + "state": "leftThumbRaise", + "var": "isLeftThumbRaise" + }, + { + "state": "leftIndexPointAndThumbRaise", + "var": "isLeftIndexPointAndThumbRaise" + } + ] + }, + { + "id": "leftThumbRaise", + "interpDuration": 3, + "interpTarget": 15, + "transitions": [ + { + "state": "leftHandGrasp", + "var": "isLeftHandGrasp" + }, + { + "state": "leftIndexPoint", + "var": "isLeftIndexPoint" + }, + { + "state": "leftIndexPointAndThumbRaise", + "var": "isLeftIndexPointAndThumbRaise" + } + ] + }, + { + "id": "leftIndexPointAndThumbRaise", + "interpDuration": 3, + "interpTarget": 15, + "transitions": [ + { + "state": "leftHandGrasp", + "var": "isLeftHandGrasp" + }, + { + "state": "leftIndexPoint", + "var": "isLeftIndexPoint" + }, + { + "state": "leftThumbRaise", + "var": "isLeftThumbRaise" + } + ] + } + ] + }, + "id": "leftHandStateMachine", + "type": "stateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 271, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_talk02.fbx" + }, + "id": "seatedTalk02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 252, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_talk03.fbx" + }, + "id": "seatedTalk03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 442, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_talk04.fbx" + }, + "id": "seatedTalk04", + "type": "clip" + } + ], + "data": { + "currentState": "seatedTalk02", + "randomSwitchTimeMax": 12, + "randomSwitchTimeMin": 7, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedTalk02", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedTalk03", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedTalk04", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + } + ], + "triggerRandomSwitch": "seatedTalkSwitch" + }, + "id": "seatedTalk", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle.fbx" + }, + "id": "seatedIdle01", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle02.fbx" + }, + "id": "seatedIdle02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle03.fbx" + }, + "id": "seatedIdle03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle04.fbx" + }, + "id": "seatedIdle04", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 332, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle05.fbx" + }, + "id": "seatedIdle05", + "type": "clip" + } + ], + "data": { + "currentState": "seatedIdle01", + "endFrame": 30, + "randomSwitchTimeMax": 40, + "randomSwitchTimeMin": 10, + "startFrame": 10, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedIdle01", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle02", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle03", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle04", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle05", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + } + ], + "timeScale": 1, + "triggerRandomSwitch": "seatedIdleSwitch", + "triggerTimeMax": 10 + }, + "id": "masterSeatedIdle", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 744, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_shifting.fbx" + }, + "id": "seatedFidgetShifting", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 420, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_lookfidget.fbx" + }, + "id": "seatedFidgetLookFidget", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 282, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_shiftweight.fbx" + }, + "id": "seatedFidgetShiftWeight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 428, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_fidget.fbx" + }, + "id": "seatedFidgeting", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 324, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_lookaround.fbx" + }, + "id": "seatedFidgetLookAround", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 120, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_lookleftright.fbx" + }, + "id": "seatedFidgetLookLeftRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 178, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_leanforward.fbx" + }, + "id": "seatedFidgetLeanForward", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 140, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_once_shakelegs.fbx" + }, + "id": "seatedFidgetShakeLegs", + "type": "clip" + } + ], + "data": { + "currentState": "seatedFidgetShifting", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetShifting", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLookFidget", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetShiftWeight", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgeting", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLookAround", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLookLeftRight", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetLeanForward", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidgetShakeLegs", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ] + }, + "id": "seatedFidget", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "currentState": "masterSeatedIdle", + "randomSwitchTimeMax": 20, + "randomSwitchTimeMin": 10, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "masterSeatedIdle", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedFidget", + "var": "timeToSeatedFidget" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedFidget", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": -1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetShiftingOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLookFidgetOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetShiftWeightOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetingOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLookAroundOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLookLeftRightOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetLeanForwardOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "seatedFidgetShakeLegsOnDone" + } + ] + } + ], + "transitionVar": "timeToSeatedFidget", + "triggerRandomSwitch": "", + "triggerTimeMax": 45, + "triggerTimeMin": 10 + }, + "id": "seatedIdle", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "alpha": 1, + "alphaVar": "talkOverlayAlpha", + "boneSet": "upperBody" + }, + "id": "seatedTalkOverlay", + "type": "overlay" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 44, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_agree_headnod.fbx" + }, + "id": "seatedReactionPositiveHeadNod", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 78, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_agree_headnodyes.fbx" + }, + "id": "seatedReactionPositiveHeadNodYes", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 65, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_agree_longheadnod.fbx" + }, + "id": "seatedReactionPositiveLongHeadNod", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 78, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_agree_cheer.fbx" + }, + "id": "seatedReactionPositiveCheer", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 64, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_agree_acknowledge.fbx" + }, + "id": "seatedReactionPositiveAcknowledge", + "type": "clip" + } + ], + "data": { + "currentState": "seatedReactionPositiveHeadNod", + "endFrame": 30, + "loopFlag": false, + "randomSwitchTimeMax": 12, + "randomSwitchTimeMin": 7, + "startFrame": 0, + "states": [ + { + "id": "seatedReactionPositiveHeadNod", + "interpDuration": 1, + "interpTarget": 1, + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "id": "seatedReactionPositiveHeadNodYes", + "interpDuration": 1, + "interpTarget": 1, + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "id": "seatedReactionPositiveLongHeadNod", + "interpDuration": 1, + "interpTarget": 1, + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "id": "seatedReactionPositiveCheer", + "interpDuration": 1, + "interpTarget": 1, + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "id": "seatedReactionPositiveAcknowledge", + "interpDuration": 1, + "interpTarget": 1, + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ], + "timeScale": 1, + "triggerRandomSwitch": "", + "url": "qrc:///avatar/animations/sitting_idle.fbx" + }, + "id": "seatedReactionPositive", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 64, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_disagree_headshake.fbx" + }, + "id": "seatedReactionNegativeDisagreeHeadshake", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 99, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_disagree_drophead.fbx" + }, + "id": "seatedReactionNegativeDisagreeDropHead", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 124, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_disagree_disbelief.fbx" + }, + "id": "seatedReactionNegativeDisagreeDisbelief", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 70, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_disagree_dismiss.fbx" + }, + "id": "seatedReactionNegativeDisagreeDismiss", + "type": "clip" + } + ], + "data": { + "currentState": "seatedReactionNegativeDisagreeHeadshake", + "endFrame": 30, + "loopFlag": false, + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "startFrame": 0, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedReactionNegativeDisagreeHeadshake", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionNegativeDisagreeDropHead", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionNegativeDisagreeDisbelief", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionNegativeDisagreeDismiss", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ], + "timeScale": 1, + "triggerRandomSwitch": "", + "url": "qrc:///avatar/animations/sitting_idle.fbx" + }, + "id": "seatedReactionNegative", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 32, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand_all.fbx" + }, + "id": "seatedReactionRaiseHandIntro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 345, + "loopFlag": true, + "startFrame": 32, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand_all.fbx" + }, + "id": "seatedReactionRaiseHandLoop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 400, + "loopFlag": false, + "startFrame": 345, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand_all.fbx" + }, + "id": "seatedReactionRaiseHandOutro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 18, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand02_all.fbx" + }, + "id": "seatedReactionRaiseHand02Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 378, + "loopFlag": true, + "startFrame": 18, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand02_all.fbx" + }, + "id": "seatedReactionRaiseHand02Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 435, + "loopFlag": false, + "startFrame": 378, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand02_all.fbx" + }, + "id": "seatedReactionRaiseHand02Outro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand03_all.fbx" + }, + "id": "seatedReactionRaiseHand03Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 233, + "loopFlag": true, + "mirrorFlag": false, + "startFrame": 15, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand03_all.fbx" + }, + "id": "seatedReactionRaiseHand03Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 296, + "loopFlag": false, + "mirrorFlag": false, + "startFrame": 233, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_raisehand03_all.fbx" + }, + "id": "seatedReactionRaiseHand03Outro", + "type": "clip" + } + ], + "data": { + "currentState": "seatedReactionRaiseHandIntro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHandIntro", + "interpDuration": 8, + "interpTarget": 9, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHandLoop", + "var": "seatedReactionRaiseHandIntroOnDone" + } + ] + }, + { + "id": "seatedReactionRaiseHandLoop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHandOutro", + "var": "reactionRaiseHandDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHandOutro", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHandLoop", + "var": "reactionRaiseHandEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHand02Intro", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHand02Loop", + "var": "seatedReactionRaiseHand02IntroOnDone" + } + ] + }, + { + "id": "seatedReactionRaiseHand02Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHand02Outro", + "var": "reactionRaiseHandDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHand02Outro", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHand02Loop", + "var": "reactionRaiseHandEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHand03Intro", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHand03Loop", + "var": "seatedReactionRaiseHand03IntroOnDone" + } + ] + }, + { + "id": "seatedReactionRaiseHand03Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHand03Outro", + "var": "reactionRaiseHandDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHand03Outro", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionRaiseHand03Loop", + "var": "reactionRaiseHandEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "seatedReactionRaiseHand", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 12, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap_all.fbx" + }, + "id": "seatedReactionApplaudIntro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 76, + "loopFlag": true, + "startFrame": 12, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap_all.fbx" + }, + "id": "seatedReactionApplaudLoop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 99, + "loopFlag": false, + "startFrame": 76, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap_all.fbx" + }, + "id": "seatedReactionApplaudOutro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 12, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap02_all.fbx" + }, + "id": "seatedReactionApplaud02Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 112, + "loopFlag": true, + "startFrame": 12, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap02_all.fbx" + }, + "id": "seatedReactionApplaud02Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 132, + "loopFlag": false, + "startFrame": 112, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap02_all.fbx" + }, + "id": "seatedReactionApplaud02Outro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 17, + "loopFlag": false, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap03_all.fbx" + }, + "id": "seatedReactionApplaud03Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 111, + "loopFlag": true, + "startFrame": 17, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap03_all.fbx" + }, + "id": "seatedReactionApplaud03Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 136, + "loopFlag": false, + "startFrame": 111, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_clap03_all.fbx" + }, + "id": "seatedReactionApplaud03Outro", + "type": "clip" + } + ], + "data": { + "currentState": "seatedReactionApplaudIntro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaudIntro", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaudLoop", + "var": "seatedReactionApplaudIntroOnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaudLoop", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaudOutro", + "var": "reactionApplaudDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaudOutro", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaudLoop", + "var": "reactionApplaudEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud02Intro", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaud02Loop", + "var": "seatedReactionApplaud02IntroOnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud02Loop", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaud02Outro", + "var": "reactionApplaudDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud02Outro", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaud02Loop", + "var": "reactionApplaudEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud03Intro", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaud03Loop", + "var": "seatedReactionApplaud03IntroOnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud03Loop", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaud03Outro", + "var": "reactionApplaudDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud03Outro", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionApplaud03Loop", + "var": "reactionApplaudEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "seatedReactionApplaud", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 21, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + }, + "id": "seatedReactionPointIntro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 100, + "loopFlag": true, + "startFrame": 21, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + }, + "id": "seatedReactionPointLoop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 134, + "loopFlag": false, + "mirrorFlag": false, + "startFrame": 100, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + }, + "id": "seatedReactionPointOutro", + "type": "clip" + } + ], + "data": { + "currentState": "seatedReactionPointIntro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPointIntro", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionPointLoop", + "var": "seatedReactionPointIntroOnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPointLoop", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionPointOutro", + "var": "reactionPointDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPointOutro", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionPointLoop", + "var": "reactionPointEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "seatedReactionPoint", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "alpha": 0, + "alphaVar": "seatedPointBlendAlpha", + "blendType": "addAbsolute" + }, + "id": "seatedReactionPointBase", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 11, + "loopFlag": true, + "startFrame": 11, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 30, + "loopFlag": true, + "startFrame": 30, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 50, + "loopFlag": true, + "startFrame": 50, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointUp", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 70, + "loopFlag": true, + "startFrame": 70, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointDown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 90, + "loopFlag": true, + "startFrame": 90, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointUpLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 110, + "loopFlag": true, + "startFrame": 110, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointUpRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 130, + "loopFlag": true, + "startFrame": 130, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointDownLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 150, + "loopFlag": true, + "startFrame": 150, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointDownRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 3, + "loopFlag": true, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointCenter", + "type": "clip" + } + ], + "data": { + "alpha": [ + 0, + 0, + 0 + ], + "alphaVar": "pointAroundAlpha", + "centerId": "seatedPointCenter", + "downId": "seatedPointDown", + "downLeftId": "seatedPointDownLeft", + "downRightId": "seatedPointDownRight", + "leftId": "seatedPointLeft", + "rightId": "seatedPointRight", + "upId": "seatedPointUp", + "upLeftId": "seatedPointUpLeft", + "upRightId": "seatedPointUpRight" + }, + "id": "seatedPointAround", + "type": "blendDirectional" + } + ], + "data": { + "alpha": 0, + "alphaVar": "pointBlendAlpha", + "blendType": "addAbsolute" + }, + "id": "seatedReactionPoint", + "type": "blendLinear" + } + ], + "data": { + "currentState": "seatedTalkOverlay", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedTalkOverlay", + "interpDuration": 25, + "interpTarget": 25, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "seatedReactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "seatedReactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "seatedReactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "seatedReactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "seatedReactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPositive", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "seatedTalkOverlay", + "var": "seatedReactionPositiveHeadNodOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionPositiveHeadNodYesOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionPositiveLongHeadNodOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionPositiveCheerOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionPositiveAcknowledgeOnDone" + }, + { + "state": "seatedReactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "seatedReactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "seatedReactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "seatedReactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionNegative", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "seatedReactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionNegativeDisagreeHeadshakeOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionNegativeDisagreeDropHeadOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionNegativeDisagreeDisbeliefOnDone" + }, + { + "state": "seatedTalkOverlay", + "var": "seatedReactionNegativeDisagreeDismissOnDone" + }, + { + "state": "seatedReactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "seatedReactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "seatedReactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionRaiseHand", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "seatedReactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "seatedReactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "seatedTalkOverlay", + "var": "reactionRaiseHandDisabled" + }, + { + "state": "seatedReactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "seatedReactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionApplaud", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "seatedReactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "seatedReactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "seatedReactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "seatedTalkOverlay", + "var": "reactionApplaudDisabled" + }, + { + "state": "seatedReactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPoint", + "interpDuration": 12, + "interpTarget": 12, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "seatedReactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "seatedReactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "seatedReactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "seatedReactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "seatedTalkOverlay", + "var": "reactionPointDisabled" + } + ] + } + ] + }, + "id": "seatedSM", + "type": "stateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 11, + "loopFlag": true, + "startFrame": 11, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 30, + "loopFlag": true, + "startFrame": 30, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 50, + "loopFlag": true, + "startFrame": 50, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookUp", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 70, + "loopFlag": true, + "startFrame": 70, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookDown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 97, + "loopFlag": true, + "startFrame": 97, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookUpLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 110, + "loopFlag": true, + "startFrame": 110, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookUpRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 130, + "loopFlag": true, + "startFrame": 130, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookDownLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 150, + "loopFlag": true, + "startFrame": 150, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookDownRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 3, + "loopFlag": true, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle_aimoffsets.fbx" + }, + "id": "seatedLookCenter", + "type": "clip" + } + ], + "data": { + "alpha": [ + 0, + 0, + 0 + ], + "alphaVar": "lookAroundAlpha", + "centerId": "seatedLookCenter", + "downId": "seatedLookDown", + "downLeftId": "seatedLookDownLeft", + "downRightId": "seatedLookDownRight", + "leftId": "seatedLookLeft", + "rightId": "seatedLookRight", + "upId": "seatedLookUp", + "upLeftId": "seatedLookUpLeft", + "upRightId": "seatedLookUpRight" + }, + "id": "seatedLookAroundBlend", + "type": "blendDirectional" + } + ], + "data": { + "alpha": 0, + "alphaVar": "seatedLookBlendAlpha", + "blendType": "addAbsolute" + }, + "id": "seated", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 500, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk.fbx" + }, + "id": "talk", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 325, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk02.fbx" + }, + "id": "talk02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 300, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk03.fbx" + }, + "id": "talk03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 500, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk04.fbx" + }, + "id": "talk04", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 215, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk_armsdown.fbx" + }, + "id": "talk_armsdown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 500, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk_lefthand.fbx" + }, + "id": "talk_lefthand", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 502, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/talk_righthand.fbx" + }, + "id": "talk_righthand", + "type": "clip" + } + ], + "data": { + "currentState": "talk", + "randomSwitchTimeMax": 12, + "randomSwitchTimeMin": 7, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "talk", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "talk02", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "talk03", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "talk04", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "talk_armsdown", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "talk_lefthand", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "talk_righthand", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": true, + "transitions": [ + ] + } + ], + "triggerRandomSwitch": "idleTalkSwitch" + }, + "id": "idleTalk", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 300, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle.fbx" + }, + "id": "masterIdle1", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 400, + "loopFlag": true, + "startFrame": 1, + "timeScale": 0.75, + "url": "qrc:///avatar/animations/idle02.fbx" + }, + "id": "masterIdle2", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle03.fbx" + }, + "id": "masterIdle3", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 902, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle04.fbx" + }, + "id": "masterIdle4", + "type": "clip" + } + ], + "data": { + "currentState": "masterIdle1", + "randomSwitchTimeMax": 30, + "randomSwitchTimeMin": 10, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "masterIdle1", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 0.25, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "masterIdle2", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 0.25, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "masterIdle3", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 0.25, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "masterIdle4", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 0.25, + "resume": true, + "transitions": [ + ] + } + ], + "triggerRandomSwitch": "masterIdleSwitch" + }, + "id": "masterIdle", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 91, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_once_slownod.fbx" + }, + "id": "idle_once_slownod", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 154, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_once_headtilt.fbx" + }, + "id": "idle_once_headtilt", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 491, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_once_shiftheelpivot.fbx" + }, + "id": "idle_once_shiftheelpivot", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 1620, + "loopFlag": false, + "startFrame": 1, + "timeScale": 0.7, + "url": "qrc:///avatar/animations/idleWS_all.fbx" + }, + "id": "idleWS_all", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 324, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_once_lookaround.fbx" + }, + "id": "idle_once_lookaround", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 169, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_once_neckstretch.fbx" + }, + "id": "idle_once_neckstretch", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 375, + "loopFlag": false, + "startFrame": 1, + "timeScale": 0.7, + "url": "qrc:///avatar/animations/idle_once_lookleftright.fbx" + }, + "id": "idle_once_lookleftright", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 429, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_once_fidget.fbx" + }, + "id": "idle_once_fidget", + "type": "clip" + } + ], + "data": { + "currentState": "idle_once_slownod", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "idle_once_slownod", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle_once_headtilt", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle_once_shiftheelpivot", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleWS_all", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle_once_lookaround", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle_once_neckstretch", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle_once_lookleftright", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle_once_fidget", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 0.2, + "resume": false, + "transitions": [ + ] + } + ] + }, + "id": "movement", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 80, + "loopFlag": false, + "startFrame": 1, + "startFrameVar": "", + "timeScale": 0.65, + "url": "qrc:///avatar/animations/idle_LFF_all.fbx" + }, + "id": "transitionToAltIdle1", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 80, + "loopFlag": false, + "startFrame": 1, + "timeScale": 0.65, + "url": "qrc:///avatar/animations/idle_RFF_all.fbx" + }, + "id": "transitionToAltIdle2", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 388, + "loopFlag": true, + "startFrame": 80, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_LFF_all.fbx" + }, + "id": "altIdle1", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 388, + "loopFlag": true, + "startFrame": 80, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_RFF_all.fbx" + }, + "id": "altIdle2", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 472, + "loopFlag": false, + "startFrame": 388, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_LFF_all.fbx" + }, + "id": "alt1ToMasterIdle", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 453, + "loopFlag": false, + "startFrame": 388, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_RFF_all.fbx" + }, + "id": "alt2ToMasterIdle", + "type": "clip" + } + ], + "data": { + "currentState": "transitionToAltIdle1", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "transitionToAltIdle1", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.5, + "resume": false, + "transitions": [ + { + "randomSwitchState": "altIdle1", + "var": "transitionToAltIdle1OnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "transitionToAltIdle2", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": 0.5, + "resume": false, + "transitions": [ + { + "randomSwitchState": "altIdle2", + "var": "transitionToAltIdle2OnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "altIdle1", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": -1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "alt1ToMasterIdle", + "var": "finishAltIdle2" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "altIdle2", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": -1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "alt2ToMasterIdle", + "var": "finishAltIdle2" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "alt1ToMasterIdle", + "interpDuration": 24, + "interpTarget": 24, + "interpType": "evaluateBoth", + "priority": -1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "alt2ToMasterIdle", + "interpDuration": 24, + "interpTarget": 24, + "interpType": "evaluateBoth", + "priority": -1, + "resume": false, + "transitions": [ + ] + } + ], + "transitionVar": "finishAltIdle2", + "triggerTimeMax": 60, + "triggerTimeMin": 10 + }, + "id": "alternateIdle", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "currentState": "movement", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "movement", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 0.6, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "alternateIdle", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 0.4, + "resume": false, + "transitions": [ + ] + } + ] + }, + "id": "fidget", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "currentState": "masterIdle", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "masterIdle", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "fidget", + "var": "timeToFidget" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "fidget", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "priority": -1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "masterIdle", + "var": "idle_once_slownodOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idle_once_headtiltOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idle_once_shiftheelpivotOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idleWS_allOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idle_once_lookaroundOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idle_once_neckstretchOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idle_once_lookleftrightOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "idle_once_fidgetOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "alt1ToMasterIdleOnDone" + }, + { + "randomSwitchState": "masterIdle", + "var": "alt2ToMasterIdleOnDone" + } + ] + } + ], + "transitionVar": "timeToFidget", + "triggerTimeMax": 50, + "triggerTimeMin": 10 + }, + "id": "idleStand", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "alpha": 1, + "alphaVar": "talkOverlayAlpha", + "boneSet": "upperBody" + }, + "id": "idleTalkOverlay", + "type": "overlay" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 64, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_agree_acknowledge.fbx" + }, + "id": "positiveAcknowledge", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 55, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_agree_headnod.fbx" + }, + "id": "positiveHeadNod", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 94, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_agree_headnodyes.fbx" + }, + "id": "positiveHeadNodYes", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 68, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_agree_longheadnod.fbx" + }, + "id": "positiveLongHeadNod", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 84, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_agree_thoughtfulheadnod.fbx" + }, + "id": "positiveThoughtfulHeadNod", + "type": "clip" + } + ], + "data": { + "currentState": "positiveAcknowledge", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "positiveAcknowledge", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "positiveHeadNod", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "positiveHeadNodYes", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "positiveLongHeadNod", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "positiveThoughtfulHeadNod", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 0.33, + "resume": false, + "transitions": [ + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "reactionPositive", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 72, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_disagree_annoyedheadshake.fbx" + }, + "id": "negativeAnnoyedHeadshake", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 84, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_disagree_thoughtfulheadshake.fbx" + }, + "id": "negativeThoughtfulHeadshake", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 100, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_disagree_drophead.fbx" + }, + "id": "negativeDropHead", + "type": "clip" + } + ], + "data": { + "currentState": "negativeAnnoyedHeadshake", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "negativeAnnoyedHeadshake", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "negativeThoughtfulHeadshake", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "negativeDropHead", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ] + }, + "id": "reactionNegative", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 18, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand01_all.fbx" + }, + "id": "raiseHand01Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 378, + "loopFlag": true, + "startFrame": 18, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand01_all.fbx" + }, + "id": "raiseHand01Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 435, + "loopFlag": false, + "startFrame": 378, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand01_all.fbx" + }, + "id": "raiseHand01Outro", + "type": "clip" + } + ], + "data": { + "currentState": "raiseHand01Intro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "raiseHand01Intro", + "interpDuration": 10, + "interpTarget": 10, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand01Loop", + "var": "raiseHand01IntroOnDone" + } + ] + }, + { + "id": "raiseHand01Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand01Outro", + "var": "reactionRaiseHandDisabled" + } + ] + }, + { + "id": "raiseHand01Outro", + "interpDuration": 6, + "interpTarget": 6, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand01Loop", + "var": "reactionRaiseHandEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "raiseHand01", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 19, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand03_all.fbx" + }, + "id": "raiseHand03Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 237, + "loopFlag": true, + "startFrame": 19, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand03_all.fbx" + }, + "id": "raiseHand03Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 300, + "loopFlag": false, + "startFrame": 237, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand03_all.fbx" + }, + "id": "raiseHand03Outro", + "type": "clip" + } + ], + "data": { + "currentState": "raiseHand03Intro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "raiseHand03Intro", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand03Loop", + "var": "raiseHand03IntroOnDone" + } + ] + }, + { + "id": "raiseHand03Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand03Outro", + "var": "reactionRaiseHandDisabled" + } + ] + }, + { + "id": "raiseHand03Outro", + "interpDuration": 6, + "interpTarget": 6, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand03Loop", + "var": "reactionRaiseHandEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "raiseHand03", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 32, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand04_all.fbx" + }, + "id": "raiseHand04Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 345, + "loopFlag": true, + "startFrame": 32, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand04_all.fbx" + }, + "id": "raiseHand04Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 400, + "loopFlag": false, + "startFrame": 345, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_raisehand04_all.fbx" + }, + "id": "raiseHand04Outro", + "type": "clip" + } + ], + "data": { + "currentState": "raiseHand04Intro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "raiseHand04Intro", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand04Loop", + "var": "raiseHand04IntroOnDone" + } + ] + }, + { + "id": "raiseHand04Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand04Outro", + "var": "reactionRaiseHandDisabled" + } + ] + }, + { + "id": "raiseHand04Outro", + "interpDuration": 6, + "interpTarget": 6, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "raiseHand04Loop", + "var": "reactionRaiseHandEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "raiseHand04", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "currentState": "raiseHand01", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "raiseHand01", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "raiseHand03", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "raiseHand04", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ] + }, + "id": "reactionRaiseHand", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 17, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap01_all.fbx" + }, + "id": "applaudClap01Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 111, + "loopFlag": true, + "startFrame": 17, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap01_all.fbx" + }, + "id": "applaudClap01Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 160, + "loopFlag": false, + "startFrame": 111, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap01_all.fbx" + }, + "id": "applaudClap01Outro", + "type": "clip" + } + ], + "data": { + "currentState": "applaudClap01Intro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "applaudClap01Intro", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap01Loop", + "var": "applaudClap01IntroOnDone" + } + ] + }, + { + "id": "applaudClap01Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap01Outro", + "var": "reactionApplaudDisabled" + } + ] + }, + { + "id": "applaudClap01Outro", + "interpDuration": 6, + "interpTarget": 6, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap01Loop", + "var": "reactionApplaudEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "applaudClap01", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 14, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap02_all.fbx" + }, + "id": "applaudClap02Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 78, + "loopFlag": true, + "startFrame": 14, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap02_all.fbx" + }, + "id": "applaudClap02Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 115, + "loopFlag": false, + "startFrame": 78, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap02_all.fbx" + }, + "id": "applaudClap02Outro", + "type": "clip" + } + ], + "data": { + "currentState": "applaudClap02Intro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "applaudClap02Intro", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap02Loop", + "var": "applaudClap02IntroOnDone" + } + ] + }, + { + "id": "applaudClap02Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap02Outro", + "var": "reactionApplaudDisabled" + } + ] + }, + { + "id": "applaudClap02Outro", + "interpDuration": 6, + "interpTarget": 6, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap02Loop", + "var": "reactionApplaudEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "applaudClap02", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 14, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap03_all.fbx" + }, + "id": "applaudClap03Intro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 100, + "loopFlag": true, + "startFrame": 14, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap03_all.fbx" + }, + "id": "applaudClap03Loop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 149, + "loopFlag": false, + "startFrame": 100, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_clap03_all.fbx" + }, + "id": "applaudClap03Outro", + "type": "clip" + } + ], + "data": { + "currentState": "applaudClap03Intro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "applaudClap03Intro", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap03Loop", + "var": "applaudClap03IntroOnDone" + } + ] + }, + { + "id": "applaudClap03Loop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap03Outro", + "var": "reactionApplaudDisabled" + } + ] + }, + { + "id": "applaudClap03Outro", + "interpDuration": 6, + "interpTarget": 6, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "applaudClap03Loop", + "var": "reactionApplaudEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "applaudClap03", + "type": "randomSwitchStateMachine" + } + ], + "data": { + "currentState": "applaudClap01", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "applaudClap01", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "applaudClap02", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "applaudClap03", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ] + }, + "id": "reactionApplaud", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 21, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_all.fbx" + }, + "id": "reactionPointIntro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 100, + "loopFlag": true, + "startFrame": 21, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_all.fbx" + }, + "id": "reactionPointLoop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 134, + "loopFlag": false, + "startFrame": 100, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_all.fbx" + }, + "id": "reactionPointOutro", + "type": "clip" + } + ], + "data": { + "currentState": "reactionPointIntro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "reactionPointIntro", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "reactionPointLoop", + "var": "reactionPointIntroOnDone" + } + ] + }, + { + "id": "reactionPointLoop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "reactionPointOutro", + "var": "reactionPointDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionPointOutro", + "interpDuration": 6, + "interpTarget": 6, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "reactionPointLoop", + "var": "reactionPointEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "reactionPoint", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 11, + "loopFlag": true, + "startFrame": 11, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 30, + "loopFlag": true, + "startFrame": 30, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 50, + "loopFlag": true, + "startFrame": 50, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointUp", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 70, + "loopFlag": true, + "startFrame": 70, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointDown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 90, + "loopFlag": true, + "startFrame": 90, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointUpLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 110, + "loopFlag": true, + "startFrame": 110, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointUpRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 130, + "loopFlag": true, + "startFrame": 130, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointDownLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 150, + "loopFlag": true, + "startFrame": 150, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointDownRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 3, + "loopFlag": true, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointCenter", + "type": "clip" + } + ], + "data": { + "alpha": [ + 0, + 0, + 0 + ], + "alphaVar": "pointAroundAlpha", + "centerId": "idlePointCenter", + "downId": "idlePointDown", + "downLeftId": "idlePointDownLeft", + "downRightId": "idlePointDownRight", + "leftId": "idlePointLeft", + "rightId": "idlePointRight", + "upId": "idlePointUp", + "upLeftId": "idlePointUpLeft", + "upRightId": "idlePointUpRight" + }, + "id": "idlePointAround", + "type": "blendDirectional" + } + ], + "data": { + "alpha": 0, + "alphaVar": "pointBlendAlpha", + "blendType": "addAbsolute" + }, + "id": "reactionPoint", + "type": "blendLinear" + } + ], + "data": { + "currentState": "idleTalkOverlay", + "states": [ + { + "easingType": "easeInOutQuad", + "id": "idleTalkOverlay", + "interpDuration": 25, + "interpTarget": 25, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "reactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "reactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "reactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "reactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "reactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionPositive", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "idleTalkOverlay", + "var": "positiveAcknowledgeOnDone" + }, + { + "state": "idleTalkOverlay", + "var": "positiveHeadNodOnDone" + }, + { + "state": "idleTalkOverlay", + "var": "positiveHeadNodYesOnDone" + }, + { + "state": "idleTalkOverlay", + "var": "positiveLongHeadNodOnDone" + }, + { + "state": "idleTalkOverlay", + "var": "positiveThoughtfulHeadNodOnDone" + }, + { + "state": "reactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "reactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "reactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "reactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionNegative", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "reactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "idleTalkOverlay", + "var": "negativeAnnoyedHeadshakeOnDone" + }, + { + "state": "idleTalkOverlay", + "var": "negativeThoughtfulHeadshakeOnDone" + }, + { + "state": "idleTalkOverlay", + "var": "negativeDropHeadOnDone" + }, + { + "state": "reactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "reactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "reactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionRaiseHand", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "reactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "reactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "idleTalkOverlay", + "var": "reactionRaiseHandDisabled" + }, + { + "state": "reactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "reactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionApplaud", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "reactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "reactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "reactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "idleTalkOverlay", + "var": "reactionApplaudDisabled" + }, + { + "state": "reactionPoint", + "var": "reactionPointEnabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionPoint", + "interpDuration": 10, + "interpTarget": 10, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "reactionNegative", + "var": "reactionNegativeTrigger" + }, + { + "state": "reactionPositive", + "var": "reactionPositiveTrigger" + }, + { + "state": "reactionRaiseHand", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "reactionApplaud", + "var": "reactionApplaudEnabled" + }, + { + "state": "idleTalkOverlay", + "var": "reactionPointDisabled" + } + ] + } + ] + }, + "id": "idle", + "type": "stateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 40, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_short_fwd.fbx" + }, + "id": "walkFwdShort_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 30, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_fwd.fbx" + }, + "id": "walkFwdNormal_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 26, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_fwd_fast.fbx" + }, + "id": "walkFwdFast_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 18, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jog_fwd.fbx" + }, + "id": "walkFwdJog_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 19, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/run_fast_fwd.fbx" + }, + "id": "walkFwdRun_c", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "moveForwardAlpha", + "characteristicSpeeds": [ + 0.5, + 1.8, + 2.5, + 3.55, + 5.675 + ], + "desiredSpeed": 1.4, + "desiredSpeedVar": "moveForwardSpeed" + }, + "id": "WALKFWD", + "type": "blendLinearMove" + }, + { + "children": [ + ], + "data": { + "endFrame": 13, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_to_walk.fbx" + }, + "id": "idleToWalkFwd", + "type": "clip" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 59, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle.fbx" + }, + "id": "idleSettle01", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "blendType": "", + "endFrame": 40, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle02.fbx" + }, + "id": "idleSettle02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 60, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle03.fbx" + }, + "id": "idleSettle03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 82, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle04.fbx" + }, + "id": "idleSettle04", + "type": "clip" + } + ], + "data": { + "currentState": "idleSettle01", + "endFrame": 59, + "loopFlag": false, + "randomSwitchTimeMin": 1, + "startFrame": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "idleSettle01", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle02", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle03", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle04", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ], + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle.fbx" + }, + "id": "idleSettle", + "type": "randomSwitchStateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 37, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_bwd.fbx" + }, + "id": "walkBwdShort_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 28, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_bwd_fast.fbx" + }, + "id": "walkBwdFast_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 20, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jog_bwd.fbx" + }, + "id": "jogBwd_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 14, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/run_bwd.fbx" + }, + "id": "runBwd_c", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "moveBackwardAlpha", + "characteristicSpeeds": [ + 0.6, + 1.6, + 2.8, + 4.5 + ], + "desiredSpeed": 1.4, + "desiredSpeedVar": "moveBackwardSpeed" + }, + "id": "WALKBWD", + "type": "blendLinearMove" + }, + { + "children": [ + ], + "data": { + "endFrame": 33, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/turn_left.fbx" + }, + "id": "turnLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 31, + "loopFlag": true, + "mirrorFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/turn_right.fbx" + }, + "id": "turnRight", + "type": "clip" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 35, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_left.fbx" + }, + "id": "strafeLeftWalk_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 21, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_left_fast.fbx" + }, + "id": "strafeLeftWalkFast_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 20, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jog_left.fbx" + }, + "id": "strafeLeftJog_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 19, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/run_fast_left.fbx" + }, + "id": "strafeLeftRun_c", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "moveLateralAlpha", + "characteristicSpeeds": [ + 1, + 2.55, + 3.35, + 5.25 + ], + "desiredSpeed": 1.4, + "desiredSpeedVar": "moveLateralSpeed" + }, + "id": "STRAFELEFT", + "type": "blendLinearMove" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 35, + "loopFlag": true, + "mirrorFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_right.fbx" + }, + "id": "strafeRightWalk_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 21, + "loopFlag": true, + "mirrorFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/walk_right_fast.fbx" + }, + "id": "strafeRightFast_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 20, + "loopFlag": true, + "mirrorFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jog_right.fbx" + }, + "id": "strafeRightJog_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 19, + "loopFlag": true, + "mirrorFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/run_fast_right.fbx" + }, + "id": "strafeRightRun_c", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "moveLateralAlpha", + "characteristicSpeeds": [ + 1, + 2.55, + 3.4, + 5.25 + ], + "desiredSpeed": 1.4, + "desiredSpeedVar": "moveLateralSpeed" + }, + "id": "STRAFERIGHT", + "type": "blendLinearMove" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 30, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/side_step_short_left.fbx" + }, + "id": "stepLeftShort_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 20, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/side_step_left.fbx" + }, + "id": "stepLeft_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 16, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/side_step_left_fast.fbx" + }, + "id": "strafeLeftAnim_c", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "moveLateralAlpha", + "characteristicSpeeds": [ + 0, + 0.5, + 2.5 + ], + "desiredSpeed": 1.4, + "desiredSpeedVar": "moveLateralSpeed" + }, + "id": "strafeLeftHmd", + "type": "blendLinearMove" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 30, + "loopFlag": true, + "mirrorFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/side_step_short_left.fbx" + }, + "id": "stepRightShort_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 20, + "loopFlag": true, + "mirrorFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/side_step_left.fbx" + }, + "id": "stepRight_c", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 16, + "loopFlag": true, + "mirrorFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/side_step_left_fast.fbx" + }, + "id": "strafeRightAnim_c", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "moveLateralAlpha", + "characteristicSpeeds": [ + 0, + 0.5, + 2.5 + ], + "desiredSpeed": 1.4, + "desiredSpeedVar": "moveLateralSpeed" + }, + "id": "strafeRightHmd", + "type": "blendLinearMove" + }, + { + "children": [ + ], + "data": { + "endFrame": 79, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/fly.fbx" + }, + "id": "fly", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 16, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_standing_launch_all.fbx" + }, + "id": "takeoffStand", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 15, + "loopFlag": false, + "startFrame": 4, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_running_launch_land_all.fbx" + }, + "id": "TAKEOFFRUN", + "type": "clip" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 1, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_standing_apex_all.fbx" + }, + "id": "inAirStandPreApex", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 2, + "loopFlag": false, + "startFrame": 2, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_standing_apex_all.fbx" + }, + "id": "inAirStandApex", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 3, + "loopFlag": false, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_standing_apex_all.fbx" + }, + "id": "inAirStandPostApex", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "inAirAlpha" + }, + "id": "inAirStand", + "type": "blendLinear" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 16, + "loopFlag": false, + "startFrame": 16, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_running_launch_land_all.fbx" + }, + "id": "inAirRunPreApex", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 22, + "loopFlag": false, + "startFrame": 22, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_running_launch_land_all.fbx" + }, + "id": "inAirRunApex", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 33, + "loopFlag": false, + "startFrame": 33, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_running_launch_land_all.fbx" + }, + "id": "inAirRunPostApex", + "type": "clip" + } + ], + "data": { + "alpha": 0, + "alphaVar": "inAirAlpha" + }, + "id": "INAIRRUN", + "type": "blendLinear" + }, + { + "children": [ + ], + "data": { + "endFrame": 6, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_standing_land_settle_all.fbx" + }, + "id": "landStandImpact", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 68, + "loopFlag": false, + "startFrame": 6, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_standing_land_settle_all.fbx" + }, + "id": "landStand", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 40, + "loopFlag": false, + "startFrame": 29, + "timeScale": 1, + "url": "qrc:///avatar/animations/jump_running_launch_land_all.fbx" + }, + "id": "LANDRUN", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 40, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle_small.fbx" + }, + "id": "idleSettleSmall", + "type": "clip" + } + ], + "data": { + "currentState": "idle", + "outputJoints": [ + "LeftFoot", + "RightFoot" + ], + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seated", + "interpDuration": 6, + "interpTarget": 6, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "idle", + "var": "isNotSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idle", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "idleToWalkFwd", + "interpDuration": 8, + "interpTarget": 12, + "transitions": [ + { + "state": "WALKFWD", + "var": "idleToWalkFwdOnDone" + }, + { + "state": "idle", + "var": "isNotInput" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idle", + "var": "idleSettle01OnDone" + }, + { + "state": "idle", + "var": "idleSettle02OnDone" + }, + { + "state": "idle", + "var": "idleSettle03OnDone" + }, + { + "state": "idle", + "var": "idleSettle04OnDone" + }, + { + "state": "idle", + "var": "reactionPositiveTrigger" + }, + { + "state": "idle", + "var": "reactionNegativeTrigger" + }, + { + "state": "idle", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "idle", + "var": "reactionApplaudEnabled" + }, + { + "state": "idle", + "var": "reactionPointEnabled" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettleSmall", + "interpDuration": 10, + "interpTarget": 10, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idle", + "var": "idleSettleSmallOnDone" + }, + { + "state": "idle", + "var": "reactionPositiveTrigger" + }, + { + "state": "idle", + "var": "reactionNegativeTrigger" + }, + { + "state": "idle", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "idle", + "var": "reactionApplaudEnabled" + }, + { + "state": "idle", + "var": "reactionPointEnabled" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "WALKFWD", + "interpDuration": 15, + "interpTarget": 35, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, + { + "state": "idleSettle", + "var": "isNotInputSlow" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "WALKBWD", + "interpDuration": 15, + "interpTarget": 35, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, + { + "state": "idleSettle", + "var": "isNotInputSlow" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "STRAFERIGHT", + "interpDuration": 15, + "interpTarget": 25, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, + { + "state": "idleSettle", + "var": "isNotInputSlow" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFELEFT", + "var": "isMovingLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "STRAFELEFT", + "interpDuration": 15, + "interpTarget": 25, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, + { + "state": "idleSettle", + "var": "isNotInputSlow" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isMovingRight" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "turnRight", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "idle", + "var": "isNotTurning" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "turnLeft", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "idle", + "var": "isNotTurning" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "strafeRightHmd", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idleSettle", + "var": "isNotMoving" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "strafeLeftHmd", + "interpDuration": 8, + "interpTarget": 8, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idleSettle", + "var": "isNotMoving" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "fly", + "interpDuration": 24, + "interpTarget": 24, + "interpType": "evaluateBoth", + "transitions": [ + { + "state": "idleSettle", + "var": "isNotFlying" + } + ] + }, + { + "id": "takeoffStand", + "interpDuration": 2, + "interpTarget": 2, + "transitions": [ + { + "state": "inAirStand", + "var": "isNotTakeoff" + } + ] + }, + { + "id": "TAKEOFFRUN", + "interpDuration": 2, + "interpTarget": 2, + "transitions": [ + { + "state": "INAIRRUN", + "var": "isNotTakeoff" + } + ] + }, + { + "id": "inAirStand", + "interpDuration": 3, + "interpTarget": 3, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "landStandImpact", + "var": "isNotInAir" + } + ] + }, + { + "id": "INAIRRUN", + "interpDuration": 3, + "interpTarget": 3, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "WALKFWD", + "var": "isNotInAir" + } + ] + }, + { + "id": "landStandImpact", + "interpDuration": 1, + "interpTarget": 1, + "transitions": [ + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "landStand", + "var": "landStandImpactOnDone" + } + ] + }, + { + "id": "landStand", + "interpDuration": 1, + "interpTarget": 1, + "transitions": [ + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "idle", + "var": "landStandOnDone" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "id": "LANDRUN", + "interpDuration": 2, + "interpTarget": 2, + "transitions": [ + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "WALKFWD", + "var": "landRunOnDone" + } + ] + } + ] + }, + "id": "mainStateMachine", + "type": "stateMachine" + }, + { + "children": [ + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 11, + "loopFlag": true, + "startFrame": 11, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 30, + "loopFlag": true, + "startFrame": 30, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 50, + "loopFlag": true, + "startFrame": 50, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookUp", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 70, + "loopFlag": true, + "startFrame": 70, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookDown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 97, + "loopFlag": true, + "startFrame": 97, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookUpLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 110, + "loopFlag": true, + "startFrame": 110, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookUpRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 130, + "loopFlag": true, + "startFrame": 130, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookDownLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 150, + "loopFlag": true, + "startFrame": 150, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookDownRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/idle_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 3, + "loopFlag": true, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle_aimoffsets.fbx" + }, + "id": "lookCenter", + "type": "clip" + } + ], + "data": { + "alpha": [ + 0, + 0, + 0 + ], + "alphaVar": "lookAroundAlpha", + "centerId": "lookCenter", + "downId": "lookDown", + "downLeftId": "lookDownLeft", + "downRightId": "lookDownRight", + "leftId": "lookLeft", + "rightId": "lookRight", + "upId": "lookUp", + "upLeftId": "lookUpLeft", + "upRightId": "lookUpRight" + }, + "id": "lookAroundBlend", + "type": "blendDirectional" + } + ], + "data": { + "alpha": 0, + "alphaVar": "lookBlendAlpha", + "blendType": "addAbsolute" + }, + "id": "lookAround", + "type": "blendLinear" + } + ], + "data": { + "alpha": 0, + "alphaVar": "leftHandOverlayAlpha", + "boneSet": "leftHand" + }, + "id": "leftHandOverlay", + "type": "overlay" + } + ], + "data": { + "alpha": 0, + "alphaVar": "rightHandOverlayAlpha", + "boneSet": "rightHand" + }, + "id": "rightHandOverlay", + "type": "overlay" + } + ], + "data": { + "alpha": 0, + "alphaVar": "defaultPoseOverlayAlpha", + "boneSet": "fullBody", + "boneSetVar": "defaultPoseOverlayBoneSet" + }, + "id": "defaultPoseOverlay", + "type": "overlay" + } + ], + "data": { + "alpha": 1, + "alphaVar": "splineIKAlpha", + "baseJointName": "Hips", + "basePositionVar": "hipsPosition", + "baseRotationVar": "hipsRotation", + "enabled": false, + "enabledVar": "splineIKEnabled", + "interpDuration": 15, + "midJointName": "Spine2", + "midPositionVar": "spine2Position", + "midRotationVar": "spine2Rotation", + "midTargetFlexCoefficients": [ + 1, + 1, + 1 + ], + "tipJointName": "Head", + "tipPositionVar": "headPosition", + "tipRotationVar": "headRotation", + "tipTargetFlexCoefficients": [ + 1, + 1, + 1, + 1, + 1 + ] + }, + "id": "userSplineIK", + "type": "splineIK" + } + ], + "data": { + "alpha": 1, + "alphaVar": "leftHandIKAlpha", + "baseJointName": "LeftArm", + "enabled": false, + "enabledVar": "leftHandIKEnabled", + "endEffectorPositionVarVar": "leftHandIKPositionVar", + "endEffectorRotationVarVar": "leftHandIKRotationVar", + "interpDuration": 15, + "midHingeAxis": [ + 0, + 0, + 1 + ], + "midJointName": "LeftForeArm", + "tipJointName": "LeftHand" + }, + "id": "leftHandIK", + "type": "twoBoneIK" + } + ], + "data": { + "baseJointName": "LeftArm", + "enabled": false, + "enabledVar": "leftHandPoleVectorEnabled", + "midJointName": "LeftForeArm", + "poleVectorVar": "leftHandPoleVector", + "referenceVector": [ + 1, + 0, + 0 + ], + "tipJointName": "LeftHand" + }, + "id": "leftHandPoleVector", + "type": "poleVectorConstraint" + } + ], + "data": { + "alpha": 1, + "alphaVar": "rightHandIKAlpha", + "baseJointName": "RightArm", + "enabled": false, + "enabledVar": "rightHandIKEnabled", + "endEffectorPositionVarVar": "rightHandIKPositionVar", + "endEffectorRotationVarVar": "rightHandIKRotationVar", + "interpDuration": 15, + "midHingeAxis": [ + 0, + 0, + -1 + ], + "midJointName": "RightForeArm", + "tipJointName": "RightHand" + }, + "id": "rightHandIK", + "type": "twoBoneIK" + } + ], + "data": { + "baseJointName": "RightArm", + "enabled": false, + "enabledVar": "rightHandPoleVectorEnabled", + "midJointName": "RightForeArm", + "poleVectorVar": "rightHandPoleVector", + "referenceVector": [ + -1, + 0, + 0 + ], + "tipJointName": "RightHand" + }, + "id": "rightHandPoleVector", + "type": "poleVectorConstraint" + } + ], + "data": { + "alpha": 1, + "alphaVar": "leftFootIKAlpha", + "baseJointName": "LeftUpLeg", + "enabled": false, + "enabledVar": "leftFootIKEnabled", + "endEffectorPositionVarVar": "leftFootIKPositionVar", + "endEffectorRotationVarVar": "leftFootIKRotationVar", + "interpDuration": 15, + "midHingeAxis": [ + -1, + 0, + 0 + ], + "midJointName": "LeftLeg", + "tipJointName": "LeftFoot" + }, + "id": "leftFootIK", + "type": "twoBoneIK" + } + ], + "data": { + "baseJointName": "LeftUpLeg", + "enabled": false, + "enabledVar": "leftFootPoleVectorEnabled", + "midJointName": "LeftLeg", + "poleVectorVar": "leftFootPoleVector", + "referenceVector": [ + 0, + 0, + 1 + ], + "tipJointName": "LeftFoot" + }, + "id": "leftFootPoleVector", + "type": "poleVectorConstraint" + } + ], + "data": { + "alpha": 1, + "alphaVar": "rightFootIKAlpha", + "baseJointName": "RightUpLeg", + "enabled": false, + "enabledVar": "rightFootIKEnabled", + "endEffectorPositionVarVar": "rightFootIKPositionVar", + "endEffectorRotationVarVar": "rightFootIKRotationVar", + "interpDuration": 15, + "midHingeAxis": [ + -1, + 0, + 0 + ], + "midJointName": "RightLeg", + "tipJointName": "RightFoot" + }, + "id": "rightFootIK", + "type": "twoBoneIK" + } + ], + "data": { + "baseJointName": "RightUpLeg", + "enabled": false, + "enabledVar": "rightFootPoleVectorEnabled", + "midJointName": "RightLeg", + "poleVectorVar": "rightFootPoleVector", + "referenceVector": [ + 0, + 0, + 1 + ], + "tipJointName": "RightFoot" + }, + "id": "userAnimNone", + "type": "poleVectorConstraint" + }, + { + "children": [ + ], + "data": { + "endFrame": 90, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle.fbx" + }, + "id": "userAnimA", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 90, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/idle.fbx" + }, + "id": "userAnimB", + "type": "clip" + } + ], + "data": { + "currentState": "userAnimNone", + "states": [ + { + "id": "userAnimNone", + "interpDuration": 6, + "interpTarget": 6, + "transitions": [ + { + "state": "userAnimA", + "var": "userAnimA" + }, + { + "state": "userAnimB", + "var": "userAnimB" + } + ] + }, + { + "id": "userAnimA", + "interpDuration": 6, + "interpTarget": 6, + "transitions": [ + { + "state": "userAnimNone", + "var": "userAnimNone" + }, + { + "state": "userAnimB", + "var": "userAnimB" + } + ] + }, + { + "id": "userAnimB", + "interpDuration": 6, + "interpTarget": 6, + "transitions": [ + { + "state": "userAnimNone", + "var": "userAnimNone" + }, + { + "state": "userAnimA", + "var": "userAnimA" + } + ] + } + ] + }, + "id": "userAnimStateMachine", + "type": "stateMachine" + }, + "version": "1.1" +} diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index c7cb176920..3ee0f6f9c4 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -684,139 +684,295 @@ "children": [ { "children": [ + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle.fbx" + }, + "id": "seatedIdle01", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle02.fbx" + }, + "id": "seatedIdle02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 0, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle03.fbx" + }, + "id": "seatedIdle03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 800, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle04.fbx" + }, + "id": "seatedIdle04", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 332, + "loopFlag": true, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_idle05.fbx" + }, + "id": "seatedIdle05", + "type": "clip" + } ], "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, + "currentState": "seatedIdle01", + "endFrame": 30, + "randomSwitchTimeMax": 40, + "randomSwitchTimeMin": 10, + "startFrame": 10, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedIdle01", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle02", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle03", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle04", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedIdle05", + "interpDuration": 30, + "interpTarget": 30, + "interpType": "evaluateBoth", + "priority": 1, + "resume": true, + "transitions": [ + ] + } + ], "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle.fbx" + "triggerRandomSwitch": "seatedIdleSwitch", + "triggerTimeMax": 10 }, - "id": "seatedIdle01", - "type": "clip" + "id": "masterSeatedIdleRand", + "type": "randomSwitchStateMachine" }, { "children": [ ], "data": { - "endFrame": 800, + "endFrame": 200, "loopFlag": true, "startFrame": 1, "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle02.fbx" + "url": "qrc:///avatar/animations/sitting_turn_left.fbx" }, - "id": "seatedIdle02", + "id": "seatedTurnLeft", "type": "clip" }, { "children": [ ], "data": { - "endFrame": 800, - "loopFlag": true, - "startFrame": 0, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle03.fbx" - }, - "id": "seatedIdle03", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 800, + "endFrame": 200, "loopFlag": true, "startFrame": 1, "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle04.fbx" + "url": "qrc:///avatar/animations/sitting_turn_right.fbx" }, - "id": "seatedIdle04", + "id": "seatedTurnRight", "type": "clip" }, { "children": [ ], "data": { - "endFrame": 332, - "loopFlag": true, + "endFrame": 45, + "loopFlag": false, "startFrame": 1, "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_idle05.fbx" + "url": "qrc:///avatar/animations/settle_sitturnright_to_sitidle.fbx" }, - "id": "seatedIdle05", + "id": "seatedTurnRight_to_Idle", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 45, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_sitturnleft_to_sitidle.fbx" + }, + "id": "seatedTurnLeft_to_Idle", "type": "clip" } ], "data": { - "currentState": "seatedIdle01", - "endFrame": 30, - "randomSwitchTimeMax": 40, - "randomSwitchTimeMin": 10, - "startFrame": 10, + "currentState": "masterSeatedIdleRand", "states": [ { "easingType": "easeInOutQuad", - "id": "seatedIdle01", - "interpDuration": 30, - "interpTarget": 30, - "interpType": "evaluateBoth", - "priority": 1, - "resume": true, + "id": "masterSeatedIdleRand", + "interpDuration": 15, + "interpTarget": 15, + "interpType": "snapshotPrev", "transitions": [ + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle02", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnLeft", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnLeft_to_Idle", + "var": "isSeatedNotTurning" + }, + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle03", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnRight", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "seatedTurnRight_to_Idle", + "var": "isSeatedNotTurning" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle04", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnRight_to_Idle", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + }, + { + "state": "masterSeatedIdleRand", + "var": "seatedTurnRight_to_IdleOnDone" + } ] }, { "easingType": "easeInOutQuad", - "id": "seatedIdle05", - "interpDuration": 30, - "interpTarget": 30, + "id": "seatedTurnLeft_to_Idle", + "interpDuration": 22, + "interpTarget": 22, "interpType": "evaluateBoth", - "priority": 1, - "resume": true, "transitions": [ + { + "state": "seatedTurnRight", + "var": "isSeatedTurningRight" + }, + { + "state": "seatedTurnLeft", + "var": "isSeatedTurningLeft" + }, + { + "state": "masterSeatedIdleRand", + "var": "seatedTurnLeft_to_IdleOnDone" + } ] } - ], - "timeScale": 1, - "triggerRandomSwitch": "seatedIdleSwitch", - "triggerTimeMax": 10 + ] }, "id": "masterSeatedIdle", - "type": "randomSwitchStateMachine" + "type": "stateMachine" }, { "children": [ @@ -1082,6 +1238,14 @@ { "randomSwitchState": "masterSeatedIdle", "var": "seatedFidgetShakeLegsOnDone" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "isSeatedTurningRight" + }, + { + "randomSwitchState": "masterSeatedIdle", + "var": "isSeatedTurningLeft" } ] } @@ -4804,16 +4968,117 @@ }, { "children": [ + { + "children": [ + ], + "data": { + "endFrame": 59, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle.fbx" + }, + "id": "idleSettle01", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "blendType": "", + "endFrame": 40, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle02.fbx" + }, + "id": "idleSettle02", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 60, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle03.fbx" + }, + "id": "idleSettle03", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 82, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle04.fbx" + }, + "id": "idleSettle04", + "type": "clip" + } ], "data": { + "currentState": "idleSettle01", "endFrame": 59, "loopFlag": false, + "randomSwitchTimeMin": 1, "startFrame": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "idleSettle01", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle02", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle03", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettle04", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + ] + } + ], "timeScale": 1, - "url": "qrc:///avatar/animations/settle_to_idle_small.fbx" + "url": "qrc:///avatar/animations/settle_to_idle.fbx" }, "id": "idleSettle", - "type": "clip" + "type": "randomSwitchStateMachine" }, { "children": [ @@ -5346,6 +5611,19 @@ }, "id": "LANDRUN", "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 40, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/settle_to_idle_small.fbx" + }, + "id": "idleSettleSmall", + "type": "clip" } ], "data": { @@ -5371,7 +5649,7 @@ { "easingType": "easeInOutQuad", "id": "idle", - "interpDuration": 15, + "interpDuration": 20, "interpTarget": 20, "interpType": "evaluateBoth", "transitions": [ @@ -5503,13 +5781,114 @@ { "easingType": "easeInOutQuad", "id": "idleSettle", - "interpDuration": 13, - "interpTarget": 14, + "interpDuration": 15, + "interpTarget": 15, "interpType": "snapshotPrev", "transitions": [ { "state": "idle", - "var": "idleSettleOnDone" + "var": "idleSettle01OnDone" + }, + { + "state": "idle", + "var": "idleSettle02OnDone" + }, + { + "state": "idle", + "var": "idleSettle03OnDone" + }, + { + "state": "idle", + "var": "idleSettle04OnDone" + }, + { + "state": "idle", + "var": "reactionPositiveTrigger" + }, + { + "state": "idle", + "var": "reactionNegativeTrigger" + }, + { + "state": "idle", + "var": "reactionRaiseHandEnabled" + }, + { + "state": "idle", + "var": "reactionApplaudEnabled" + }, + { + "state": "idle", + "var": "reactionPointEnabled" + }, + { + "state": "WALKFWD", + "var": "isInputForward" + }, + { + "state": "WALKBWD", + "var": "isInputBackward" + }, + { + "state": "STRAFERIGHT", + "var": "isInputRight" + }, + { + "state": "STRAFELEFT", + "var": "isInputLeft" + }, + { + "state": "strafeRightHmd", + "var": "isMovingRightHmd" + }, + { + "state": "strafeLeftHmd", + "var": "isMovingLeftHmd" + }, + { + "state": "turnRight", + "var": "isTurningRight" + }, + { + "state": "turnLeft", + "var": "isTurningLeft" + }, + { + "state": "fly", + "var": "isFlying" + }, + { + "state": "takeoffStand", + "var": "isTakeoffStand" + }, + { + "state": "TAKEOFFRUN", + "var": "isTakeoffRun" + }, + { + "state": "inAirStand", + "var": "isInAirStand" + }, + { + "state": "INAIRRUN", + "var": "isInAirRun" + }, + { + "state": "seated", + "var": "isSeated" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "idleSettleSmall", + "interpDuration": 10, + "interpTarget": 10, + "interpType": "snapshotPrev", + "transitions": [ + { + "state": "idle", + "var": "idleSettleSmallOnDone" }, { "state": "idle", @@ -5595,6 +5974,10 @@ "interpTarget": 35, "interpType": "snapshotPrev", "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, { "state": "idleSettle", "var": "isNotInputSlow" @@ -5659,6 +6042,10 @@ "interpTarget": 35, "interpType": "snapshotPrev", "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, { "state": "idleSettle", "var": "isNotInputSlow" @@ -5723,6 +6110,10 @@ "interpTarget": 25, "interpType": "snapshotPrev", "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, { "state": "idleSettle", "var": "isNotInputSlow" @@ -5779,6 +6170,10 @@ "interpTarget": 25, "interpType": "snapshotPrev", "transitions": [ + { + "state": "idleSettleSmall", + "var": "isNotInputNoMomentum" + }, { "state": "idleSettle", "var": "isNotInputSlow" diff --git a/interface/resources/avatar/avatar-animation_withSplineIKNode.json b/interface/resources/avatar/avatar-animation_withSplineIKNode.json deleted file mode 100644 index b1f198c52c..0000000000 --- a/interface/resources/avatar/avatar-animation_withSplineIKNode.json +++ /dev/null @@ -1,2229 +0,0 @@ -{ - "version": "1.1", - "root": { - "id": "userAnimStateMachine", - "type": "stateMachine", - "data": { - "currentState": "userAnimNone", - "states": [ - { - "id": "userAnimNone", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { - "var": "userAnimA", - "state": "userAnimA" - }, - { - "var": "userAnimB", - "state": "userAnimB" - } - ] - }, - { - "id": "userAnimA", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { - "var": "userAnimNone", - "state": "userAnimNone" - }, - { - "var": "userAnimB", - "state": "userAnimB" - } - ] - }, - { - "id": "userAnimB", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { - "var": "userAnimNone", - "state": "userAnimNone" - }, - { - "var": "userAnimA", - "state": "userAnimA" - } - ] - } - ] - }, - "children": [ - { - "id": "userAnimNone", - "type": "poleVectorConstraint", - "data": { - "enabled": false, - "referenceVector": [ 0, 0, 1 ], - "baseJointName": "RightUpLeg", - "midJointName": "RightLeg", - "tipJointName": "RightFoot", - "enabledVar": "rightFootPoleVectorEnabled", - "poleVectorVar": "rightFootPoleVector" - }, - "children": [ - { - "id": "rightFootIK", - "type": "twoBoneIK", - "data": { - "alpha": 1.0, - "enabled": false, - "interpDuration": 15, - "baseJointName": "RightUpLeg", - "midJointName": "RightLeg", - "tipJointName": "RightFoot", - "midHingeAxis": [ -1, 0, 0 ], - "alphaVar": "rightFootIKAlpha", - "enabledVar": "rightFootIKEnabled", - "endEffectorRotationVarVar": "rightFootIKRotationVar", - "endEffectorPositionVarVar": "rightFootIKPositionVar" - }, - "children": [ - { - "id": "leftFootPoleVector", - "type": "poleVectorConstraint", - "data": { - "enabled": false, - "referenceVector": [ 0, 0, 1 ], - "baseJointName": "LeftUpLeg", - "midJointName": "LeftLeg", - "tipJointName": "LeftFoot", - "enabledVar": "leftFootPoleVectorEnabled", - "poleVectorVar": "leftFootPoleVector" - }, - "children": [ - { - "id": "leftFootIK", - "type": "twoBoneIK", - "data": { - "alpha": 1.0, - "enabled": false, - "interpDuration": 15, - "baseJointName": "LeftUpLeg", - "midJointName": "LeftLeg", - "tipJointName": "LeftFoot", - "midHingeAxis": [ -1, 0, 0 ], - "alphaVar": "leftFootIKAlpha", - "enabledVar": "leftFootIKEnabled", - "endEffectorRotationVarVar": "leftFootIKRotationVar", - "endEffectorPositionVarVar": "leftFootIKPositionVar" - }, - "children": [ - { - "id": "rightHandPoleVector", - "type": "poleVectorConstraint", - "data": { - "enabled": false, - "referenceVector": [ -1, 0, 0 ], - "baseJointName": "RightArm", - "midJointName": "RightForeArm", - "tipJointName": "RightHand", - "enabledVar": "rightHandPoleVectorEnabled", - "poleVectorVar": "rightHandPoleVector" - }, - "children": [ - { - "id": "rightHandIK", - "type": "twoBoneIK", - "data": { - "alpha": 1.0, - "enabled": false, - "interpDuration": 15, - "baseJointName": "RightArm", - "midJointName": "RightForeArm", - "tipJointName": "RightHand", - "midHingeAxis": [ 0, 0, -1 ], - "alphaVar": "rightHandIKAlpha", - "enabledVar": "rightHandIKEnabled", - "endEffectorRotationVarVar": "rightHandIKRotationVar", - "endEffectorPositionVarVar": "rightHandIKPositionVar" - }, - "children": [ - { - "id": "leftHandPoleVector", - "type": "poleVectorConstraint", - "data": { - "enabled": false, - "referenceVector": [ 1, 0, 0 ], - "baseJointName": "LeftArm", - "midJointName": "LeftForeArm", - "tipJointName": "LeftHand", - "enabledVar": "leftHandPoleVectorEnabled", - "poleVectorVar": "leftHandPoleVector" - }, - "children": [ - { - "id": "leftHandIK", - "type": "twoBoneIK", - "data": { - "alpha": 1.0, - "enabled": false, - "interpDuration": 15, - "baseJointName": "LeftArm", - "midJointName": "LeftForeArm", - "tipJointName": "LeftHand", - "midHingeAxis": [ 0, 0, 1 ], - "alphaVar": "leftHandIKAlpha", - "enabledVar": "leftHandIKEnabled", - "endEffectorRotationVarVar": "leftHandIKRotationVar", - "endEffectorPositionVarVar": "leftHandIKPositionVar" - }, - "children": [ - { - "id": "userSplineIK", - "type": "splineIK", - "data": { - "alpha": 1.0, - "enabled": false, - "interpDuration": 15, - "baseJointName": "Hips", - "midJointName": "Spine2", - "tipJointName": "Head", - "basePositionVar": "hipsPosition", - "baseRotationVar": "hipsRotation", - "midPositionVar": "spine2Position", - "midRotationVar": "spine2Rotation", - "tipPositionVar": "headPosition", - "tipRotationVar": "headRotation", - "alphaVar": "splineIKAlpha", - "enabledVar": "splineIKEnabled", - "tipTargetFlexCoefficients": [ 1.0, 1.0, 1.0, 1.0, 1.0 ], - "midTargetFlexCoefficients": [ 1.0, 1.0, 1.0 ] - }, - "children": [ - { - "id": "defaultPoseOverlay", - "type": "overlay", - "data": { - "alpha": 0.0, - "alphaVar": "defaultPoseOverlayAlpha", - "boneSet": "fullBody", - "boneSetVar": "defaultPoseOverlayBoneSet" - }, - "children": [ - { - "id": "defaultPose", - "type": "defaultPose", - "data": { - }, - "children": [] - }, - { - "id": "rightHandOverlay", - "type": "overlay", - "data": { - "alpha": 0.0, - "boneSet": "rightHand", - "alphaVar": "rightHandOverlayAlpha" - }, - "children": [ - { - "id": "rightHandStateMachine", - "type": "stateMachine", - "data": { - "currentState": "rightHandGrasp", - "states": [ - { - "id": "rightHandGrasp", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { - "var": "isRightIndexPoint", - "state": "rightIndexPoint" - }, - { - "var": "isRightThumbRaise", - "state": "rightThumbRaise" - }, - { - "var": "isRightIndexPointAndThumbRaise", - "state": "rightIndexPointAndThumbRaise" - } - ] - }, - { - "id": "rightIndexPoint", - "interpTarget": 15, - "interpDuration": 3, - "transitions": [ - { - "var": "isRightHandGrasp", - "state": "rightHandGrasp" - }, - { - "var": "isRightThumbRaise", - "state": "rightThumbRaise" - }, - { - "var": "isRightIndexPointAndThumbRaise", - "state": "rightIndexPointAndThumbRaise" - } - ] - }, - { - "id": "rightThumbRaise", - "interpTarget": 15, - "interpDuration": 3, - "transitions": [ - { - "var": "isRightHandGrasp", - "state": "rightHandGrasp" - }, - { - "var": "isRightIndexPoint", - "state": "rightIndexPoint" - }, - { - "var": "isRightIndexPointAndThumbRaise", - "state": "rightIndexPointAndThumbRaise" - } - ] - }, - { - "id": "rightIndexPointAndThumbRaise", - "interpTarget": 15, - "interpDuration": 3, - "transitions": [ - { - "var": "isRightHandGrasp", - "state": "rightHandGrasp" - }, - { - "var": "isRightIndexPoint", - "state": "rightIndexPoint" - }, - { - "var": "isRightThumbRaise", - "state": "rightThumbRaise" - } - ] - } - ] - }, - "children": [ - { - "id": "rightHandGrasp", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "rightHandGraspAlpha" - }, - "children": [ - { - "id": "rightHandGraspOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/hydra_pose_open_right.fbx", - "startFrame": 0.0, - "endFrame": 0.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightHandGraspClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/hydra_pose_closed_right.fbx", - "startFrame": 0.0, - "endFrame": 0.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "rightIndexPoint", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "rightHandGraspAlpha" - }, - "children": [ - { - "id": "rightIndexPointOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_point_open_right.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightIndexPointClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_point_closed_right.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "rightThumbRaise", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "rightHandGraspAlpha" - }, - "children": [ - { - "id": "rightThumbRaiseOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_open_right.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightThumbRaiseClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_closed_right.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "rightIndexPointAndThumbRaise", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "rightHandGraspAlpha" - }, - "children": [ - { - "id": "rightIndexPointAndThumbRaiseOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightIndexPointAndThumbRaiseClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_point_closed_right.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - } - ] - }, - { - "id": "leftHandOverlay", - "type": "overlay", - "data": { - "alpha": 0.0, - "boneSet": "leftHand", - "alphaVar": "leftHandOverlayAlpha" - }, - "children": [ - { - "id": "leftHandStateMachine", - "type": "stateMachine", - "data": { - "currentState": "leftHandGrasp", - "states": [ - { - "id": "leftHandGrasp", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { - "var": "isLeftIndexPoint", - "state": "leftIndexPoint" - }, - { - "var": "isLeftThumbRaise", - "state": "leftThumbRaise" - }, - { - "var": "isLeftIndexPointAndThumbRaise", - "state": "leftIndexPointAndThumbRaise" - } - ] - }, - { - "id": "leftIndexPoint", - "interpTarget": 15, - "interpDuration": 3, - "transitions": [ - { - "var": "isLeftHandGrasp", - "state": "leftHandGrasp" - }, - { - "var": "isLeftThumbRaise", - "state": "leftThumbRaise" - }, - { - "var": "isLeftIndexPointAndThumbRaise", - "state": "leftIndexPointAndThumbRaise" - } - ] - }, - { - "id": "leftThumbRaise", - "interpTarget": 15, - "interpDuration": 3, - "transitions": [ - { - "var": "isLeftHandGrasp", - "state": "leftHandGrasp" - }, - { - "var": "isLeftIndexPoint", - "state": "leftIndexPoint" - }, - { - "var": "isLeftIndexPointAndThumbRaise", - "state": "leftIndexPointAndThumbRaise" - } - ] - }, - { - "id": "leftIndexPointAndThumbRaise", - "interpTarget": 15, - "interpDuration": 3, - "transitions": [ - { - "var": "isLeftHandGrasp", - "state": "leftHandGrasp" - }, - { - "var": "isLeftIndexPoint", - "state": "leftIndexPoint" - }, - { - "var": "isLeftThumbRaise", - "state": "leftThumbRaise" - } - ] - } - ] - }, - "children": [ - { - "id": "leftHandGrasp", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "leftHandGraspAlpha" - }, - "children": [ - { - "id": "leftHandGraspOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/hydra_pose_open_left.fbx", - "startFrame": 0.0, - "endFrame": 0.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "leftHandGraspClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/hydra_pose_closed_left.fbx", - "startFrame": 10.0, - "endFrame": 10.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "leftIndexPoint", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "leftHandGraspAlpha" - }, - "children": [ - { - "id": "leftIndexPointOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_point_open_left.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "leftIndexPointClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_point_closed_left.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "leftThumbRaise", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "leftHandGraspAlpha" - }, - "children": [ - { - "id": "leftThumbRaiseOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_open_left.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "leftThumbRaiseClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_closed_left.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "leftIndexPointAndThumbRaise", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "leftHandGraspAlpha" - }, - "children": [ - { - "id": "leftIndexPointAndThumbRaiseOpen", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "leftIndexPointAndThumbRaiseClosed", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/touch_thumb_point_closed_left.fbx", - "startFrame": 15.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - } - ] - }, - { - "id": "mainStateMachine", - "type": "stateMachine", - "data": { - "outputJoints": [ "LeftFoot", "RightFoot" ], - "currentState": "idle", - "states": [ - { - "id": "idle", - "interpTarget": 20, - "interpDuration": 8, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "idleToWalkFwd", - "interpTarget": 12, - "interpDuration": 8, - "transitions": [ - { - "var": "idleToWalkFwdOnDone", - "state": "WALKFWD" - }, - { - "var": "isNotMoving", - "state": "idle" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "idleSettle", - "interpTarget": 15, - "interpDuration": 8, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "idleSettleOnDone", - "state": "idle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - } - ] - }, - { - "id": "WALKFWD", - "interpTarget": 35, - "interpDuration": 10, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotMoving", - "state": "idleSettle" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "WALKBWD", - "interpTarget": 35, - "interpDuration": 10, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotMoving", - "state": "idleSettle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "STRAFERIGHT", - "interpTarget": 25, - "interpDuration": 8, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotMoving", - "state": "idleSettle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "STRAFELEFT", - "interpTarget": 25, - "interpDuration": 8, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotMoving", - "state": "idleSettle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "turnRight", - "interpTarget": 6, - "interpDuration": 8, - "transitions": [ - { - "var": "isNotTurning", - "state": "idle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "turnLeft", - "interpTarget": 6, - "interpDuration": 8, - "transitions": [ - { - "var": "isNotTurning", - "state": "idle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "strafeRightHmd", - "interpTarget": 5, - "interpDuration": 8, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotMoving", - "state": "idleSettle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - } - ] - }, - { - "id": "strafeLeftHmd", - "interpTarget": 5, - "interpDuration": 8, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotMoving", - "state": "idleSettle" - }, - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - } - ] - }, - { - "id": "fly", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { - "var": "isNotFlying", - "state": "idleSettle" - } - ] - }, - { - "id": "takeoffStand", - "interpTarget": 2, - "interpDuration": 2, - "transitions": [ - { - "var": "isNotTakeoff", - "state": "inAirStand" - } - ] - }, - { - "id": "TAKEOFFRUN", - "interpTarget": 2, - "interpDuration": 2, - "transitions": [ - { - "var": "isNotTakeoff", - "state": "INAIRRUN" - } - ] - }, - { - "id": "inAirStand", - "interpTarget": 3, - "interpDuration": 3, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotInAir", - "state": "landStandImpact" - } - ] - }, - { - "id": "INAIRRUN", - "interpTarget": 3, - "interpDuration": 3, - "interpType": "snapshotPrev", - "transitions": [ - { - "var": "isNotInAir", - "state": "WALKFWD" - } - ] - }, - { - "id": "landStandImpact", - "interpTarget": 1, - "interpDuration": 1, - "transitions": [ - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "landStandImpactOnDone", - "state": "landStand" - } - ] - }, - { - "id": "landStand", - "interpTarget": 1, - "interpDuration": 1, - "transitions": [ - { - "var": "isMovingForward", - "state": "WALKFWD" - }, - { - "var": "isMovingBackward", - "state": "WALKBWD" - }, - { - "var": "isMovingRight", - "state": "STRAFERIGHT" - }, - { - "var": "isMovingLeft", - "state": "STRAFELEFT" - }, - { - "var": "isTurningRight", - "state": "turnRight" - }, - { - "var": "isTurningLeft", - "state": "turnLeft" - }, - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "isInAirStand", - "state": "inAirStand" - }, - { - "var": "isInAirRun", - "state": "INAIRRUN" - }, - { - "var": "landStandOnDone", - "state": "idle" - }, - { - "var": "isMovingRightHmd", - "state": "strafeRightHmd" - }, - { - "var": "isMovingLeftHmd", - "state": "strafeLeftHmd" - } - ] - }, - { - "id": "LANDRUN", - "interpTarget": 2, - "interpDuration": 2, - "transitions": [ - { - "var": "isFlying", - "state": "fly" - }, - { - "var": "isTakeoffStand", - "state": "takeoffStand" - }, - { - "var": "isTakeoffRun", - "state": "TAKEOFFRUN" - }, - { - "var": "landRunOnDone", - "state": "WALKFWD" - } - ] - } - ] - }, - "children": [ - { - "id": "idle", - "type": "stateMachine", - "data": { - "currentState": "idleStand", - "states": [ - { - "id": "idleStand", - "interpTarget": 6, - "interpDuration": 10, - "transitions": [ - { - "var": "isTalking", - "state": "idleTalk" - } - ] - }, - { - "id": "idleTalk", - "interpTarget": 6, - "interpDuration": 10, - "transitions": [ - { - "var": "notIsTalking", - "state": "idleStand" - } - ] - } - ] - }, - "children": [ - { - "id": "idleStand", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/idle.fbx", - "startFrame": 0.0, - "endFrame": 300.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "idleTalk", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/talk.fbx", - "startFrame": 0.0, - "endFrame": 800.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "WALKFWD", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [ 0.5, 1.8, 2.3, 3.2, 4.5 ], - "alphaVar": "moveForwardAlpha", - "desiredSpeedVar": "moveForwardSpeed" - }, - "children": [ - { - "id": "walkFwdShort_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_short_fwd.fbx", - "startFrame": 0.0, - "endFrame": 39.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "walkFwdNormal_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_fwd.fbx", - "startFrame": 0.0, - "endFrame": 30.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "walkFwdFast_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_fwd_fast.fbx", - "startFrame": 0.0, - "endFrame": 25.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "walkFwdJog_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jog_fwd.fbx", - "startFrame": 0.0, - "endFrame": 25.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "walkFwdRun_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/run_fwd.fbx", - "startFrame": 0.0, - "endFrame": 21.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "idleToWalkFwd", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/idle_to_walk.fbx", - "startFrame": 1.0, - "endFrame": 13.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "idleSettle", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/settle_to_idle.fbx", - "startFrame": 1.0, - "endFrame": 59.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "WALKBWD", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [ 0.6, 1.6, 2.3, 3.1 ], - "alphaVar": "moveBackwardAlpha", - "desiredSpeedVar": "moveBackwardSpeed" - }, - "children": [ - { - "id": "walkBwdShort_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_short_bwd.fbx", - "startFrame": 0.0, - "endFrame": 38.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "walkBwdFast_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_bwd_fast.fbx", - "startFrame": 0.0, - "endFrame": 27.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "jogBwd_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jog_bwd.fbx", - "startFrame": 0.0, - "endFrame": 24.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "runBwd_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/run_bwd.fbx", - "startFrame": 0.0, - "endFrame": 16.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "turnLeft", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/turn_left.fbx", - "startFrame": 0.0, - "endFrame": 32.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "turnRight", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/turn_left.fbx", - "startFrame": 0.0, - "endFrame": 32.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "STRAFELEFT", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [ 0.1, 0.5, 1.0, 2.6, 3.0 ], - "alphaVar": "moveLateralAlpha", - "desiredSpeedVar": "moveLateralSpeed" - }, - "children": [ - { - "id": "strafeLeftShortStep_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_short_left.fbx", - "startFrame": 0.0, - "endFrame": 29.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "strafeLeftStep_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_left.fbx", - "startFrame": 0.0, - "endFrame": 20.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "strafeLeftWalk_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_left.fbx", - "startFrame": 0.0, - "endFrame": 35.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "strafeLeftWalkFast_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_left_fast.fbx", - "startFrame": 0.0, - "endFrame": 21.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "strafeLeftJog_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jog_left.fbx", - "startFrame": 0.0, - "endFrame": 24.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "STRAFERIGHT", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [ 0.1, 0.5, 1.0, 2.6, 3.0 ], - "alphaVar": "moveLateralAlpha", - "desiredSpeedVar": "moveLateralSpeed" - }, - "children": [ - { - "id": "strafeRightShortStep_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_short_left.fbx", - "startFrame": 0.0, - "endFrame": 29.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "strafeRightStep_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_left.fbx", - "startFrame": 0.0, - "endFrame": 20.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "strafeRightWalk_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_left.fbx", - "startFrame": 0.0, - "endFrame": 35.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "strafeRightFast_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/walk_left_fast.fbx", - "startFrame": 0.0, - "endFrame": 21.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "strafeRightJog_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jog_left.fbx", - "startFrame": 0.0, - "endFrame": 24.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - } - ] - }, - { - "id": "strafeLeftHmd", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [ 0, 0.5, 2.5 ], - "alphaVar": "moveLateralAlpha", - "desiredSpeedVar": "moveLateralSpeed" - }, - "children": [ - { - "id": "stepLeftShort_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_short_left.fbx", - "startFrame": 0.0, - "endFrame": 29.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "stepLeft_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_left.fbx", - "startFrame": 0.0, - "endFrame": 20.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "strafeLeftAnim_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_left_fast.fbx", - "startFrame": 0.0, - "endFrame": 16.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "strafeRightHmd", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [ 0, 0.5, 2.5 ], - "alphaVar": "moveLateralAlpha", - "desiredSpeedVar": "moveLateralSpeed" - }, - "children": [ - { - "id": "stepRightShort_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_short_left.fbx", - "startFrame": 0.0, - "endFrame": 29.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "stepRight_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_left.fbx", - "startFrame": 0.0, - "endFrame": 20.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - }, - { - "id": "strafeRightAnim_c", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/side_step_left_fast.fbx", - "startFrame": 0.0, - "endFrame": 16.0, - "timeScale": 1.0, - "loopFlag": true, - "mirrorFlag": true - }, - "children": [] - } - ] - }, - { - "id": "fly", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/fly.fbx", - "startFrame": 1.0, - "endFrame": 80.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "takeoffStand", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_standing_launch.fbx", - "startFrame": 2.0, - "endFrame": 16.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "TAKEOFFRUN", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_running_launch_land.fbx", - "startFrame": 4.0, - "endFrame": 15.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "inAirStand", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "inAirAlpha" - }, - "children": [ - { - "id": "inAirStandPreApex", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_standing_apex.fbx", - "startFrame": 0.0, - "endFrame": 0.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "inAirStandApex", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_standing_apex.fbx", - "startFrame": 1.0, - "endFrame": 1.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "inAirStandPostApex", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_standing_apex.fbx", - "startFrame": 2.0, - "endFrame": 2.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - } - ] - }, - { - "id": "INAIRRUN", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "inAirAlpha" - }, - "children": [ - { - "id": "inAirRunPreApex", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_running_launch_land.fbx", - "startFrame": 16.0, - "endFrame": 16.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "inAirRunApex", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_running_launch_land.fbx", - "startFrame": 22.0, - "endFrame": 22.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "inAirRunPostApex", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_running_launch_land.fbx", - "startFrame": 33.0, - "endFrame": 33.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - } - ] - }, - { - "id": "landStandImpact", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_standing_land_settle.fbx", - "startFrame": 1.0, - "endFrame": 6.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "landStand", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_standing_land_settle.fbx", - "startFrame": 6.0, - "endFrame": 68.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "LANDRUN", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/jump_running_launch_land.fbx", - "startFrame": 29.0, - "endFrame": 40.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "id": "userAnimA", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/idle.fbx", - "startFrame": 0.0, - "endFrame": 90.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "userAnimB", - "type": "clip", - "data": { - "url": "qrc:///avatar/animations/idle.fbx", - "startFrame": 0.0, - "endFrame": 90.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - } -} diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index d6ecc540c2..11c80a4dd2 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -269,6 +269,8 @@ { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + { "from": "Keyboard.GesturePinchOut", "to": "Actions.BOOM_OUT"}, + { "from": "Keyboard.GesturePinchIn", "to": "Actions.BOOM_IN"}, { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, diff --git a/interface/resources/controllers/osc.json b/interface/resources/controllers/osc.json new file mode 100644 index 0000000000..e5c639f8b7 --- /dev/null +++ b/interface/resources/controllers/osc.json @@ -0,0 +1,71 @@ +{ + "name": "OSC to Standard", + "channels": [ + { "from": "OSC.Head", "to" : "Standard.Head" }, + { "from": "OSC.LeftEye", "to" : "Standard.LeftEye" }, + { "from": "OSC.RightEye", "to" : "Standard.RightEye" }, + + { "from": "OSC.EyeBlink_L", "to": "Standard.EyeBlink_L" }, + { "from": "OSC.EyeBlink_R", "to": "Standard.EyeBlink_R" }, + { "from": "OSC.EyeSquint_L", "to": "Standard.EyeSquint_L" }, + { "from": "OSC.EyeSquint_R", "to": "Standard.EyeSquint_R" }, + { "from": "OSC.EyeDown_L", "to": "Standard.EyeDown_L" }, + { "from": "OSC.EyeDown_R", "to": "Standard.EyeDown_R" }, + { "from": "OSC.EyeIn_L", "to": "Standard.EyeIn_L" }, + { "from": "OSC.EyeIn_R", "to": "Standard.EyeIn_R" }, + { "from": "OSC.EyeOpen_L", "to": "Standard.EyeOpen_L" }, + { "from": "OSC.EyeOpen_R", "to": "Standard.EyeOpen_R" }, + { "from": "OSC.EyeOut_L", "to": "Standard.EyeOut_L" }, + { "from": "OSC.EyeOut_R", "to": "Standard.EyeOut_R" }, + { "from": "OSC.EyeUp_L", "to": "Standard.EyeUp_L" }, + { "from": "OSC.EyeUp_R", "to": "Standard.EyeUp_R" }, + { "from": "OSC.BrowsD_L", "to": "Standard.BrowsD_L" }, + { "from": "OSC.BrowsD_R", "to": "Standard.BrowsD_R" }, + { "from": "OSC.BrowsU_C", "to": "Standard.BrowsU_C" }, + { "from": "OSC.BrowsU_L", "to": "Standard.BrowsU_L" }, + { "from": "OSC.BrowsU_R", "to": "Standard.BrowsU_R" }, + { "from": "OSC.JawFwd", "to": "Standard.JawFwd" }, + { "from": "OSC.JawLeft", "to": "Standard.JawLeft" }, + { "from": "OSC.JawOpen", "to": "Standard.JawOpen" }, + { "from": "OSC.JawRight", "to": "Standard.JawRight" }, + { "from": "OSC.MouthLeft", "to": "Standard.MouthLeft" }, + { "from": "OSC.MouthRight", "to": "Standard.MouthRight" }, + { "from": "OSC.MouthFrown_L", "to": "Standard.MouthFrown_L" }, + { "from": "OSC.MouthFrown_R", "to": "Standard.MouthFrown_R" }, + { "from": "OSC.MouthSmile_L", "to": "Standard.MouthSmile_L" }, + { "from": "OSC.MouthSmile_R", "to": "Standard.MouthSmile_R" }, + { "from": "OSC.MouthDimple_L", "to": "Standard.MouthDimple_L" }, + { "from": "OSC.MouthDimple_R", "to": "Standard.MouthDimple_R" }, + { "from": "OSC.LipsStretch_L", "to": "Standard.LipsStretch_L" }, + { "from": "OSC.LipsStretch_R", "to": "Standard.LipsStretch_R" }, + { "from": "OSC.LipsUpperClose", "to": "Standard.LipsUpperClose" }, + { "from": "OSC.LipsLowerClose", "to": "Standard.LipsLowerClose" }, + { "from": "OSC.LipsFunnel", "to": "Standard.LipsFunnel" }, + { "from": "OSC.LipsPucker", "to": "Standard.LipsPucker" }, + { "from": "OSC.Puff", "to": "Standard.Puff" }, + { "from": "OSC.CheekSquint_L", "to": "Standard.CheekSquint_L" }, + { "from": "OSC.CheekSquint_R", "to": "Standard.CheekSquint_R" }, + { "from": "OSC.MouthClose", "to": "Standard.MouthClose" }, + { "from": "OSC.MouthUpperUp_L", "to": "Standard.MouthUpperUp_L" }, + { "from": "OSC.MouthUpperUp_R", "to": "Standard.MouthUpperUp_R" }, + { "from": "OSC.MouthLowerDown_L", "to": "Standard.MouthLowerDown_L" }, + { "from": "OSC.MouthLowerDown_R", "to": "Standard.MouthLowerDown_R" }, + { "from": "OSC.MouthPress_L", "to": "Standard.MouthPress_L" }, + { "from": "OSC.MouthPress_R", "to": "Standard.MouthPress_R" }, + { "from": "OSC.MouthShrugLower", "to": "Standard.MouthShrugLower" }, + { "from": "OSC.MouthShrugUpper", "to": "Standard.MouthShrugUpper" }, + { "from": "OSC.NoseSneer_L", "to": "Standard.NoseSneer_L" }, + { "from": "OSC.NoseSneer_R", "to": "Standard.NoseSneer_R" }, + { "from": "OSC.TongueOut", "to": "Standard.TongueOut" }, + { "from": "OSC.UserBlendshape0", "to": "Standard.UserBlendshape0" }, + { "from": "OSC.UserBlendshape1", "to": "Standard.UserBlendshape1" }, + { "from": "OSC.UserBlendshape2", "to": "Standard.UserBlendshape2" }, + { "from": "OSC.UserBlendshape3", "to": "Standard.UserBlendshape3" }, + { "from": "OSC.UserBlendshape4", "to": "Standard.UserBlendshape4" }, + { "from": "OSC.UserBlendshape5", "to": "Standard.UserBlendshape5" }, + { "from": "OSC.UserBlendshape6", "to": "Standard.UserBlendshape6" }, + { "from": "OSC.UserBlendshape7", "to": "Standard.UserBlendshape7" }, + { "from": "OSC.UserBlendshape8", "to": "Standard.UserBlendshape8" }, + { "from": "OSC.UserBlendshape9", "to": "Standard.UserBlendshape9" } + ] +} diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json index 08088f50d9..cca92e1353 100644 --- a/interface/resources/controllers/xbox.json +++ b/interface/resources/controllers/xbox.json @@ -52,7 +52,6 @@ { "from": "GamePad.DL", "to": "Standard.DL" }, { "from": "GamePad.DR", "to": "Standard.DR" }, - { "from": [ "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true }, { "from": "GamePad.A", "to": "Standard.A" }, { "from": "GamePad.B", "to": "Standard.B" }, { "from": "GamePad.X", "to": "Standard.X" }, diff --git a/interface/resources/html/commerce/backup_instructions.html b/interface/resources/html/commerce/backup_instructions.html index 4056b81896..034844e705 100644 --- a/interface/resources/html/commerce/backup_instructions.html +++ b/interface/resources/html/commerce/backup_instructions.html @@ -596,7 +596,7 @@

Want to learn more?

You can find out much more about the blockchain and about commerce in High Fidelity by visiting our Docs site:

-

Visit High Fidelity's Docs

+

Visit High Fidelity's Docs


diff --git a/interface/resources/html/img/tablet-help-gamepad.jpg b/interface/resources/html/img/tablet-help-gamepad.jpg index 3f246e5cc0..49fd99b141 100644 Binary files a/interface/resources/html/img/tablet-help-gamepad.jpg and b/interface/resources/html/img/tablet-help-gamepad.jpg differ diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index ec4c0ec60d..7e706187f7 100644 Binary files a/interface/resources/html/img/tablet-help-keyboard.jpg and b/interface/resources/html/img/tablet-help-keyboard.jpg differ diff --git a/interface/resources/html/img/tablet-help-oculus.jpg b/interface/resources/html/img/tablet-help-oculus.jpg index 64b57aaccb..5ebc66b75f 100644 Binary files a/interface/resources/html/img/tablet-help-oculus.jpg and b/interface/resources/html/img/tablet-help-oculus.jpg differ diff --git a/interface/resources/html/img/tablet-help-vive.jpg b/interface/resources/html/img/tablet-help-vive.jpg index 75ccf601e2..c9a5e0da7a 100644 Binary files a/interface/resources/html/img/tablet-help-vive.jpg and b/interface/resources/html/img/tablet-help-vive.jpg differ diff --git a/interface/resources/html/img/tablet-help-windowsMR.jpg b/interface/resources/html/img/tablet-help-windowsMR.jpg index 848e3deff6..2db23a0b89 100644 Binary files a/interface/resources/html/img/tablet-help-windowsMR.jpg and b/interface/resources/html/img/tablet-help-windowsMR.jpg differ diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index a414417239..17b41a1979 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -77,9 +77,9 @@ var handControllerImageURL = null; var index = 0; var count = 3; - var handControllerRefURL = "https://docs.projectathena.dev/explore/get-started/vr-controls.html#vr-controls"; - var keyboardRefURL = "https://docs.projectathena.dev/explore/get-started/desktop.html#movement-controls"; - var gamepadRefURL = "https://docs.projectathena.dev/explore/get-started/vr-controls.html#gamepad"; + var handControllerRefURL = "https://docs.vircadia.dev/explore/get-started/vr-controls.html#vr-controls"; + var keyboardRefURL = "https://docs.vircadia.dev/explore/get-started/desktop.html#movement-controls"; + var gamepadRefURL = "https://docs.vircadia.dev/explore/get-started/vr-controls.html#gamepad"; function showKbm() { document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg"); @@ -189,7 +189,7 @@
- Report Problem + Report Problem diff --git a/interface/resources/images/Loading-Inner-H.png b/interface/resources/images/Loading-Inner-H.png index 93369d61c8..06359f0c35 100644 Binary files a/interface/resources/images/Loading-Inner-H.png and b/interface/resources/images/Loading-Inner-H.png differ diff --git a/interface/resources/images/about-projectathena.png b/interface/resources/images/about-projectathena.png deleted file mode 100644 index ae2d2bcd06..0000000000 Binary files a/interface/resources/images/about-projectathena.png and /dev/null differ diff --git a/interface/resources/images/about-vircadia.png b/interface/resources/images/about-vircadia.png new file mode 100644 index 0000000000..38ece8a154 Binary files /dev/null and b/interface/resources/images/about-vircadia.png differ diff --git a/interface/resources/images/avatarapp/AvatarIsland.jpg b/interface/resources/images/avatarapp/AvatarIsland.jpg deleted file mode 100644 index f5f649abea..0000000000 Binary files a/interface/resources/images/avatarapp/AvatarIsland.jpg and /dev/null differ diff --git a/interface/resources/images/avatarapp/BodyMart.PNG b/interface/resources/images/avatarapp/BodyMart.PNG deleted file mode 100644 index 669a02c8fe..0000000000 Binary files a/interface/resources/images/avatarapp/BodyMart.PNG and /dev/null differ diff --git a/interface/resources/images/hifi-logo-blackish.svg b/interface/resources/images/hifi-logo-blackish.svg deleted file mode 100644 index 60bfb3d418..0000000000 --- a/interface/resources/images/hifi-logo-blackish.svg +++ /dev/null @@ -1,123 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/interface/resources/images/hifi-logo.svg b/interface/resources/images/hifi-logo.svg deleted file mode 100644 index e5d66d8f18..0000000000 --- a/interface/resources/images/hifi-logo.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/images/project-athena-banner-color2.svg b/interface/resources/images/project-athena-banner-color2.svg deleted file mode 100644 index b41a980fe0..0000000000 --- a/interface/resources/images/project-athena-banner-color2.svg +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - image/svg+xml - - Artboard 1 - - - - - - - - - - - - - - - - - - - Artboard 1 - - - - - - - - - - - - - - - - - - - - - diff --git a/interface/resources/images/vircadia-banner.svg b/interface/resources/images/vircadia-banner.svg new file mode 100644 index 0000000000..b53dd79040 --- /dev/null +++ b/interface/resources/images/vircadia-banner.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/images/vircadia-logo.svg b/interface/resources/images/vircadia-logo.svg new file mode 100644 index 0000000000..10645c4120 --- /dev/null +++ b/interface/resources/images/vircadia-logo.svg @@ -0,0 +1,60 @@ + +image/svg+xml + + + + + + + + + + + \ No newline at end of file diff --git a/interface/resources/qml/+android_interface/Stats.qml b/interface/resources/qml/+android_interface/Stats.qml index 1f07af786f..2581e4a82c 100644 --- a/interface/resources/qml/+android_interface/Stats.qml +++ b/interface/resources/qml/+android_interface/Stats.qml @@ -201,7 +201,7 @@ Item { } StatText { visible: root.expanded; - text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + text: "Audio In Audio: " + root.audioInboundPPS + " pps, " + "Silent: " + root.audioSilentInboundPPS + " pps"; } StatText { diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml index 7f2136ec4f..fcf4b39a2e 100644 --- a/interface/resources/qml/+webengine/BrowserWebView.qml +++ b/interface/resources/qml/+webengine/BrowserWebView.qml @@ -6,7 +6,7 @@ import controlsUit 1.0 WebView { id: webview - url: "https://projectathena.io/" + url: "https://vircadia.com/" profile: FileTypeProfile; property var parentRoot: null diff --git a/interface/resources/qml/ConnectionFailureDialog.qml b/interface/resources/qml/ConnectionFailureDialog.qml index 0d5bdfd38d..ab1e7a096c 100644 --- a/interface/resources/qml/ConnectionFailureDialog.qml +++ b/interface/resources/qml/ConnectionFailureDialog.qml @@ -7,7 +7,7 @@ MessageDialog { objectName: "ConnectionFailureDialog" title: "No Connection" - text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain." + text: "Unable to connect to this domain. Click the 'EXPLORE' button on the toolbar to visit another domain." buttons: OriginalDialogs.StandardButton.Ok icon: OriginalDialogs.StandardIcon.Warning defaultButton: OriginalDialogs.StandardButton.NoButton; diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index fbc8f9495a..a27bf0c3bb 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -3,10 +3,10 @@ // // Created by David Rowe on 3 Jun 2015 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// // Distributed under the Apache License, Version 2.0. -// // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// import Hifi 1.0 import QtQuick 2.4 @@ -85,7 +85,9 @@ FocusScope { Image { id: banner anchors.centerIn: parent - source: "../images/project-athena-banner-color2.svg" + sourceSize.width: 500 + sourceSize.height: 91 + source: "../images/vircadia-banner.svg" horizontalAlignment: Image.AlignHCenter } } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 6de8676fd0..694fd6158f 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -3,6 +3,7 @@ // // Created by Clement on 7/18/16 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -44,6 +45,9 @@ Item { property bool lostFocus: false readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp() + // If not logging into domain, then we must be logging into the metaverse... + readonly property bool isLoggingInToDomain: loginDialog.getDomainLoginRequested() + readonly property string domainLoginDomain: loginDialog.getDomainLoginDomain() QtObject { id: d @@ -70,7 +74,12 @@ Item { } function login() { - loginDialog.login(emailField.text, passwordField.text); + if (!isLoggingInToDomain) { + loginDialog.login(emailField.text, passwordField.text); + } else { + loginDialog.loginDomain(emailField.text, passwordField.text); + } + if (linkAccountBody.loginDialogPoppedUp) { var data; if (linkAccountBody.linkSteam) { @@ -85,27 +94,39 @@ Item { UserActivityLogger.logAction("encourageLoginDialog", data); } bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam, - "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus }); + "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus, + "displayName":displayNameField.text, "isLoggingInToDomain": linkAccountBody.isLoggingInToDomain, "domainLoginDomain": linkAccountBody.domainLoginDomain }); } function init() { // going to/from sign in/up dialog. loginErrorMessage.text = linkAccountBody.errorString; loginErrorMessage.visible = (linkAccountBody.errorString !== ""); - if (loginErrorMessageTextMetrics.width > emailField.width) { + if (loginErrorMessageTextMetrics.width > displayNameField.width) { loginErrorMessage.wrapMode = Text.WordWrap; - errorContainer.height = (loginErrorMessageTextMetrics.width / emailField.width) * loginErrorMessageTextMetrics.height; + errorContainer.height = (loginErrorMessageTextMetrics.width / displayNameField.width) * loginErrorMessageTextMetrics.height; } + var domainLoginText = "Log In to Domain\n" + domainLoginDomain; + loginDialogText.text = (!isLoggingInToDomain) ? "Log In to Metaverse" : domainLoginText; loginButton.text = (!linkAccountBody.linkSteam && !linkAccountBody.linkOculus) ? "Log In" : "Link Account"; + loginButton.text = (!isLoggingInToDomain) ? "Log In to Metaverse" : "Log In to Domain"; loginButton.color = hifi.buttons.blue; - emailField.placeholderText = "Username or Email"; - var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", ""); - emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : ""; + displayNameField.placeholderText = "Display Name (optional)"; + var savedDisplayName = Settings.getValue("Avatar/displayName", ""); + displayNameField.text = savedDisplayName; + emailField.placeholderText = (!isLoggingInToDomain) ? "Username or Email" : "Username"; + if (!isLoggingInToDomain) { + var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", ""); + emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : ""; + } else { + // ####### TODO + } + if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) { loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2; - loginButton.anchors.right = emailField.right; + loginButton.anchors.right = displayNameField.right; } else { - loginButton.anchors.left = emailField.left; + loginButton.anchors.left = displayNameField.left; } loginContainer.visible = true; } @@ -125,14 +146,14 @@ Item { Item { id: loginContainer - width: emailField.width - height: errorContainer.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y + + width: displayNameField.width + height: errorContainer.height + loginDialogTextContainer.height + displayNameField.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y + keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height anchors { top: parent.top topMargin: root.bannerHeight + 0.25 * parent.height left: parent.left - leftMargin: (parent.width - emailField.width) / 2 + leftMargin: (parent.width - displayNameField.width) / 2 } Item { @@ -140,9 +161,10 @@ Item { width: parent.width height: loginErrorMessageTextMetrics.height anchors { - bottom: emailField.top; - bottomMargin: hifi.dimensions.contentSpacing.y; - left: emailField.left; + bottom: loginDialogTextContainer.top + bottomMargin: hifi.dimensions.contentSpacing.y + left: loginDialogTextContainer.left + right: loginDialogTextContainer.right } TextMetrics { id: loginErrorMessageTextMetrics @@ -155,13 +177,89 @@ Item { font.family: linkAccountBody.fontFamily font.pixelSize: linkAccountBody.textFieldFontSize font.bold: linkAccountBody.fontBold + anchors { + top: parent.top + left: parent.left + right: parent.right + } verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: "" visible: false } } + + Item { + id: loginDialogTextContainer + height: 56 + anchors { + top: parent.top + left: parent.left + right: parent.right + topMargin: 1.5 * hifi.dimensions.contentSpacing.y + } + + Text { + id: loginDialogText + text: qsTr("Log In") + lineHeight: 1 + color: "white" + anchors { + top: parent.top + left: parent.left + right: parent.right + } + font.family: linkAccountBody.fontFamily + font.pixelSize: 24 + font.bold: linkAccountBody.fontBold + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + HifiControlsUit.TextField { + id: displayNameField + width: root.bannerWidth + height: linkAccountBody.textFieldHeight + font.pixelSize: linkAccountBody.textFieldFontSize + styleRenderType: Text.QtRendering + anchors { + top: loginDialogTextContainer.bottom + topMargin: 1.5 * hifi.dimensions.contentSpacing.y + } + placeholderText: "Display Name (optional)" + activeFocusOnPress: true + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Tab: + event.accepted = true; + emailField.focus = true; + break; + case Qt.Key_Backtab: + event.accepted = true; + passwordField.focus = true; + break; + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true; + if (keepMeLoggedInCheckbox.checked) { + if (!isLoggingInToDomain) { + Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + } else { + // ####### TODO + } + } + linkAccountBody.login(); + break; + } + } + onFocusChanged: { + root.text = ""; + if (focus) { + root.isPassword = false; + } + } + } HifiControlsUit.TextField { id: emailField width: root.bannerWidth @@ -169,8 +267,8 @@ Item { font.pixelSize: linkAccountBody.textFieldFontSize styleRenderType: Text.QtRendering anchors { - top: parent.top - topMargin: errorContainer.height + top: displayNameField.bottom + topMargin: 1.5 * hifi.dimensions.contentSpacing.y } placeholderText: "Username or Email" activeFocusOnPress: true @@ -182,13 +280,17 @@ Item { break; case Qt.Key_Backtab: event.accepted = true; - passwordField.focus = true; + displayNameField.focus = true; break; case Qt.Key_Enter: case Qt.Key_Return: event.accepted = true; if (keepMeLoggedInCheckbox.checked) { - Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + if (!isLoggingInToDomain) { + Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + } else { + // ####### TODO + } } linkAccountBody.login(); break; @@ -257,6 +359,9 @@ Item { Keys.onPressed: { switch (event.key) { case Qt.Key_Tab: + event.accepted = true; + displayNameField.focus = true; + break; case Qt.Key_Backtab: event.accepted = true; emailField.focus = true; @@ -265,7 +370,11 @@ Item { case Qt.Key_Return: event.accepted = true; if (keepMeLoggedInCheckbox.checked) { - Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + if (!isLoggingInToDomain) { + Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + } else { + // ####### TODO + } } linkAccountBody.login(); break; @@ -274,12 +383,13 @@ Item { } HifiControlsUit.CheckBox { id: keepMeLoggedInCheckbox - checked: Settings.getValue("keepMeLoggedIn", false); + checked: !isLoggingInToDomain ? Settings.getValue("keepMeLoggedIn", false) : false; // ####### TODO text: qsTr("Keep Me Logged In"); boxSize: 18; labelFontFamily: linkAccountBody.fontFamily labelFontSize: 18; color: hifi.colors.white; + visible: !isLoggingInToDomain anchors { top: passwordField.bottom; topMargin: hifi.dimensions.contentSpacing.y; @@ -287,14 +397,22 @@ Item { } onCheckedChanged: { Settings.setValue("keepMeLoggedIn", checked); - if (keepMeLoggedInCheckbox.checked) { - Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + if (!isLoggingInToDomain) { + if (keepMeLoggedInCheckbox.checked) { + Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text); + } else { + Settings.setValue("keepMeLoggedIn/savedUsername", ""); + } } else { - Settings.setValue("keepMeLoggedIn/savedUsername", ""); + // ####### TODO } } Component.onCompleted: { - keepMeLoggedInCheckbox.checked = !Account.loggedIn; + if (!isLoggingInToDomain) { + keepMeLoggedInCheckbox.checked = !Account.loggedIn; + } else { + // ####### TODO + } } } HifiControlsUit.Button { @@ -346,17 +464,17 @@ Item { HifiStylesUit.ShortcutText { id: cantAccessText z: 10 - visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus + visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain anchors { top: loginButton.bottom topMargin: hifi.dimensions.contentSpacing.y - left: emailField.left + left: displayNameField.left } font.family: linkAccountBody.fontFamily font.pixelSize: linkAccountBody.textFieldFontSize font.bold: linkAccountBody.fontBold - text: " Can't access your account?" + text: " Can't access your account?" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -381,13 +499,13 @@ Item { } HifiControlsUit.Button { id: continueButton; - width: emailField.width; + width: displayNameField.width; height: d.minHeightButton color: hifi.buttons.none; anchors { top: cantAccessText.bottom topMargin: hifi.dimensions.contentSpacing.y - left: emailField.left + left: displayNameField.left } text: qsTr("CONTINUE WITH STEAM") fontFamily: linkAccountBody.fontFamily @@ -420,7 +538,8 @@ Item { } bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, - "withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus }); + "withSteam": linkAccountBody.withSteam, "withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus, + "displayName":displayNameField.text}); } Component.onCompleted: { if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) { @@ -439,15 +558,16 @@ Item { } } } + Item { id: signUpContainer width: loginContainer.width height: signUpTextMetrics.height - visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus + visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain anchors { left: loginContainer.left top: loginContainer.bottom - topMargin: 0.15 * parent.height + topMargin: 0.05 * parent.height } TextMetrics { id: signUpTextMetrics @@ -480,7 +600,7 @@ Item { leftMargin: hifi.dimensions.contentSpacing.x } - text: "Sign Up" + text: "Sign Up" linkColor: hifi.colors.blueAccent onLinkActivated: { @@ -495,37 +615,54 @@ Item { "errorString": "" }); } } - } - TextMetrics { - id: dismissButtonTextMetrics - font: loginErrorMessage.font - text: dismissButton.text - } - HifiControlsUit.Button { - id: dismissButton - width: dismissButtonTextMetrics.width - height: d.minHeightButton - anchors { - bottom: parent.bottom - right: parent.right - margins: 3 * hifi.dimensions.contentSpacing.y - } - color: hifi.buttons.noneBorderlessWhite - text: qsTr("No thanks, take me in-world! >") - fontCapitalization: Font.MixedCase - fontFamily: linkAccountBody.fontFamily - fontSize: linkAccountBody.fontSize - fontBold: linkAccountBody.fontBold - visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus; - onClicked: { - if (linkAccountBody.loginDialogPoppedUp) { - var data = { - "action": "user dismissed login screen" - }; - UserActivityLogger.logAction("encourageLoginDialog", data); - loginDialog.dismissLoginDialog(); + + Text { + id: signUpTextSecond + text: qsTr("or") + anchors { + left: signUpShortcutText.right + leftMargin: hifi.dimensions.contentSpacing.x + } + lineHeight: 1 + color: "white" + font.family: linkAccountBody.fontFamily + font.pixelSize: linkAccountBody.textFieldFontSize + font.bold: linkAccountBody.fontBold + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus; + } + + TextMetrics { + id: dismissButtonTextMetrics + font: loginErrorMessage.font + text: dismissButton.text + } + HifiControlsUit.Button { + id: dismissButton + width: loginButton.width + height: d.minHeightButton + anchors { + top: signUpText.bottom + topMargin: hifi.dimensions.contentSpacing.y + left: loginButton.left + } + text: qsTr("Use without account, log in anonymously") + fontCapitalization: Font.MixedCase + fontFamily: linkAccountBody.fontFamily + fontSize: linkAccountBody.fontSize + fontBold: linkAccountBody.fontBold + visible: loginDialog.getLoginDialogPoppedUp() && !linkAccountBody.linkSteam && !linkAccountBody.linkOculus; + onClicked: { + if (linkAccountBody.loginDialogPoppedUp) { + var data = { + "action": "user dismissed login screen" + }; + UserActivityLogger.logAction("encourageLoginDialog", data); + loginDialog.dismissLoginDialog(); + } + root.tryDestroy(); } - root.tryDestroy(); } } } @@ -535,7 +672,7 @@ Item { onFocusEnabled: { if (!linkAccountBody.lostFocus) { Qt.callLater(function() { - emailField.forceActiveFocus(); + displayNameField.forceActiveFocus(); }); } } @@ -543,6 +680,7 @@ Item { linkAccountBody.lostFocus = !root.isTablet && !root.isOverlay; if (linkAccountBody.lostFocus) { Qt.callLater(function() { + displayNameField.focus = false; emailField.focus = false; passwordField.focus = false; }); @@ -558,7 +696,7 @@ Item { d.resize(); init(); Qt.callLater(function() { - emailField.forceActiveFocus(); + displayNameField.forceActiveFocus(); }); } diff --git a/interface/resources/qml/LoginDialog/LoggingInBody.qml b/interface/resources/qml/LoginDialog/LoggingInBody.qml index 583f00583b..757c8b7449 100644 --- a/interface/resources/qml/LoginDialog/LoggingInBody.qml +++ b/interface/resources/qml/LoginDialog/LoggingInBody.qml @@ -3,6 +3,7 @@ // // Created by Wayne Chen on 10/18/18 // Copyright 2018 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -31,6 +32,9 @@ Item { property bool linkSteam: linkSteam property bool linkOculus: linkOculus property bool createOculus: createOculus + property bool isLoggingInToDomain: isLoggingInToDomain + property string domainLoginDomain: domainLoginDomain + property string displayName: "" readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp() @@ -105,6 +109,9 @@ Item { loggingInGlyph.visible = true; loggingInText.text = "Logging in to Oculus"; loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2; + } else if (loggingInBody.isLoggingInToDomain) { + loggingInText.text = "Logging in to " + domainLoginDomain; + loggingInText.anchors.centerIn = loggingInHeader; } else { loggingInText.text = "Logging in"; loggingInText.anchors.centerIn = loggingInHeader; @@ -161,6 +168,7 @@ Item { } } + MyAvatar.displayName = displayName; successTimer.start(); } diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index 7347464f4e..fcb47c3534 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -23,7 +23,7 @@ Item { clip: true height: root.height width: root.width - readonly property string termsContainerText: qsTr("By signing up, you agree to Project Athena's Terms of Service") + readonly property string termsContainerText: qsTr("By signing up, you agree to Vircadia's Terms of Service") property int textFieldHeight: 31 property string fontFamily: "Raleway" property int fontSize: 15 @@ -395,7 +395,7 @@ Item { text: signUpBody.termsContainerText Component.onCompleted: { // with the link. - termsText.text = qsTr("By signing up, you agree to Project Athena's Terms of Service") + termsText.text = qsTr("By signing up, you agree to Vircadia's Terms of Service") } } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 8b3c878d46..9710723bed 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -19,7 +19,7 @@ import TabletScriptingInterface 1.0 Item { id: usernameCollisionBody clip: true - readonly property string termsContainerText: qsTr("By creating this user profile, you agree to Project Athena's Terms of Service") + readonly property string termsContainerText: qsTr("By creating this user profile, you agree to Vircadia's Terms of Service") width: root.width height: root.height readonly property string fontFamily: "Raleway" @@ -218,7 +218,7 @@ Item { text: usernameCollisionBody.termsContainerText Component.onCompleted: { // with the link. - termsText.text = qsTr("By creating this user profile, you agree to Project Athena's Terms of Service") + termsText.text = qsTr("By creating this user profile, you agree to Vircadia's Terms of Service") } } diff --git a/interface/resources/qml/OverlayLoginDialog.qml b/interface/resources/qml/OverlayLoginDialog.qml index 0ad2c57e5f..5fb72f2689 100644 --- a/interface/resources/qml/OverlayLoginDialog.qml +++ b/interface/resources/qml/OverlayLoginDialog.qml @@ -81,7 +81,9 @@ FocusScope { Image { id: banner anchors.centerIn: parent - source: "../images/high-fidelity-banner.svg" + sourceSize.width: 500 + sourceSize.height: 91 + source: "../images/vircadia-banner.svg" horizontalAlignment: Image.AlignHCenter } } diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 23aa256cdc..7ab216d4ff 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -238,7 +238,7 @@ Item { } StatText { visible: root.expanded; - text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + text: "Audio In Audio: " + root.audioInboundPPS + " pps, " + "Silent: " + root.audioSilentInboundPPS + " pps"; } StatText { diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 9c22d0b65b..c3a7a45c69 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -47,7 +47,7 @@ ScrollingWindow { Image { id: logo - source: "../images/hifi-logo.svg" + source: "../images/vircadia-logo.svg" width: updateDialog.logoSize height: updateDialog.logoSize anchors { diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 3340226761..d58bcd2eba 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -49,4 +49,6 @@ Item { Component.onCompleted: { load(root.url, root.scriptUrl); } + + signal sendToScript(var message); } diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index a36b6791a4..e9546748c0 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -137,7 +137,7 @@ Item { if (webViewCoreUserAgent !== undefined) { webViewCore.profile.httpUserAgent = webViewCoreUserAgent } else { - webViewCore.profile.httpUserAgent += " (HighFidelityInterface)"; + webViewCore.profile.httpUserAgent += " (VircadiaInterface)"; } // Ensure the JS from the web-engine makes it to our logging webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { diff --git a/interface/resources/qml/controlsUit/SpinBox.qml b/interface/resources/qml/controlsUit/SpinBox.qml index 564157efb0..a888bbd07c 100644 --- a/interface/resources/qml/controlsUit/SpinBox.qml +++ b/interface/resources/qml/controlsUit/SpinBox.qml @@ -109,7 +109,7 @@ SpinBox { id: spinboxText z: 2 color: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray) + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight @@ -130,7 +130,7 @@ SpinBox { } color: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray) + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) text: suffix verticalAlignment: Qt.AlignVCenter diff --git a/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml b/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml index 544824135e..17292d2923 100644 --- a/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml +++ b/interface/resources/qml/dialogs/TabletConnectionFailureDialog.qml @@ -19,7 +19,7 @@ Item { buttons: OriginalDialogs.StandardButton.Ok, defaultButton: OriginalDialogs.StandardButton.NoButton, title: "No Connection", - text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain." + text: "Unable to connect to this domain. Click the 'EXPLORE' button on the toolbar to visit another domain." }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Ok) { diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index a7e5c236ca..f794fea66a 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -129,7 +129,9 @@ FocusScope { Image { id: banner anchors.centerIn: parent - source: "../../images/project-athena-banner-color2.svg" + sourceSize.width: 400 + sourceSize.height: 73 + source: "../../images/vircadia-banner.svg" horizontalAlignment: Image.AlignHCenter } } diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index 997407885b..cfc1121af9 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -777,9 +777,7 @@ Rectangle { hoverEnabled: true onClicked: { - popup.showBuyAvatars(function() { - emitSendToScript({'method' : 'navigate', 'url' : 'hifi://BodyMart'}) - }, function(link) { + popup.showBuyAvatars(null, function(link) { emitSendToScript({'method' : 'navigate', 'url' : link}) }); } diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 963a3246fc..31f3ee44df 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -6,6 +6,7 @@ // // Created by Vlad Stelmahovsky on 03/22/2017 // Copyright 2017 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -325,178 +326,14 @@ Rectangle { size: 16; text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to talk.") : - qsTr("Press and hold grip triggers on both of your controllers to talk."); - } - } - - Separator { - id: secondSeparator; - anchors.top: pttTextContainer.visible ? pttTextContainer.bottom : switchesContainer.bottom; - anchors.topMargin: 10; - } - - Item { - id: inputDeviceHeader - 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("Choose input device"); - } - } - - ListView { - id: inputView; - width: rightMostInputLevelPos; - anchors.top: inputDeviceHeader.bottom; - anchors.topMargin: 10; - x: margins.paddings - interactive: false; - height: contentHeight; - - clip: true; - model: AudioScriptingInterface.devices.input; - delegate: Item { - width: rightMostInputLevelPos - margins.paddings*2 - height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ? - (margins.sizeCheckBox > checkBoxInput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxInput.implicitHeight + 4) : 0 - visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1) - AudioControls.CheckBox { - id: checkBoxInput - anchors.left: parent.left - spacing: margins.sizeCheckBox - boxSize - anchors.verticalCenter: parent.verticalCenter - width: parent.width - inputLevel.width - clip: true - checkable: !checked - checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; - boxSize: margins.sizeCheckBox / 2 - isRound: true - text: devicename - fontSize: 16; - onPressed: { - if (!checked) { - stereoInput.checked = false; - AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo - AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1); - } - } - } - 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; - } - } - } - - AudioControls.LoopbackAudio { - id: loopbackAudio - x: margins.paddings - anchors.top: inputView.bottom; - anchors.topMargin: 10; - - visible: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR); - anchors { left: parent.left; leftMargin: margins.paddings } - } - - Separator { - id: thirdSeparator; - anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputView.bottom; - anchors.topMargin: 10; - } - - Item { - id: outputDeviceHeader; - anchors.topMargin: 10; - anchors.top: thirdSeparator.bottom; - x: margins.paddings; - width: parent.width - margins.paddings*2 - height: 36 - - HiFiGlyphs { - anchors.left: parent.left - anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% - anchors.verticalCenter: parent.verticalCenter; - width: margins.sizeCheckBox - text: hifi.glyphs.unmuted; - color: hifi.colors.white; - size: 36; - } - - RalewayRegular { - width: margins.sizeText + margins.sizeLevel - anchors.left: parent.left - anchors.leftMargin: margins.sizeCheckBox - anchors.verticalCenter: parent.verticalCenter; - size: 22; - color: hifi.colors.white; - text: qsTr("Choose output device"); - } - } - - ListView { - id: outputView - width: parent.width - margins.paddings*2 - x: margins.paddings; - interactive: false; - height: contentHeight; - anchors.top: outputDeviceHeader.bottom; - anchors.topMargin: 10; - clip: true; - model: AudioScriptingInterface.devices.output; - delegate: Item { - width: rightMostInputLevelPos - height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ? - (margins.sizeCheckBox > checkBoxOutput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxOutput.implicitHeight + 4) : 0 - visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1) - - AudioControls.CheckBox { - id: checkBoxOutput - width: parent.width - spacing: margins.sizeCheckBox - boxSize - boxSize: margins.sizeCheckBox / 2 - isRound: true - checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; - checkable: !checked - text: devicename - fontSize: 16 - onPressed: { - if (!checked) { - AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1); - } - } - } + qsTr("Press and hold grip triggers on both controllers to talk."); } } Item { id: avatarGainContainer x: margins.paddings; - anchors.top: outputView.bottom; + anchors.top: pttTextContainer.bottom; anchors.topMargin: 10; width: parent.width - margins.paddings*2 height: avatarGainSliderTextMetrics.height @@ -677,12 +514,174 @@ Rectangle { } } - AudioControls.PlaySampleSound { - id: playSampleSound - x: margins.paddings + Separator { + id: secondSeparator; anchors.top: systemInjectorGainContainer.bottom; anchors.topMargin: 10; } - } -} + Item { + id: inputDeviceHeader + 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("Choose input device"); + } + } + + AudioControls.LoopbackAudio { + id: loopbackAudio + x: margins.paddings + anchors.top: inputDeviceHeader.bottom; + anchors.topMargin: 10; + visible: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR); + anchors { left: parent.left; leftMargin: margins.paddings } + } + + ListView { + id: inputView; + width: rightMostInputLevelPos; + anchors.top: loopbackAudio.visible ? loopbackAudio.bottom : inputDeviceHeader.bottom; + anchors.topMargin: 10; + x: margins.paddings + interactive: false; + height: contentHeight; + + clip: true; + model: AudioScriptingInterface.devices.input; + delegate: Item { + width: rightMostInputLevelPos - margins.paddings*2 + height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ? + (margins.sizeCheckBox > checkBoxInput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxInput.implicitHeight + 4) : 0 + visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1) + AudioControls.CheckBox { + id: checkBoxInput + anchors.left: parent.left + spacing: margins.sizeCheckBox - boxSize + anchors.verticalCenter: parent.verticalCenter + width: parent.width - inputLevel.width + clip: true + checkable: !checked + checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + boxSize: margins.sizeCheckBox / 2 + isRound: true + text: devicename + fontSize: 16; + onPressed: { + if (!checked) { + stereoInput.checked = false; + AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo + AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1); + } + } + } + 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; + } + } + } + + Separator { + id: thirdSeparator; + anchors.top: inputView.bottom; + anchors.topMargin: 10; + } + + Item { + id: outputDeviceHeader; + anchors.topMargin: 10; + anchors.top: thirdSeparator.bottom; + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: 36 + + HiFiGlyphs { + anchors.left: parent.left + anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% + anchors.verticalCenter: parent.verticalCenter; + width: margins.sizeCheckBox + text: hifi.glyphs.unmuted; + color: hifi.colors.white; + size: 36; + } + + RalewayRegular { + width: margins.sizeText + margins.sizeLevel + anchors.left: parent.left + anchors.leftMargin: margins.sizeCheckBox + anchors.verticalCenter: parent.verticalCenter; + size: 22; + color: hifi.colors.white; + text: qsTr("Choose output device"); + } + } + + AudioControls.PlaySampleSound { + id: playSampleSound + x: margins.paddings + anchors.top: outputDeviceHeader.bottom; + anchors.topMargin: 10; + } + + ListView { + id: outputView + width: parent.width - margins.paddings*2 + x: margins.paddings; + interactive: false; + height: contentHeight + 10; + anchors.top: playSampleSound.bottom; + anchors.topMargin: 10; + clip: true; + model: AudioScriptingInterface.devices.output; + delegate: Item { + width: rightMostInputLevelPos + height: ((type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1)) ? + (margins.sizeCheckBox > checkBoxOutput.implicitHeight ? margins.sizeCheckBox + 4 : checkBoxOutput.implicitHeight + 4) : 0 + visible: (type != "hmd" && bar.currentIndex === 0) || (type != "desktop" && bar.currentIndex === 1) + AudioControls.CheckBox { + id: checkBoxOutput + width: parent.width + spacing: margins.sizeCheckBox - boxSize + boxSize: margins.sizeCheckBox / 2 + isRound: true + checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + checkable: !checked + text: devicename + fontSize: 16 + onPressed: { + if (!checked) { + AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml index 9158e25f75..00d8a29561 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml @@ -229,7 +229,7 @@ Item { } function openDocs() { - Qt.openUrlExternally("https://docs.projectathena.dev/create/avatars/package-avatar.html"); + Qt.openUrlExternally("https://docs.vircadia.dev/create/avatars/package-avatar.html"); } function openVideo() { diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml index edb862b023..128ef61c75 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml @@ -128,7 +128,7 @@ ShadowRectangle { } } - // FIXME: Link to a Project Athena version of the video. + // FIXME: Link to a Vircadias version of the video. /* RalewayButton { id: video diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml index a0c98b0821..9b59303a7e 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml @@ -318,7 +318,7 @@ Item { text: "This item is not for sale yet, learn more." onLinkActivated: { - Qt.openUrlExternally("https://docs.projectathena.dev/sell/add-item/upload-avatar.html"); + Qt.openUrlExternally("https://docs.vircadia.dev/sell/add-item/upload-avatar.html"); } } diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 391e4fab37..dfee6e60f7 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -245,9 +245,7 @@ Rectangle { linkColor: hifi.colors.blueHighlight Layout.alignment: Qt.AlignVCenter onLinkActivated: { - popup.showGetWearables(function() { - emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'}) - }, function(link) { + popup.showGetWearables(null, function(link) { emitSendToScript({'method' : 'navigate', 'url' : link}) }); } @@ -405,7 +403,6 @@ Rectangle { Vector3 { id: positionVector - backgroundColor: "lightgray" enabled: getCurrentWearable() !== null function set(localPosition) { @@ -465,7 +462,6 @@ Rectangle { Vector3 { id: rotationVector - backgroundColor: "lightgray" enabled: getCurrentWearable() !== null function set(localRotationAngles) { @@ -552,7 +548,7 @@ Rectangle { realFrom: 0.1 realTo: 3.0 realValue: 1.0 - backgroundColor: "lightgray" + backgroundColor: activeFocus ? "white" : "lightgray" width: positionVector.spinboxWidth colorScheme: hifi.colorSchemes.light diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 691fbedac3..62ceee1b63 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -7,7 +7,7 @@ MessageBox { popup.onButton2Clicked = callback; popup.titleText = 'Specify Avatar URL' popup.bodyText = 'This will not overwrite your existing favorite if you are wearing one.
' + - '' + + '' + 'Learn to make a custom avatar by opening this link on your desktop.' + '' popup.inputText.visible = true; @@ -56,25 +56,12 @@ MessageBox { popup.inputText.forceActiveFocus(); } - property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' - function showGetWearables(callback, linkCallback) { - popup.button2text = 'AvatarIsland' - popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; + popup.dialogButtons.yesButton.visible = false; popup.button1text = 'CANCEL' popup.titleText = 'Get Wearables' popup.bodyText = 'Get wearables from Marketplace.' + '
' + - 'Wear wearable from Inventory.' + '
' + '
' + - 'Visit “AvatarIsland” to get wearables' - - popup.imageSource = getWearablesUrl; - popup.onButton2Clicked = function() { - popup.close(); - - if (callback) { - callback(); - } - } + 'Wear wearable from Inventory.' popup.onLinkClicked = function(link) { popup.close(); @@ -120,26 +107,13 @@ MessageBox { popup.open(); } - property url getAvatarsUrl: '../../../images/avatarapp/BodyMart.PNG' - function showBuyAvatars(callback, linkCallback) { - popup.button2text = 'BodyMart' - popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; + popup.dialogButtons.yesButton.visible = false; popup.button1text = 'CANCEL' popup.titleText = 'Get Avatars' popup.bodyText = 'Get avatars from Marketplace.' + '
' + - 'Wear avatars in Inventory.' + '
' + '
' + - 'Visit “BodyMart” to get free avatars.' - - popup.imageSource = getAvatarsUrl; - popup.onButton2Clicked = function() { - popup.close(); - - if (callback) { - callback(); - } - } + 'Wear avatars in Inventory.' popup.onLinkClicked = function(link) { popup.close(); diff --git a/interface/resources/qml/hifi/avatarapp/Vector3.qml b/interface/resources/qml/hifi/avatarapp/Vector3.qml index 698123104f..54f8d087e5 100644 --- a/interface/resources/qml/hifi/avatarapp/Vector3.qml +++ b/interface/resources/qml/hifi/avatarapp/Vector3.qml @@ -29,7 +29,7 @@ Row { id: xspinner width: parent.spinboxWidth labelInside: "X:" - backgroundColor: parent.backgroundColor + backgroundColor: activeFocus ? "white" : "lightgray" colorLabelInside: hifi.colors.redHighlight colorScheme: hifi.colorSchemes.light decimals: root.decimals; @@ -43,7 +43,7 @@ Row { id: yspinner width: parent.spinboxWidth labelInside: "Y:" - backgroundColor: parent.backgroundColor + backgroundColor: activeFocus ? "white" : "lightgray" colorLabelInside: hifi.colors.greenHighlight colorScheme: hifi.colorSchemes.light decimals: root.decimals; @@ -57,7 +57,7 @@ Row { id: zspinner width: parent.spinboxWidth labelInside: "Z:" - backgroundColor: parent.backgroundColor + backgroundColor: activeFocus ? "white" : "lightgray" colorLabelInside: hifi.colors.primaryHighlight colorScheme: hifi.colorSchemes.light decimals: root.decimals; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b1b367f2b2..424b4d616d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -778,7 +778,7 @@ Rectangle { lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + "For more information about backing up and restoring content, " + - "" + + "" + "click here to open info on your desktop browser."; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 66113985e8..8ccfceb03c 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -602,7 +602,7 @@ Rectangle { lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + "For more information about backing up and restoring content, " + - "
" + + "" + "click here to open info on your desktop browser."; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index af1e1b5ab9..dbdefc4c79 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -207,7 +207,7 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta if (link === "#privateKeyPath") { Qt.openUrlExternally("file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/'))); } else if (link === "#blockchain") { - Qt.openUrlExternally("https://docs.projectathena.dev/explore/shop.html"); + Qt.openUrlExternally("https://docs.vircadia.dev/explore/shop.html"); } else if (link === "#bank") { if ((Account.metaverseServerURL).toString().indexOf("staging") >= 0) { Qt.openUrlExternally("hifi://hifiqa-master-metaverse-staging"); // So that we can test in staging. diff --git a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml index 2be66442ce..e7a9e0cef2 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml @@ -23,9 +23,9 @@ Rectangle { spacing: 5 Image { - sourceSize.width: 295 - sourceSize.height: 75 - source: "../../../images/about-projectathena.png" + width: 400; height: 73 + fillMode: Image.PreserveAspectFit + source: "../../../images/vircadia-banner.svg" } Item { height: 30; width: 1 } Column { @@ -53,7 +53,7 @@ Rectangle { textFormat: Text.StyledText linkColor: "#00B4EF" color: "white" - text: "Project Athena Github." + text: "Vircadia Github." size: 20 onLinkActivated: { HiFiAbout.openUrl("https:/github.com/kasenvr/project-athena"); @@ -116,7 +116,7 @@ Rectangle { Item { height: 20; width: 1 } RalewayRegular { color: "white" - text: "© 2019 - 2020 Project Athena Contributors." + text: "© 2019-2020 Vircadia contributors." size: 14 } RalewayRegular { diff --git a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml index 3b6502cc98..6e345caaf7 100644 --- a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml +++ b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml @@ -336,8 +336,8 @@ Item { height: parent.height colorScheme: hifi.colorSchemes.dark minimumValue: 0.25 - maximumValue: 1.0 - stepSize: 0.02 + maximumValue: 2.0 + stepSize: 0.05 value: Render.viewportResolutionScale live: true diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index 6727047eb0..ea694ae2d7 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -1,6 +1,7 @@ // // Created by Dante Ruiz on 6/1/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -96,7 +97,8 @@ Item { HifiControls.ImageMessageBox { id: imageMessageBox - anchors.fill: parent + anchors.top: parent.top + anchors.topMargin: 444 z: 2000 imageWidth: 442 imageHeight: 670 @@ -179,7 +181,7 @@ Item { HifiControls.CheckBox { id: checkBox colorScheme: hifi.colorSchemes.dark - text: "show all input devices" + text: "Show all input devices" onClicked: { box.model = inputPlugins(); @@ -332,7 +334,7 @@ Item { anchors.fill: stackView id: controllerPrefereneces objectName: "TabletControllerPreferences" - showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"] + showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion", "Open Sound Control (OSC)"] categoryProperties: { "VR Movement" : { "User real-world height (meters)" : { "anchors.right" : "undefined" }, diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index a9d67fec35..f1275a39fb 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -1,6 +1,7 @@ // // Created by Dante Ruiz on 6/5/17. // Copyright 2017 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -71,6 +72,7 @@ Flickable { property int state: buttonState.disabled property var lastConfiguration: null + property bool isConfiguring: false HifiConstants { id: hifi } @@ -360,9 +362,9 @@ Flickable { RalewayRegular { id: info - text: "See Recommended Tracker Placement" + text: "See Recommended Placement" color: hifi.colors.blueHighlight - size: 10 + size: 12 anchors { left: additional.right leftMargin: 10 @@ -415,7 +417,6 @@ Flickable { id: feetBox width: 15 height: 15 - boxRadius: 7 onClicked: { if (!checked) { @@ -446,7 +447,6 @@ Flickable { id: hipBox width: 15 height: 15 - boxRadius: 7 onClicked: { if (checked) { @@ -486,7 +486,6 @@ Flickable { id: chestBox width: 15 height: 15 - boxRadius: 7 onClicked: { if (checked) { @@ -524,7 +523,6 @@ Flickable { id: shoulderBox width: 15 height: 15 - boxRadius: 7 onClicked: { if (checked) { @@ -823,7 +821,6 @@ Flickable { id: viveInDesktop width: 15 height: 15 - boxRadius: 7 anchors.top: advanceSettings.bottom anchors.topMargin: 5 @@ -840,9 +837,71 @@ Flickable { } } + + HifiControls.CheckBox { + id: eyeTracking + width: 15 + height: 15 + + anchors.top: viveInDesktop.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + + onClicked: { + sendConfigurationSettings(); + } + } + + RalewayBold { + id: eyeTrackingLabel + size: 12 + text: "Use eye tracking (if available)." + color: hifi.colors.lightGrayText + anchors { + left: eyeTracking.right + leftMargin: 5 + verticalCenter: eyeTracking.verticalCenter + } + } + + RalewayRegular { + id: privacyPolicy + text: "Privacy Policy" + color: hifi.colors.blueHighlight + size: 12 + anchors { + left: eyeTrackingLabel.right + leftMargin: 10 + verticalCenter: eyeTrackingLabel.verticalCenter + } + + Rectangle { + id: privacyPolicyUnderline + color: hifi.colors.blueHighlight + width: privacyPolicy.width + height: 1 + anchors { + top: privacyPolicy.bottom + topMargin: 1 + left: privacyPolicy.left + } + visible: false + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true + onEntered: privacyPolicyUnderline.visible = true; + onExited: privacyPolicyUnderline.visible = false; + onClicked: HiFiAbout.openUrl("https://vircadia.com/privacy-policy"); + } + } + + Row { id: outOfRangeDataStrategyRow - anchors.top: viveInDesktop.bottom + anchors.top: eyeTracking.bottom anchors.topMargin: 5 anchors.left: openVrConfiguration.left anchors.leftMargin: leftMargin + 10 @@ -966,6 +1025,8 @@ Flickable { } function displayConfiguration() { + isConfiguring = true; + var settings = InputConfiguration.configurationSettings(openVrConfiguration.pluginName); var configurationType = settings["trackerConfiguration"]; displayTrackerConfiguration(configurationType); @@ -982,6 +1043,7 @@ Flickable { var viveController = settings["handController"]; var desktopMode = settings["desktopMode"]; var hmdDesktopPosition = settings["hmdDesktopTracking"]; + var eyeTrackingEnabled = settings["eyeTrackingEnabled"]; armCircumference.realValue = settings.armCircumference; shoulderWidth.realValue = settings.shoulderWidth; @@ -1004,6 +1066,7 @@ Flickable { viveInDesktop.checked = desktopMode; hmdInDesktop.checked = hmdDesktopPosition; + eyeTracking.checked = eyeTrackingEnabled; outOfRangeDataStrategyComboBox.currentIndex = outOfRangeDataStrategyComboBox.model.indexOf(settings.outOfRangeDataStrategy); initializeButtonState(); @@ -1014,6 +1077,8 @@ Flickable { }; UserActivityLogger.logAction("mocap_ui_open_dialog", data); + + isConfiguring = false; } function displayTrackerConfiguration(type) { @@ -1170,6 +1235,7 @@ Flickable { "shoulderWidth": shoulderWidth.realValue, "desktopMode": viveInDesktop.checked, "hmdDesktopTracking": hmdInDesktop.checked, + "eyeTrackingEnabled": eyeTracking.checked, "outOfRangeDataStrategy": outOfRangeDataStrategyComboBox.model[outOfRangeDataStrategyComboBox.currentIndex] } @@ -1177,6 +1243,10 @@ Flickable { } function sendConfigurationSettings() { + if (isConfiguring) { + // Ignore control value changes during dialog initialization. + return; + } var settings = composeConfigurationSettings(); InputConfiguration.setConfigurationSettings(settings, openVrConfiguration.pluginName); updateCalibrationButton(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a73281e569..cc2aed7f53 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4,6 +4,7 @@ // // Created by Andrzej Kapolka on 5/10/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -65,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +187,7 @@ #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" +#include "scripting/ScreenshareScriptingInterface.h" #include "scripting/AccountServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" @@ -346,7 +349,6 @@ static const QString STANDARD_TO_ACTION_MAPPING_NAME = "Standard to Action"; static const QString NO_MOVEMENT_MAPPING_NAME = "Standard to Action (No Movement)"; static const QString NO_MOVEMENT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard_nomovement.json"; -static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; @@ -655,8 +657,8 @@ private: /**jsdoc *

The Controller.Hardware.Application object has properties representing Interface's state. The property * values are integer IDs, uniquely identifying each output. Read-only.

- *

These states can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject} - * mapping (e.g., using the {@link RouteObject#when} method). Each data value is either 1.0 for "true" or + *

These states can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject} + * mapping (e.g., using the {@link RouteObject#when} method). Each data value is either 1.0 for "true" or * 0.0 for "false".

* * @@ -664,14 +666,21 @@ private: * * * + * Legacy first person camera mode. + * * - * + * Legacy third person camera mode. + * + * + * * * * - * * * @@ -821,7 +830,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { audioDLLPath += "/audioWin7"; } QCoreApplication::addLibraryPath(audioDLLPath); -#endif +#endif QString defaultScriptsOverrideOption = getCmdOption(argc, constArgv, "--defaultScriptsOverride"); @@ -845,6 +854,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { #else DependencyManager::set(true, std::bind(&Application::getUserAgent, qApp)); #endif + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT, defaultScriptsOverrideOption); DependencyManager::set(); @@ -939,8 +949,9 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); PlatformHelper::setup(); - + QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] { QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); @@ -1083,8 +1094,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { // identify gpu as early as possible to help identify OpenGL initialization errors. auto gpuIdent = GPUIdent::getInstance(); - setCrashAnnotation("gpu_name", gpuIdent->getName().toStdString()); - setCrashAnnotation("gpu_driver", gpuIdent->getDriver().toStdString()); + setCrashAnnotation("sentry[contexts][gpu][name]", gpuIdent->getName().toStdString()); + setCrashAnnotation("sentry[contexts][gpu][version]", gpuIdent->getDriver().toStdString()); setCrashAnnotation("gpu_memory", std::to_string(gpuIdent->getMemory())); } @@ -1130,7 +1141,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-SemiBold.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-Regular.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-Medium.ttf"); - _window->setWindowTitle("Project Athena"); + _window->setWindowTitle("Vircadia"); Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us @@ -1153,7 +1164,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId()); deadlockWatchdogThread->start(); - // Pause the deadlock watchdog when we sleep, or it might + // Pause the deadlock watchdog when we sleep, or it might // trigger a false positive when we wake back up auto platformHelper = PlatformHelper::instance(); @@ -1340,6 +1351,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo #endif connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle); + auto domainAccountManager = DependencyManager::get(); + connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(), + &DialogsManager::showDomainLoginDialog); + connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, + &Application::updateWindowTitle); + // ####### TODO: Connect any other signals from domainAccountManager. + // use our MyAvatar position and quat for address manager path addressManager->setPositionGetter([] { auto avatarManager = DependencyManager::get(); @@ -1563,7 +1581,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Do not show login dialog if requested not to on the command line QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY); int index = arguments().indexOf(hifiNoLoginCommandLineKey); - if (index != -1) { + if (index != -1 || _disableLoginScreen) { resumeAfterLoginDialogActionTaken(); return; } @@ -2606,7 +2624,7 @@ QString Application::getUserAgent() { return userAgent; } - QString userAgent = "Mozilla/5.0 (HighFidelityInterface/" + BuildInfo::VERSION + "; " + QString userAgent = NetworkingConstants::VIRCADIA_USER_AGENT + "/" + BuildInfo::VERSION + "; " + QSysInfo::productType() + " " + QSysInfo::productVersion() + ")"; auto formatPluginName = [](QString name) -> QString { return name.trimmed().replace(" ", "-"); }; @@ -2793,6 +2811,7 @@ void Application::cleanupBeforeQuit() { if (!keepMeLoggedIn) { DependencyManager::get()->removeAccountFromFile(); } + // ####### TODO _displayPlugin.reset(); PluginManager::getInstance()->shutdown(); @@ -2908,6 +2927,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::get()->cleanup(); @@ -3141,6 +3161,7 @@ extern void setupPreferences(); static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false); #endif +// ####### TODO void Application::showLoginScreen() { #if !defined(DISABLE_QML) auto accountManager = DependencyManager::get(); @@ -3156,7 +3177,7 @@ void Application::showLoginScreen() { QJsonObject loginData = {}; loginData["action"] = "login dialog popped up"; UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - _window->setWindowTitle("Project Athena"); + _window->setWindowTitle("Vircadia"); } else { resumeAfterLoginDialogActionTaken(); } @@ -3167,7 +3188,7 @@ void Application::showLoginScreen() { #endif } -static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://content.highfidelity.com/Experiences/Releases" }; +static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://cdn.vircadia.com/community-apps/applications" }; void Application::initializeUi() { @@ -3186,14 +3207,16 @@ void Application::initializeUi() { safeURLS += settingsSafeURLS; // END PULL SAFEURLS FROM INTERFACE.JSON Settings - - bool isInWhitelist = false; // assume unsafe - for (const auto& str : safeURLS) { - if (!str.isEmpty() && str.endsWith(".qml") && url.toString().endsWith(".qml") && - url.toString().startsWith(str)) { - qCDebug(interfaceapp) << "Found matching url!" << url.host(); - isInWhitelist = true; - return true; + + if (AUTHORIZED_EXTERNAL_QML_SOURCE.isParentOf(url)) { + return true; + } else { + for (const auto& str : safeURLS) { + if (!str.isEmpty() && str.endsWith(".qml") && url.toString().endsWith(".qml") && + url.toString().startsWith(str)) { + qCDebug(interfaceapp) << "Found matching url!" << url.host(); + return true; + } } } @@ -3401,6 +3424,10 @@ void Application::initializeUi() { setIsInterstitialMode(true); + +#if defined(DISABLE_QML) && defined(Q_OS_LINUX) + resumeAfterLoginDialogActionTaken(); +#endif } @@ -3439,7 +3466,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Users", DependencyManager::get().data()); surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get().data()); - + surfaceContext->setContextProperty("Screenshare", DependencyManager::get().data()); surfaceContext->setContextProperty("Camera", &_myCamera); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) @@ -3545,6 +3572,7 @@ void Application::userKickConfirmation(const QUuid& nodeID) { } void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) { + surfaceContext->setContextProperty("Screenshare", DependencyManager::get().data()); surfaceContext->setContextProperty("Users", DependencyManager::get().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get().data()); surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get().data()); @@ -3773,9 +3801,8 @@ void Application::setPreferredCursor(const QString& cursorName) { if (_displayPlugin && _displayPlugin->isHmd()) { _preferredCursor.set(cursorName.isEmpty() ? DEFAULT_CURSOR_NAME : cursorName); - } - else { - _preferredCursor.set(cursorName.isEmpty() ? Cursor::Manager::getIconName(Cursor::Icon::SYSTEM) : cursorName); + } else { + _preferredCursor.set(cursorName.isEmpty() ? Cursor::Manager::getIconName(Cursor::Icon::SYSTEM) : cursorName); } showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get())); @@ -3842,6 +3869,11 @@ void Application::showHelp() { //InfoView::show(INFO_HELP_PATH, false, queryString.toString()); } +void Application::gotoTutorial() { + const QString TUTORIAL_ADDRESS = "file:///~/serverless/tutorial.json"; + DependencyManager::get()->handleLookupString(TUTORIAL_ADDRESS); +} + void Application::resizeEvent(QResizeEvent* event) { resizeGL(); } @@ -3919,12 +3951,15 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers; - // when --url in command line, teleport to location - const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; - int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); QString addressLookupString; - if (urlIndex != -1) { - QUrl url(arguments().value(urlIndex + 1)); + + // when --url in command line, teleport to location + QCommandLineParser parser; + QCommandLineOption urlOption("url", "", "value"); + parser.addOption(urlOption); + parser.parse(arguments()); + if (parser.isSet(urlOption)) { + QUrl url = QUrl(parser.value(urlOption)); if (url.scheme() == URL_SCHEME_HIFIAPP) { Setting::Handle("startUpApp").set(url.path()); } else { @@ -3962,7 +3997,7 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { DependencyManager::get()->loadSettings(addressLookupString); sentTo = SENT_TO_PREVIOUS_LOCATION; } - + UserActivityLogger::getInstance().logAction("startup_sent_to", { { "sent_to", sentTo }, { "sandbox_is_running", sandboxIsRunning }, @@ -4050,7 +4085,7 @@ std::map Application::prepareServerlessDomainContents(QUrl dom bool success = tmpTree->readFromByteArray(domainURL.toString(), data); if (success) { tmpTree->reaverageOctreeElements(); - tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); + tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), "domain", 0, 0, 0); } std::map namedPaths = tmpTree->getNamedPaths(); @@ -4197,7 +4232,7 @@ bool Application::event(QEvent* event) { idle(); #ifdef DEBUG_EVENT_QUEUE_DEPTH - // The event queue may very well grow beyond 400, so + // The event queue may very well grow beyond 400, so // this code should only be enabled on local builds { int count = ::hifi::qt::getEventQueueSize(QThread::currentThread()); @@ -4236,7 +4271,7 @@ bool Application::event(QEvent* event) { { //testing to see if we can set focus when focus is not set to root window. _glWidget->activateWindow(); _glWidget->setFocus(); - return true; + return true; } case QEvent::TouchBegin: @@ -4880,6 +4915,9 @@ void Application::touchEndEvent(QTouchEvent* event) { } void Application::touchGestureEvent(QGestureEvent* event) { + if (_keyboardMouseDevice->isActive()) { + _keyboardMouseDevice->touchGestureEvent(event); + } if (_touchscreenDevice && _touchscreenDevice->isActive()) { _touchscreenDevice->touchGestureEvent(event); } @@ -5218,7 +5256,7 @@ void Application::idle() { } } #endif - + checkChangeCursor(); #if !defined(DISABLE_QML) @@ -5471,7 +5509,7 @@ void Application::loadSettings() { RenderScriptingInterface::getInstance()->loadSettings(); // Setup the PerformanceManager which will enforce the several settings to match the Preset - // On the first run, the Preset is evaluated from the + // On the first run, the Preset is evaluated from the getPerformanceManager().setupPerformancePresetSettings(_firstRun.get()); // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings @@ -5517,9 +5555,9 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse _entityClipboard->withWriteLock([&] { _entityClipboard->eraseAllOctreeElements(); - // FIXME: readFromURL() can take over the main event loop which may cause problems, especially if downloading the JSON + // FIXME: readFromURL() can take over the main event loop which may cause problems, especially if downloading the JSON // from the Web. - success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId); + success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId, true); if (success) { _entityClipboard->reaverageOctreeElements(); } @@ -5527,8 +5565,8 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse return success; } -QVector Application::pasteEntities(float x, float y, float z) { - return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z); +QVector Application::pasteEntities(const QString& entityHostType, float x, float y, float z) { + return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), entityHostType, x, y, z); } void Application::init() { @@ -5670,6 +5708,7 @@ void Application::resumeAfterLoginDialogActionTaken() { return; } +#if !defined(DISABLE_QML) if (!isHMDMode() && getDesktopTabletBecomesToolbarSetting()) { auto toolbar = DependencyManager::get()->getToolbar("com.highfidelity.interface.toolbar.system"); toolbar->writeProperty("visible", true); @@ -5679,6 +5718,7 @@ void Application::resumeAfterLoginDialogActionTaken() { } updateSystemTabletMode(); +#endif { auto userInputMapper = DependencyManager::get(); @@ -5829,12 +5869,7 @@ void Application::centerUI() { void Application::cycleCamera() { auto menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { - - menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); - - } else if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) { + if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) { menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, false); menu->setIsOptionChecked(MenuOption::LookAtCamera, true); @@ -5842,12 +5877,16 @@ void Application::cycleCamera() { } else if (menu->isOptionChecked(MenuOption::LookAtCamera)) { menu->setIsOptionChecked(MenuOption::LookAtCamera, false); - menu->setIsOptionChecked(MenuOption::SelfieCamera, true); + if (menu->getActionForOption(MenuOption::SelfieCamera)->isVisible()) { + menu->setIsOptionChecked(MenuOption::SelfieCamera, true); + } else { + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); + } } else if (menu->isOptionChecked(MenuOption::SelfieCamera)) { menu->setIsOptionChecked(MenuOption::SelfieCamera, false); - menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); } cameraMenuChanged(); // handle the menu change @@ -6273,7 +6312,7 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); - if (deltaTime > FLT_EPSILON) { + if (deltaTime > FLT_EPSILON && userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z) == 0.0f) { myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); myAvatar->setDriveKey(MyAvatar::DELTA_PITCH, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_PITCH)); @@ -7042,19 +7081,23 @@ void Application::updateWindowTitle() const { auto nodeList = DependencyManager::get(); auto accountManager = DependencyManager::get(); + auto domainAccountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); + bool isMetaverseLoggedIn = accountManager->isLoggedIn(); + bool isDomainLoggedIn = domainAccountManager->isLoggedIn(); + QString authedDomain = domainAccountManager->getAuthedDomain(); - QString buildVersion = " - Project Athena v0.86.0 K2 - " + QString buildVersion = " - Vircadia - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + " " + applicationVersion(); - QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)"; - QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" : nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; - QString username = accountManager->getAccountInfo().getUsername(); - setCrashAnnotation("username", username.toStdString()); + QString metaverseUsername = accountManager->getAccountInfo().getUsername(); + QString domainUsername = domainAccountManager->getUsername(); + + setCrashAnnotation("sentry[user][username]", metaverseUsername.toStdString()); QString currentPlaceName; if (isServerlessMode()) { @@ -7070,8 +7113,22 @@ void Application::updateWindowTitle() const { } } - QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) - + currentPlaceName + connectionStatus + loginStatus + buildVersion; + QString metaverseDetails; + if (isMetaverseLoggedIn) { + metaverseDetails = "Metaverse: Logged in as " + metaverseUsername; + } else { + metaverseDetails = "Metaverse: Not Logged In"; + } + + QString domainDetails; + if (currentPlaceName == authedDomain && isDomainLoggedIn) { + domainDetails = "Domain: Logged in as " + domainUsername; + } else { + domainDetails = "Domain: Not Logged In"; + } + + QString title = QString() + currentPlaceName + connectionStatus + " (" + metaverseDetails + ") (" + domainDetails + ")" + + buildVersion; #ifndef WIN32 // crashes with vs2013/win32 @@ -7329,6 +7386,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Camera", &_myCamera); + scriptEngine->registerGlobalObject("Screenshare", DependencyManager::get().data()); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) scriptEngine->registerGlobalObject("SpeechRecognizer", DependencyManager::get().data()); @@ -7599,7 +7657,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { QUrl scriptURL { scriptFilenameOrURL }; - if (scriptURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) { + if (scriptURL.host().endsWith(NetworkingConstants::MARKETPLACE_CDN_HOSTNAME)) { int startIndex = shortName.lastIndexOf('/') + 1; int endIndex = shortName.lastIndexOf('?'); shortName = shortName.mid(startIndex, endIndex - startIndex); @@ -7630,7 +7688,7 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(networkRequest); int requestNumber = ++_avatarAttachmentRequest; connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() { @@ -7722,12 +7780,12 @@ bool Application::askToReplaceDomainContent(const QString& url) { const int MAX_CHARACTERS_PER_LINE = 90; if (DependencyManager::get()->getThisNodeCanReplaceContent()) { QUrl originURL { url }; - if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) { + if (originURL.host().endsWith(NetworkingConstants::MARKETPLACE_CDN_HOSTNAME)) { // Create a confirmation dialog when this call is made static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. " "If you want to save what you have now, create a backup before proceeding. For more information about backing up " "and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) + - "\nhttps://docs.projectathena.dev/host/maintain-domain/backup-domain.html"; + "\nhttps://docs.vircadia.dev/host/maintain-domain/backup-domain.html"; ModalDialogListener* dig = OffscreenUi::asyncQuestion("Are you sure you want to replace this domain's content set?", infoText, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); @@ -8715,7 +8773,7 @@ bool Application::isThrottleRendering() const { bool Application::hasFocus() const { bool result = (QApplication::activeWindow() != nullptr); - + #if defined(Q_OS_WIN) // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't // take user focus away from their current window. So also check whether the application is the user's current foreground diff --git a/interface/src/Application.h b/interface/src/Application.h index 198f5ef7cf..f42696cda0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -223,9 +223,7 @@ public: bool getPreferStylusOverLaser() { return _preferStylusOverLaserSetting.get(); } void setPreferStylusOverLaser(bool value); - // FIXME: Remove setting completely or make available through JavaScript API? - //bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); } - bool getPreferAvatarFingerOverStylus() { return false; } + bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); } void setPreferAvatarFingerOverStylus(bool value); bool getMiniTabletEnabled() { return _miniTabletEnabledSetting.get(); } @@ -377,7 +375,7 @@ signals: void awayStateWhenFocusLostInVRChanged(bool enabled); public slots: - QVector pasteEntities(float x, float y, float z); + QVector pasteEntities(const QString& entityHostType, float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, float x, float y, float z, float scale); bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1); @@ -427,6 +425,7 @@ public slots: #endif static void showHelp(); + static void gotoTutorial(); void cycleCamera(); void cameraModeChanged(); @@ -734,6 +733,7 @@ private: GraphicsEngine _graphicsEngine; void updateRenderArgs(float deltaTime); + bool _disableLoginScreen { true }; Overlays _overlays; ApplicationOverlay _applicationOverlay; diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 6f6a93ff19..9204cd7514 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -154,7 +154,7 @@ void AvatarBookmarks::deleteBookmark() { void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto currentAvatarEntities = myAvatar->getAvatarEntityData(); + auto currentAvatarEntities = myAvatar->getAvatarEntityDataNonDefault(); std::set newAvatarEntities; // Update or add all the new avatar entities @@ -187,7 +187,7 @@ void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { * @property {number} avatarScale - The target scale of the avatar. * @property {Array>} [avatarEntites] - The avatar entities included with the * bookmark. - * @property {MyAvatar.AttachmentData[]} [attachments] - The attachments included with the bookmark. + * @property {AttachmentData[]} [attachments] - The attachments included with the bookmark. *

Deprecated: Use avatar entities instead. */ @@ -296,7 +296,7 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { if (entityTree) { QScriptEngine scriptEngine; - auto avatarEntities = myAvatar->getAvatarEntityData(); + auto avatarEntities = myAvatar->getAvatarEntityDataNonDefault(); for (auto entityID : avatarEntities.keys()) { auto entity = entityTree->findEntityByID(entityID); if (!entity || !isWearableEntity(entity)) { diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index 33d9fddc1b..070015f05b 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -14,8 +14,10 @@ #include "Application.h" #include "ui/DialogsManager.h" +#include #include #include +#include #include #include @@ -34,6 +36,10 @@ void ConnectionMonitor::init() { connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer); connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer); connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState); + auto accountManager = DependencyManager::get(); + connect(accountManager.data(), &AccountManager::loginComplete, this, &ConnectionMonitor::startTimer); + auto domainAccountManager = DependencyManager::get(); + connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, &ConnectionMonitor::startTimer); _timer.setSingleShot(true); if (!domainHandler.isConnected()) { diff --git a/interface/src/CrashHandler_Crashpad.cpp b/interface/src/CrashHandler_Crashpad.cpp index d1b5103990..900a296955 100644 --- a/interface/src/CrashHandler_Crashpad.cpp +++ b/interface/src/CrashHandler_Crashpad.cpp @@ -84,10 +84,9 @@ bool startCrashHandler(std::string appPath) { std::vector arguments; std::map annotations; - annotations["token"] = BACKTRACE_TOKEN; - annotations["format"] = "minidump"; - annotations["version"] = BuildInfo::VERSION.toStdString(); - annotations["build_number"] = BuildInfo::BUILD_NUMBER.toStdString(); + annotations["sentry[release]"] = BACKTRACE_TOKEN; + annotations["sentry[contexts][app][app_version]"] = BuildInfo::VERSION.toStdString(); + annotations["sentry[contexts][app][app_build]"] = BuildInfo::BUILD_NUMBER.toStdString(); annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING.toStdString(); auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index f7be71e053..2a131d991f 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -20,7 +20,7 @@ class FancyCamera : public Camera { /**jsdoc * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * * @namespace Camera * diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp index 33328f54cc..0f1c8876a9 100644 --- a/interface/src/InterfaceParentFinder.cpp +++ b/interface/src/InterfaceParentFinder.cpp @@ -45,10 +45,6 @@ SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& s success = true; return parent; } - if (parentID == AVATAR_SELF_ID) { - success = true; - return avatarManager->getMyAvatar(); - } success = false; return parent; diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 4708deb61b..16b0529b05 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -66,16 +66,62 @@ class AABox; /**jsdoc - * The LOD class manages your Level of Detail functions within Interface. + * The LODManager API manages the Level of Detail displayed in Interface. If the LOD is being automatically + * adjusted, the LOD is decreased if the measured frame rate is lower than the target FPS, and increased if the measured frame + * rate is greater than the target FPS. + * * @namespace LODManager * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {number} presentTime Read-only. - * @property {number} engineRunTime Read-only. - * @property {number} gpuTime Read-only. + * @property {LODManager.WorldDetailQuality} worldDetailQuality - The quality of the rendered world detail. + *

Setting this value updates the current desktop or HMD target LOD FPS.

+ * @property {number} lodQualityLevel - Not used. + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} automaticLODAdjust - true to automatically adjust the LOD, false to manually + * adjust it. + * + * @property {number} engineRunTime - The time spent in the "render" thread to produce the most recent frame, in ms. + * Read-only. + * @property {number} batchTime - The time spent in the "present" thread processing the batches of the most recent frame, in ms. + * Read-only. + * @property {number} presentTime - The time spent in the "present" thread between the last buffer swap, i.e., the total time + * to submit the most recent frame, in ms. + * Read-only. + * @property {number} gpuTime - The time spent in the GPU executing the most recent frame, in ms. + * Read-only. + * + * @property {number} nowRenderTime - The current, instantaneous time spend to produce frames, in ms. This is the worst of + * engineRunTime, batchTime, presentTime, and gpuTime. + * Read-only. + * @property {number} nowRenderFPS - The current, instantaneous frame rate, in Hz. + * Read-only. + * + * @property {number} smoothScale - The amount of smoothing applied to calculate smoothRenderTime and + * smoothRenderFPS. + * @property {number} smoothRenderTime - The average time spend to produce frames, in ms. + * Read-only. + * @property {number} smoothRenderFPS - The average frame rate, in Hz. + * Read-only. + * + * @property {number} lodTargetFPS - The target LOD FPS per the current desktop or HMD display mode, capped by the target + * refresh rate set by the {@link Performance} API. + * Read-only. + + * @property {number} lodAngleDeg - The minimum angular dimension (relative to the camera position) of an entity in order for + * it to be rendered, in degrees. The angular dimension is calculated as a sphere of radius half the diagonal of the + * entity's AA box. + * + * @property {number} pidKp - Not used. + * @property {number} pidKi - Not used. + * @property {number} pidKd - Not used. + * @property {number} pidKv - Not used. + * @property {number} pidOp - Not used. Read-only. + * @property {number} pidOi - Not used. Read-only. + * @property {number} pidOd - Not used. Read-only. + * @property {number} pidO - Not used. Read-only. */ class LODManager : public QObject, public Dependency { Q_OBJECT @@ -117,83 +163,96 @@ class LODManager : public QObject, public Dependency { public: /**jsdoc + * Sets whether the LOD should be automatically adjusted. * @function LODManager.setAutomaticLODAdjust - * @param {boolean} value + * @param {boolean} value - true to automatically adjust the LOD, false to manually adjust it. */ Q_INVOKABLE void setAutomaticLODAdjust(bool value); /**jsdoc + * Gets whether the LOD is being automatically adjusted. * @function LODManager.getAutomaticLODAdjust - * @returns {boolean} + * @returns {boolean} true if the LOD is being automatically adjusted, false if it is being + * manually adjusted. */ Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; } /**jsdoc + * Sets the target desktop LOD FPS. * @function LODManager.setDesktopLODTargetFPS - * @param {number} value + * @param {number} value - The target desktop LOD FPS, in Hz. */ Q_INVOKABLE void setDesktopLODTargetFPS(float value); /**jsdoc + * Gets the target desktop LOD FPS. * @function LODManager.getDesktopLODTargetFPS - * @returns {number} + * @returns {number} The target desktop LOD FPS, in Hz. */ Q_INVOKABLE float getDesktopLODTargetFPS() const; /**jsdoc + * Sets the target HMD LOD FPS. * @function LODManager.setHMDLODTargetFPS - * @param {number} value + * @param {number} value - The target HMD LOD FPS, in Hz. */ Q_INVOKABLE void setHMDLODTargetFPS(float value); /**jsdoc + * Gets the target HMD LOD FPS. + * The target FPS in HMD mode. The LOD is adjusted to ... * @function LODManager.getHMDLODTargetFPS - * @returns {number} + * @returns {number} The target HMD LOD FPS, in Hz. */ Q_INVOKABLE float getHMDLODTargetFPS() const; // User Tweakable LOD Items + /**jsdoc + * Gets a text description of the current level of detail rendered. * @function LODManager.getLODFeedbackText - * @returns {string} + * @returns {string} A text description of the current level of detail rendered. + * @example
+ * print("You can currently see: " + LODManager.getLODFeedbackText()); */ Q_INVOKABLE QString getLODFeedbackText(); /**jsdoc * @function LODManager.setOctreeSizeScale - * @param {number} sizeScale - * @deprecated This function is deprecated and will be removed. Use the {@link LODManager.lodAngleDeg} property instead. + * @param {number} sizeScale - The octree size scale. + * @deprecated This function is deprecated and will be removed. Use the lodAngleDeg property instead. */ Q_INVOKABLE void setOctreeSizeScale(float sizeScale); /**jsdoc * @function LODManager.getOctreeSizeScale - * @returns {number} - * @deprecated This function is deprecated and will be removed. Use the {@link LODManager.lodAngleDeg} property instead. + * @returns {number} The octree size scale. + * @deprecated This function is deprecated and will be removed. Use the lodAngleDeg property instead. */ Q_INVOKABLE float getOctreeSizeScale() const; /**jsdoc * @function LODManager.setBoundaryLevelAdjust - * @param {number} boundaryLevelAdjust + * @param {number} boundaryLevelAdjust - The boundary level adjust factor. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); /**jsdoc * @function LODManager.getBoundaryLevelAdjust - * @returns {number} + * @returns {number} The boundary level adjust factor. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } /**jsdoc - * @function LODManager.getLODTargetFPS - * @returns {number} - */ + * The target LOD FPS per the current desktop or HMD display mode, capped by the target refresh rate. + * @function LODManager.getLODTargetFPS + * @returns {number} The target LOD FPS, in Hz. + */ Q_INVOKABLE float getLODTargetFPS() const; @@ -249,19 +308,41 @@ public: signals: /**jsdoc + * Not triggered. * @function LODManager.LODIncreased * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. */ void LODIncreased(); /**jsdoc + * Not triggered. * @function LODManager.LODDecreased * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. */ void LODDecreased(); + /**jsdoc + * Triggered when whether or not the LOD is being automatically adjusted changes. + * @function LODManager.autoLODChanged + * @returns {Signal} + */ void autoLODChanged(); + + /**jsdoc + * Triggered when the lodQualityLevel property value changes. + * @function LODManager.lodQualityLevelChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ void lodQualityLevelChanged(); + + /**jsdoc + * Triggered when the world detail quality changes. + * @function LODManager.worldDetailQualityChanged + * @returns {Signal} + */ void worldDetailQualityChanged(); private: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index fbb69842bd..64cdf98239 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -9,6 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// For happ(ier) development of QML, use these two things: +// This forces QML files to be pulled from the source as you edit it: set environment variable HIFI_USE_SOURCE_TREE_RESOURCES=1 +// Use this to live reload: DependencyManager::get()->clearCache(); + #include "Menu.h" #include #include @@ -29,6 +33,8 @@ #include #include #include +#include +#include #include "Application.h" #include "AccountManager.h" @@ -82,6 +88,13 @@ Menu::Menu() { dialogsManager.data(), &DialogsManager::toggleLoginDialog); } + auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In"); + connect(domainLogin, &QAction::triggered, [] { + auto dialogsManager = DependencyManager::get(); + dialogsManager->setDomainLoginState(); + dialogsManager->showDomainLoginDialog(); + }); + // File > Quit addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole); @@ -221,9 +234,9 @@ Menu::Menu() { MenuWrapper* startupLocationMenu = navigateMenu->addMenu(MenuOption::StartUpLocation); QActionGroup* startupLocatiopnGroup = new QActionGroup(startupLocationMenu); startupLocatiopnGroup->setExclusive(true); - startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0, + startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0, false)); - startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0, + startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0, true)); // Settings menu ---------------------------------- @@ -286,13 +299,13 @@ Menu::Menu() { hmd->toggleShouldShowTablet(); } }); - + // Settings > Entity Script / QML Whitelist action = addActionToQMenuAndActionHash(settingsMenu, "Entity Script / QML Whitelist"); connect(action, &QAction::triggered, [] { auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); auto hmd = DependencyManager::get(); - + tablet->pushOntoStack("hifi/dialogs/security/EntityScriptQMLWhitelist.qml"); if (!hmd->getShouldShowTablet()) { @@ -308,10 +321,10 @@ Menu::Menu() { // Developer menu ---------------------------------- MenuWrapper* developerMenu = addMenu("Developer", "Developer"); - + // Developer > Scripting >>> MenuWrapper* scriptingOptionsMenu = developerMenu->addMenu("Scripting"); - + // Developer > Scripting > Console... addActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, DependencyManager::get().data(), @@ -326,7 +339,7 @@ Menu::Menu() { defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js"); DependencyManager::get()->loadScript(defaultScriptsLoc.toString()); }); - + // Developer > Scripting > Entity Script Server Log auto essLogAction = addActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::EntityScriptServerLog, 0, qApp, SLOT(toggleEntityScriptServerLogDialog())); @@ -346,7 +359,7 @@ Menu::Menu() { // Developer > Scripting > Verbose Logging addCheckableActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::VerboseLogging, 0, false, qApp, SLOT(updateVerboseLogging())); - + // Developer > Scripting > Enable Speech Control API #if defined(Q_OS_MAC) || defined(Q_OS_WIN) auto speechRecognizer = DependencyManager::get(); @@ -358,20 +371,20 @@ Menu::Menu() { UNSPECIFIED_POSITION); connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif - + // Developer > UI >>> MenuWrapper* uiOptionsMenu = developerMenu->addMenu("UI"); action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::DesktopTabletToToolbar, 0, qApp->getDesktopTabletBecomesToolbarSetting()); - + // Developer > UI > Show Overlays addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Overlays, 0, true); - + // Developer > UI > Desktop Tablet Becomes Toolbar connect(action, &QAction::triggered, [action] { qApp->setDesktopTabletBecomesToolbarSetting(action->isChecked()); }); - + // Developer > UI > HMD Tablet Becomes Toolbar action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::HMDTabletToToolbar, 0, qApp->getHmdTabletBecomesToolbarSetting()); @@ -595,14 +608,32 @@ Menu::Menu() { QString("hifi/tablet/TabletNetworkingPreferences.qml"), "NetworkingPreferencesDialog"); }); addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); - addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCache, 0, - DependencyManager::get().data(), SLOT(clearCache())); + + action = addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCaches); + connect(action, &QAction::triggered, [] { + // The following caches are cleared immediately + DependencyManager::get()->clearCache(); +#ifndef Q_OS_ANDROID + FileTypeProfile::clearCache(); + HFWebEngineProfile::clearCache(); +#endif + + // Clear the KTX cache on the next restart. It can't be cleared immediately because its files might be in use. + Setting::Handle(KTXCache::SETTING_VERSION_NAME, KTXCache::INVALID_VERSION).set(KTXCache::INVALID_VERSION); + }); + addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, - false, + true, &UserActivityLogger::getInstance(), SLOT(disable(bool))); + addCheckableActionToQMenuAndActionHash(networkMenu, + MenuOption::DisableCrashLogger, + 0, + true, + &UserActivityLogger::getInstance(), + SLOT(crashMonitorDisable(bool))); addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0, qApp, SLOT(loadDomainConnectionDialog())); @@ -688,7 +719,7 @@ Menu::Menu() { result = QProcessEnvironment::systemEnvironment().contains(HIFI_SHOW_DEVELOPER_CRASH_MENU); if (result) { MenuWrapper* crashMenu = developerMenu->addMenu("Crash"); - + // Developer > Crash > Display Crash Options addCheckableActionToQMenuAndActionHash(crashMenu, MenuOption::DisplayCrashOptions, 0, true); @@ -727,7 +758,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOnShutdown, 0, qApp, SLOT(crashOnShutdown())); } - + // Developer > Show Statistics addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats, 0, true); @@ -768,46 +799,48 @@ Menu::Menu() { // Help/Application menu ---------------------------------- MenuWrapper * helpMenu = addMenu("Help"); - // Help > About Project Athena - action = addActionToQMenuAndActionHash(helpMenu, "About Project Athena"); + // Help > About Vircadia + action = addActionToQMenuAndActionHash(helpMenu, "About Vircadia"); connect(action, &QAction::triggered, [] { qApp->showDialog(QString("hifi/dialogs/AboutDialog.qml"), QString("hifi/dialogs/TabletAboutDialog.qml"), "AboutDialog"); }); helpMenu->addSeparator(); - // Help > Athena Docs + // Help > Vircadia Docs action = addActionToQMenuAndActionHash(helpMenu, "Online Documentation"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://docs.projectathena.dev/")); + QDesktopServices::openUrl(NetworkingConstants::HELP_DOCS_URL); }); - // Help > Athena Forum + // Help > Vircadia Forum /* action = addActionToQMenuAndActionHash(helpMenu, "Online Forums"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://forums.highfidelity.com/")); + QDesktopServices::openUrl(NetworkingConstants::HELP_FORUM_URL)); }); */ // Help > Scripting Reference action = addActionToQMenuAndActionHash(helpMenu, "Online Script Reference"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://apidocs.projectathena.dev/")); + QDesktopServices::openUrl(NetworkingConstants::HELP_SCRIPTING_REFERENCE_URL); }); addActionToQMenuAndActionHash(helpMenu, "Controls Reference", 0, qApp, SLOT(showHelp())); + addActionToQMenuAndActionHash(helpMenu, "Tutorial", 0, qApp, SLOT(gotoTutorial())); + helpMenu->addSeparator(); // Help > Release Notes action = addActionToQMenuAndActionHash(helpMenu, "Release Notes"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://docs.projectathena.dev/release-notes.html")); + QDesktopServices::openUrl(NetworkingConstants::HELP_RELEASE_NOTES_URL); }); // Help > Report a Bug! action = addActionToQMenuAndActionHash(helpMenu, "Report a Bug!"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://github.com/kasenvr/project-athena/issues")); + QDesktopServices::openUrl(NetworkingConstants::HELP_BUG_REPORT_URL); }); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 1d6c010a05..a3da179ad3 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -56,7 +56,7 @@ namespace MenuOption { const QString CalibrateCamera = "Calibrate Camera"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; - const QString ClearDiskCache = "Clear Disk Cache"; + const QString ClearDiskCaches = "Clear Disk Caches (requires restart)"; const QString Collisions = "Collisions"; const QString Connexion = "Activate 3D Connexion Devices"; const QString Console = "Console..."; @@ -86,6 +86,7 @@ namespace MenuOption { const QString DeleteAvatarEntitiesBookmark = "Delete Avatar Entities Bookmark"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; + const QString DisableCrashLogger = "Disable Crash Reporter"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; const QString DisplayCrashOptions = "Display Crash Options"; @@ -118,7 +119,6 @@ namespace MenuOption { const QString FixGaze = "Fix Gaze (no saccade)"; const QString Forward = "Forward"; const QString FrameTimer = "Show Timer"; - const QString FullscreenMirror = "Mirror"; const QString Help = "Help..."; const QString HomeLocation = "Home "; const QString IncreaseAvatarSize = "Increase Avatar Size"; diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index db74b34d91..c145d40549 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -24,7 +24,7 @@ #include "ModelPropertiesDialog.h" #include "InterfaceLogging.h" -static const int MAX_TEXTURE_SIZE = 1024; +static const int MAX_TEXTURE_SIZE = 8192; void copyDirectoryContent(QDir& from, QDir& to) { for (auto entry : from.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | diff --git a/interface/src/PerformanceManager.cpp b/interface/src/PerformanceManager.cpp index a7b9eff7cc..47e4f0612b 100644 --- a/interface/src/PerformanceManager.cpp +++ b/interface/src/PerformanceManager.cpp @@ -27,7 +27,7 @@ void PerformanceManager::setupPerformancePresetSettings(bool evaluatePlatformTie // If evaluatePlatformTier, evalute the Platform Tier and assign the matching Performance profile by default. // A bunch of Performance, Simulation and Render settings will be set to a matching default value from this - // Here is the mapping between pelatformTIer and performance profile + // Here is the mapping between platformTier and performance profile const std::array platformToPerformancePresetMap = { { PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp index da6c5cbd37..bb4ec42e41 100644 --- a/interface/src/RefreshRateManager.cpp +++ b/interface/src/RefreshRateManager.cpp @@ -17,12 +17,58 @@ static const int VR_TARGET_RATE = 90; +/**jsdoc + *

Refresh rate profile.

+ *
CameraFirstPersonnumbernumberThe camera is in first-person mode. - *
CameraFirstPersonLookAtnumbernumberThe camera is in first-person mode. + * Default first person camera mode.
CameraThirdPersonnumbernumberThe camera is in third-person mode. - *
CameraFSMnumbernumberThe camera is in full screen mirror mode.
CameraLookAtnumbernumberThe camera is in third-person mode. + * Default third person camera mode.
CameraFSMnumbernumberThe camera is in full screen mirror mode. + * Legacy "look at myself" behavior.
CameraSelfienumbernumberThe camera is in selfie mode. + * Default "look at myself" camera mode.
CameraIndependentnumbernumberThe camera is in independent mode.
CameraEntitynumbernumberThe camera is in entity mode.
InHMDnumbernumberThe user is in HMD mode.
AdvancedMovementnumbernumberAdvanced movement (walking) controls are + *
AdvancedMovementnumbernumberAdvanced movement (walking) controls are * enabled.
StrafeEnablednumbernumberStrafing is enabled
LeftHandDominantnumbernumberDominant hand set to left.
Report the current level of detail rendered.
+ * + * + * + * + * + * + * + *
ValueDescription
"Eco"Low refresh rate, which is reduced when Interface doesn't have focus or is + * minimized.
"Interactive"Medium refresh rate, which is reduced when Interface doesn't have focus or is + * minimized.
"Realtime"High refresh rate, even when Interface doesn't have focus or is minimized. + *
+ * @typedef {string} RefreshRateProfileName + */ static const std::array REFRESH_RATE_PROFILE_TO_STRING = { { "Eco", "Interactive", "Realtime" } }; +/**jsdoc + *

Interface states that affect the refresh rate.

+ * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"FocusActive"Interface has focus and the user is active or is in VR.
"FocusInactive"Interface has focus and the user is inactive.
"Unfocus"Interface doesn't have focus.
"Minimized"Interface is minimized.
"StartUp"Interface is starting up.
"ShutDown"Interface is shutting down.
+ * @typedef {string} RefreshRateRegimeName + */ static const std::array REFRESH_RATE_REGIME_TO_STRING = { { "FocusActive", "FocusInactive", "Unfocus", "Minimized", "StartUp", "ShutDown" } }; +/**jsdoc + *

User experience (UX) modes.

+ * + * + * + * + * + * + * + * + *
ValueDescription
"Desktop"Desktop user experience.
"VR"VR user experience.
+ * @typedef {string} UXModeName + */ static const std::array UX_MODE_TO_STRING = { { "Desktop", "VR" } }; @@ -42,7 +88,7 @@ static const std::array { { 30, 20, 10, 2, 30, 30 } }; static const std::array REALTIME_PROFILE = - { { 60, 60, 10, 2, 30, 30} }; + { { 60, 60, 60, 2, 30, 30} }; static const std::array, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILES = { { ECO_PROFILE, INTERACTIVE_PROFILE, REALTIME_PROFILE } }; diff --git a/interface/src/RefreshRateManager.h b/interface/src/RefreshRateManager.h index 567a515898..18fad392c9 100644 --- a/interface/src/RefreshRateManager.h +++ b/interface/src/RefreshRateManager.h @@ -31,6 +31,23 @@ public: }; static bool isValidRefreshRateProfile(RefreshRateProfile value) { return (value >= RefreshRateProfile::ECO && value <= RefreshRateProfile::REALTIME); } + /**jsdoc + *

Interface states that affect the refresh rate.

+ * + * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0FOCUS_ACTIVEInterface has focus and the user is active or is in VR.
1FOCUS_INACTIVEInterface has focus and the user is inactive.
2UNFOCUSInterface doesn't have focus.
3MINIMIZEDInterface is minimized.
4STARTUPInterface is starting up.
5SHUTDOWNInterface is shutting down.
+ * @typedef {number} RefreshRateRegime + */ enum RefreshRateRegime { FOCUS_ACTIVE = 0, FOCUS_INACTIVE, @@ -42,6 +59,19 @@ public: }; static bool isValidRefreshRateRegime(RefreshRateRegime value) { return (value >= RefreshRateRegime::FOCUS_ACTIVE && value <= RefreshRateRegime::SHUTDOWN); } + /**jsdoc + *

User experience (UX) modes.

+ * + * + * + * + * + * + * + * + *
ValueNameDescription
0DESKTOPDesktop user experience.
1VRVR use experience.
+ * @typedef {number} UXMode + */ enum UXMode { DESKTOP = 0, VR, diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index b0c82f1afc..d84383cf4f 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -55,7 +55,7 @@ static QStringList HAND_MAPPING_SUFFIXES = { "HandThumb1", }; -const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.projectathena.dev/create/avatars/package-avatar.html"); +const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.vircadia.dev/create/avatars/package-avatar.html"); AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) : _avatarFSTFileUrl(avatarFSTFileUrl) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 553033f394..32e725388c 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -543,26 +543,8 @@ void AvatarManager::removeDeadAvatarEntities(const SetOfEntities& deadEntities) for (auto entity : deadEntities) { QUuid entityOwnerID = entity->getOwningAvatarID(); AvatarSharedPointer avatar = getAvatarBySessionID(entityOwnerID); - const bool REQUIRES_REMOVAL_FROM_TREE = false; if (avatar) { - avatar->clearAvatarEntity(entity->getID(), REQUIRES_REMOVAL_FROM_TREE); - } - if (entityTree && entity->isMyAvatarEntity()) { - entityTree->withWriteLock([&] { - // We only need to delete the direct children (rather than the descendants) because - // when the child is deleted, it will take care of its own children. If the child - // is also an avatar-entity, we'll end up back here. If it's not, the entity-server - // will take care of it in the usual way. - entity->forEachChild([&](SpatiallyNestablePointer child) { - EntityItemPointer childEntity = std::dynamic_pointer_cast(child); - if (childEntity) { - entityTree->deleteEntity(childEntity->getID(), true, true); - if (avatar) { - avatar->clearAvatarEntity(childEntity->getID(), REQUIRES_REMOVAL_FROM_TREE); - } - } - }); - }); + avatar->clearAvatarEntity(entity->getID()); } } } diff --git a/interface/src/avatar/AvatarProject.h b/interface/src/avatar/AvatarProject.h index 4c1e55fa1c..3e0d69f78b 100644 --- a/interface/src/avatar/AvatarProject.h +++ b/interface/src/avatar/AvatarProject.h @@ -95,7 +95,7 @@ public: static bool isValidNewProjectName(const QString& projectPath, const QString& projectName); static QString getDefaultProjectsPath() { - return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Project Athena Projects"; + return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Vircadia Projects"; } signals: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 76575ab8ef..570ac5fce2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -972,7 +972,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { recorder->recordFrame(FRAME_TYPE, toFrame(*this)); } - locationChanged(true, false); + locationChanged(true, true); // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; @@ -981,16 +981,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { entityTree->withWriteLock([&] { zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - forEachDescendant([&](SpatiallyNestablePointer object) { - locationChanged(true, false); - // we need to update attached queryAACubes in our own local tree so point-select always works - // however we don't want to flood the update pipeline with AvatarEntity updates, so we assume - // others have all info required to properly update queryAACube of AvatarEntities on their end - EntityItemPointer entity = std::dynamic_pointer_cast(object); - bool iShouldTellServer = !(entity && entity->isAvatarEntity()); - const bool force = false; - entityTree->updateEntityQueryAACube(object, packetSender, force, iShouldTellServer); - }); + entityTree->updateEntityQueryAACube(shared_from_this(), packetSender, false, true); }); bool isPhysicsEnabled = qApp->isPhysicsEnabled(); bool zoneAllowsFlying = zoneInteractionProperties.first; @@ -1518,7 +1509,8 @@ void MyAvatar::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteAr } void MyAvatar::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) { - AvatarData::clearAvatarEntity(entityID, requiresRemovalFromTree); + // NOTE: the requiresRemovalFromTree argument is unused + AvatarData::clearAvatarEntity(entityID); _avatarEntitiesLock.withWriteLock([&] { _cachedAvatarEntityBlobsToDelete.push_back(entityID); }); @@ -1526,7 +1518,12 @@ void MyAvatar::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFrom void MyAvatar::sanitizeAvatarEntityProperties(EntityItemProperties& properties) const { properties.setEntityHostType(entity::HostType::AVATAR); - properties.setOwningAvatarID(getID()); + + // Note: we store AVATAR_SELF_ID in EntityItem::_owningAvatarID and we usually + // store the actual sessionUUID in EntityItemProperties::_owningAvatarID (for JS + // consumption, for example). However at this context we are preparing properties + // for outgoing packet, in which case we use AVATAR_SELF_ID. + properties.setOwningAvatarID(AVATAR_SELF_ID); // there's no entity-server to tell us we're the simulation owner, so always set the // simulationOwner to the owningAvatarID and a high priority. @@ -1581,20 +1578,20 @@ void MyAvatar::handleChangedAvatarEntityData() { // AvatarData::_packedAvatarEntityData via deeper logic. // move the lists to minimize lock time - std::vector cachedBlobsToDelete; - std::vector cachedBlobsToUpdate; - std::vector entitiesToDelete; - std::vector entitiesToAdd; - std::vector entitiesToUpdate; + std::vector cachedBlobsToDelete; + std::vector cachedBlobsToUpdate; + std::vector entitiesToDelete; + std::vector entitiesToAdd; + std::vector entitiesToUpdate; _avatarEntitiesLock.withWriteLock([&] { - cachedBlobsToDelete = std::move(_cachedAvatarEntityBlobsToDelete); - cachedBlobsToUpdate = std::move(_cachedAvatarEntityBlobsToAddOrUpdate); - entitiesToDelete = std::move(_entitiesToDelete); - entitiesToAdd = std::move(_entitiesToAdd); - entitiesToUpdate = std::move(_entitiesToUpdate); + cachedBlobsToDelete.swap(_cachedAvatarEntityBlobsToDelete); + cachedBlobsToUpdate.swap(_cachedAvatarEntityBlobsToAddOrUpdate); + entitiesToDelete.swap(_entitiesToDelete); + entitiesToAdd.swap(_entitiesToAdd); + entitiesToUpdate.swap(_entitiesToUpdate); }); - auto removeAllInstancesHelper = [] (const QUuid& id, std::vector& v) { + auto removeAllInstancesHelper = [] (const EntityItemID& id, std::vector& v) { uint32_t i = 0; while (i < v.size()) { if (id == v[i]) { @@ -1621,11 +1618,7 @@ void MyAvatar::handleChangedAvatarEntityData() { } // DELETE real entities - for (const auto& id : entitiesToDelete) { - entityTree->withWriteLock([&] { - entityTree->deleteEntity(id); - }); - } + entityTree->deleteEntitiesByID(entitiesToDelete); // ADD real entities EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); @@ -1696,7 +1689,7 @@ void MyAvatar::handleChangedAvatarEntityData() { _needToSaveAvatarEntitySettings = true; } // also remove from list of stale blobs to avoid failed entity lookup later - std::set::iterator blobItr = _staleCachedAvatarEntityBlobs.find(id); + std::set::iterator blobItr = _staleCachedAvatarEntityBlobs.find(id); if (blobItr != _staleCachedAvatarEntityBlobs.end()) { _staleCachedAvatarEntityBlobs.erase(blobItr); } @@ -1764,9 +1757,9 @@ bool MyAvatar::updateStaleAvatarEntityBlobs() const { return false; } - std::set staleBlobs = std::move(_staleCachedAvatarEntityBlobs); + std::set staleIDs = std::move(_staleCachedAvatarEntityBlobs); int32_t numFound = 0; - for (const auto& id : staleBlobs) { + for (const auto& id : staleIDs) { bool found = false; EntityItemProperties properties; entityTree->withReadLock([&] { @@ -1809,6 +1802,46 @@ void MyAvatar::prepareAvatarEntityDataForReload() { } AvatarEntityMap MyAvatar::getAvatarEntityData() const { + // NOTE: the return value is expected to be a map of unfortunately-formatted-binary-blobs + AvatarEntityMap data; + + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (!entityTree) { + return data; + } + + QList avatarEntityIDs; + _avatarEntitiesLock.withReadLock([&] { + avatarEntityIDs = _packedAvatarEntityData.keys(); + }); + for (const auto& entityID : avatarEntityIDs) { + auto entity = entityTree->findEntityByID(entityID); + if (!entity) { + continue; + } + + EncodeBitstreamParams params; + auto desiredProperties = entity->getEntityProperties(params); + desiredProperties += PROP_LOCAL_POSITION; + desiredProperties += PROP_LOCAL_ROTATION; + desiredProperties += PROP_LOCAL_VELOCITY; + desiredProperties += PROP_LOCAL_ANGULAR_VELOCITY; + desiredProperties += PROP_LOCAL_DIMENSIONS; + EntityItemProperties properties = entity->getProperties(desiredProperties); + + QByteArray blob; + { + std::lock_guard guard(_scriptEngineLock); + EntityItemProperties::propertiesToBlob(*_scriptEngine, getID(), properties, blob, true); + } + + data[entityID] = blob; + } + return data; +} + +AvatarEntityMap MyAvatar::getAvatarEntityDataNonDefault() const { // NOTE: the return value is expected to be a map of unfortunately-formatted-binary-blobs updateStaleAvatarEntityBlobs(); AvatarEntityMap result; @@ -1851,7 +1884,7 @@ void MyAvatar::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { ++constItr; } // find and erase deleted IDs from _cachedAvatarEntityBlobs - std::vector deletedIDs; + std::vector deletedIDs; AvatarEntityMap::iterator itr = _cachedAvatarEntityBlobs.begin(); while (itr != _cachedAvatarEntityBlobs.end()) { QUuid id = itr.key(); @@ -1946,7 +1979,7 @@ void MyAvatar::loadData() { // Flying preferences must be loaded before calling setFlyingEnabled() Setting::Handle firstRunVal { Settings::firstRun, true }; - setFlyingHMDPref(firstRunVal.get() ? false : _flyingHMDSetting.get()); + setFlyingHMDPref(firstRunVal.get() ? true : _flyingHMDSetting.get()); setMovementReference(firstRunVal.get() ? false : _movementReferenceSetting.get()); setDriveGear1(firstRunVal.get() ? DEFAULT_GEAR_1 : _driveGear1Setting.get()); setDriveGear2(firstRunVal.get() ? DEFAULT_GEAR_2 : _driveGear2Setting.get()); @@ -2469,18 +2502,11 @@ bool isWearableEntity(const EntityItemPointer& entity) { void MyAvatar::removeWornAvatarEntity(const EntityItemID& entityID) { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (entityTree) { auto entity = entityTree->findEntityByID(entityID); if (entity && isWearableEntity(entity)) { - entityTree->withWriteLock([&entityID, &entityTree] { - // remove this entity first from the entity tree - entityTree->deleteEntity(entityID, true, true); - }); - - // remove the avatar entity from our internal list - // (but indicate it doesn't need to be pulled from the tree) - clearAvatarEntity(entityID, false); + treeRenderer->deleteEntity(entityID); + clearAvatarEntity(entityID); } } } @@ -2527,6 +2553,9 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() { auto desiredProperties = entity->getEntityProperties(params); desiredProperties += PROP_LOCAL_POSITION; desiredProperties += PROP_LOCAL_ROTATION; + desiredProperties += PROP_LOCAL_VELOCITY; + desiredProperties += PROP_LOCAL_ANGULAR_VELOCITY; + desiredProperties += PROP_LOCAL_DIMENSIONS; QVariantMap avatarEntityData; avatarEntityData["id"] = entityID; EntityItemProperties entityProperties = entity->getProperties(desiredProperties); @@ -3098,7 +3127,7 @@ void MyAvatar::initAnimGraph() { graphUrl = PathUtils::resourcesUrl("avatar/avatar-animation.json"); #if defined(Q_OS_ANDROID) || defined(HIFI_USE_OPTIMIZED_IK) - graphUrl = PathUtils::resourcesUrl("avatar/avatar-animation_withSplineIKNode.json"); + graphUrl = PathUtils::resourcesUrl("avatar/avatar-animation-optimized-ik.json"); #endif } @@ -3513,8 +3542,46 @@ void MyAvatar::updateOrientation(float deltaTime) { } setWorldOrientation(glm::slerp(getWorldOrientation(), faceRotation, blend)); } else if (isRotatingWhileSeated) { - float rotatingWhileSeatedYaw = -getDriveKey(TRANSLATE_X) * _yawSpeed * deltaTime; - setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, rotatingWhileSeatedYaw, 0.0f)))); + float direction = -getDriveKey(TRANSLATE_X); + float seatedTargetSpeed = direction * _yawSpeed * deltaTime; //deg/renderframe + + const float SEATED_ROTATION_ACCEL_SCALE = 3.5; + + float blend = deltaTime * SEATED_ROTATION_ACCEL_SCALE; + if (blend > 1.0f) { + blend = 1.0f; + } + + //init, accelerate or clamp rotation at target speed + if (fabsf(_seatedBodyYawDelta) > 0.0f) { + if (fabsf(_seatedBodyYawDelta) >= fabsf(seatedTargetSpeed)) { + _seatedBodyYawDelta = seatedTargetSpeed; + } else { + _seatedBodyYawDelta += blend * direction; + } + } else { + _seatedBodyYawDelta = blend * direction; + } + + setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, _seatedBodyYawDelta, 0.0f)))); + + } else if (_seatedBodyYawDelta != 0.0f) { + //decelerate from seated rotation + const float ROTATION_DECAY_TIMESCALE = 0.25f; + float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; + if (attenuation < 0.0f) { + attenuation = 0.0f; + } + _seatedBodyYawDelta *= attenuation; + + float MINIMUM_ROTATION_RATE = 2.0f; + if (fabsf(_seatedBodyYawDelta) < MINIMUM_ROTATION_RATE * deltaTime) { + _seatedBodyYawDelta = 0.0f; + } + + setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, _seatedBodyYawDelta, 0.0f)))); + } else { + _seatedBodyYawDelta = 0.0f; } } @@ -3573,24 +3640,28 @@ void MyAvatar::updateOrientation(float deltaTime) { const float DEFAULT_REORIENT_ANGLE = 65.0f; const float FIRST_PERSON_REORIENT_ANGLE = 95.0f; - const float TRIGGER_REORIENT_ANGLE = 45.0f; + const float TRIGGER_REORIENT_ANGLE = 135.0f; const float FIRST_PERSON_TRIGGER_REORIENT_ANGLE = 65.0f; glm::vec3 ajustedYawVector = cameraYawVector; - float limitAngle = 0.0f; - float triggerAngle = -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE)); + float triggerAngle = glm::cos(glm::radians(TRIGGER_REORIENT_ANGLE)); + float limitAngle = triggerAngle; if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { - limitAngle = glm::sin(glm::radians(90.0f - FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); + limitAngle = glm::cos(glm::radians(FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); triggerAngle = limitAngle; } float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? FIRST_PERSON_REORIENT_ANGLE : DEFAULT_REORIENT_ANGLE; + if (frontBackDot < 0.0f) { + ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); + } if (frontBackDot < limitAngle) { - if (frontBackDot < 0.0f) { - ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); - } + if (!isRotatingWhileSeated) { - if (frontBackDot < triggerAngle) { + if (frontBackDot < triggerAngle && _seatedBodyYawDelta == 0.0f) { _shouldTurnToFaceCamera = true; _firstPersonSteadyHeadTimer = 0.0f; + } else { + setWorldOrientation(previousOrientation); + _seatedBodyYawDelta = 0.0f; } } else { setWorldOrientation(previousOrientation); @@ -3934,6 +4005,10 @@ float MyAvatar::getGravity() { void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { QUuid oldSessionID = getSessionUUID(); Avatar::setSessionUUID(sessionUUID); + bool sendPackets = !DependencyManager::get()->getSessionUUID().isNull(); + if (!sendPackets) { + return; + } QUuid newSessionID = getSessionUUID(); if (newSessionID != oldSessionID) { auto treeRenderer = DependencyManager::get(); @@ -3943,7 +4018,6 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { _avatarEntitiesLock.withReadLock([&] { avatarEntityIDs = _packedAvatarEntityData.keys(); }); - bool sendPackets = !DependencyManager::get()->getSessionUUID().isNull(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); entityTree->withWriteLock([&] { for (const auto& entityID : avatarEntityIDs) { @@ -3951,11 +4025,9 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { if (!entity) { continue; } - // update OwningAvatarID so entity can be identified as "ours" later - entity->setOwningAvatarID(newSessionID); // NOTE: each attached AvatarEntity already have the correct updated parentID // via magic in SpatiallyNestable, hence we check against newSessionID - if (sendPackets && entity->getParentID() == newSessionID) { + if (entity->getParentID() == newSessionID) { // but when we have a real session and the AvatarEntity is parented to MyAvatar // we need to update the "packedAvatarEntityData" sent to the avatar-mixer // because it contains a stale parentID somewhere deep inside @@ -6554,22 +6626,23 @@ void MyAvatar::updateEyesLookAtPosition(float deltaTime) { int rightEyeJointIndex = getJointIndex("RightEye"); bool eyesAreOverridden = getIsJointOverridden(leftEyeJointIndex) || getIsJointOverridden(rightEyeJointIndex); + const float DEFAULT_GAZE_DISTANCE = 20.0f; // meters if (eyesAreOverridden) { // A script has set the eye rotations, so use these to set lookAtSpot glm::quat leftEyeRotation = getAbsoluteJointRotationInObjectFrame(leftEyeJointIndex); glm::quat rightEyeRotation = getAbsoluteJointRotationInObjectFrame(rightEyeJointIndex); - glm::vec3 leftVec = getWorldOrientation() * leftEyeRotation * IDENTITY_FORWARD; - glm::vec3 rightVec = getWorldOrientation() * rightEyeRotation * IDENTITY_FORWARD; + glm::vec3 leftVec = getWorldOrientation() * leftEyeRotation * Vectors::UNIT_Z; + glm::vec3 rightVec = getWorldOrientation() * rightEyeRotation * Vectors::UNIT_Z; glm::vec3 leftEyePosition = myHead->getLeftEyePosition(); glm::vec3 rightEyePosition = myHead->getRightEyePosition(); float t1, t2; bool success = findClosestApproachOfLines(leftEyePosition, leftVec, rightEyePosition, rightVec, t1, t2); - if (success) { + if (success && t1 > 0 && t2 > 0) { glm::vec3 leftFocus = leftEyePosition + leftVec * t1; glm::vec3 rightFocus = rightEyePosition + rightVec * t2; lookAtSpot = (leftFocus + rightFocus) / 2.0f; // average } else { - lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * 1000.0f; + lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * DEFAULT_GAZE_DISTANCE; } } else if (_scriptControlsEyesLookAt) { if (_scriptEyesControlTimer < MAX_LOOK_AT_TIME_SCRIPT_CONTROL) { @@ -6583,19 +6656,18 @@ void MyAvatar::updateEyesLookAtPosition(float deltaTime) { controller::Pose rightEyePose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_EYE); if (leftEyePose.isValid() && rightEyePose.isValid()) { // an eye tracker is in use, set lookAtSpot from this - glm::vec3 leftVec = getWorldOrientation() * leftEyePose.rotation * glm::vec3(0.0f, 0.0f, -1.0f); - glm::vec3 rightVec = getWorldOrientation() * rightEyePose.rotation * glm::vec3(0.0f, 0.0f, -1.0f); - + glm::vec3 leftVec = getWorldOrientation() * leftEyePose.rotation * Vectors::UNIT_Z; + glm::vec3 rightVec = getWorldOrientation() * rightEyePose.rotation * Vectors::UNIT_Z; glm::vec3 leftEyePosition = myHead->getLeftEyePosition(); glm::vec3 rightEyePosition = myHead->getRightEyePosition(); float t1, t2; bool success = findClosestApproachOfLines(leftEyePosition, leftVec, rightEyePosition, rightVec, t1, t2); - if (success) { + if (success && t1 > 0 && t2 > 0) { glm::vec3 leftFocus = leftEyePosition + leftVec * t1; glm::vec3 rightFocus = rightEyePosition + rightVec * t2; lookAtSpot = (leftFocus + rightFocus) / 2.0f; // average } else { - lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * 1000.0f; + lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * DEFAULT_GAZE_DISTANCE; } } else { // no script override, no eye tracker, so do procedural eye motion diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 95256500e1..3140c68f88 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -148,6 +148,22 @@ class MyAvatar : public Avatar { * size in the virtual world. Read-only. * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. * Read-only. + * @property {boolean} hasScriptedBlendshapes=false - true if blend shapes are controlled by scripted actions, + * otherwise false. Set this to true before using the {@link MyAvatar.setBlendshape} method, + * and set back to false after you no longer want scripted control over the blend shapes. + *

Note: This property will automatically be set to true if the controller system has + * valid facial blend shape actions.

+ * @property {boolean} hasProceduralBlinkFaceMovement=true - true if avatars blink automatically by animating + * facial blend shapes, false if automatic blinking is disabled. Set to false to fully control + * the blink facial blend shapes via the {@link MyAvatar.setBlendshape} method. + * @property {boolean} hasProceduralEyeFaceMovement=true - true if the facial blend shapes for an avatar's eyes + * adjust automatically as the eyes move, false if this automatic movement is disabled. Set this property + * to true to prevent the iris from being obscured by the upper or lower lids. Set to false to + * fully control the eye blend shapes via the {@link MyAvatar.setBlendshape} method. + * @property {boolean} hasAudioEnabledFaceMovement=true - true if the avatar's mouth blend shapes animate + * automatically based on detected microphone input, false if this automatic movement is disabled. Set + * this property to false to fully control the mouth facial blend shapes via the + * {@link MyAvatar.setBlendshape} method. * * @comment IMPORTANT: This group of properties is copied from Avatar.h; they should NOT be edited here. * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the @@ -270,9 +286,9 @@ class MyAvatar : public Avatar { * @property {number} isInSittingState - true if the user wearing the HMD is determined to be sitting * (avatar leaning is disabled, recentering is enabled), false if the user wearing the HMD is * determined to be standing (avatar leaning is enabled, and avatar recenters if it leans too far). - * If userRecenterModel == 2 (i.e., auto) the property value automatically updates as the user sits + * If userRecenterModel == 2 (i.e., "auto") the property value automatically updates as the user sits * or stands, unless isSitStandStateLocked == true. Setting the property value overrides the current - * siting / standing state, which is updated when the user next sits or stands unless + * sitting / standing state, which is updated when the user next sits or stands unless * isSitStandStateLocked == true. * @property {boolean} isSitStandStateLocked - true to lock the avatar sitting/standing state, i.e., use this * to disable automatically changing state. @@ -305,10 +321,7 @@ class MyAvatar : public Avatar { * @borrows Avatar.setAttachmentsVariant as setAttachmentsVariant * @borrows Avatar.updateAvatarEntity as updateAvatarEntity * @borrows Avatar.clearAvatarEntity as clearAvatarEntity - * @borrows Avatar.hasScriptedBlendshapes as hasScriptedBlendshapes - * @borrows Avatar.hasProceduralBlinkFaceMovement as hasProceduralBlinkFaceMovement - * @borrows Avatar.hasProceduralEyeFaceMovement as hasProceduralEyeFaceMovement - * @borrows Avatar.hasAudioEnabledFaceMovement as hasAudioEnabledFaceMovement + * @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected * @borrows Avatar.setSkeletonModelURL as setSkeletonModelURL * @borrows Avatar.getAttachmentData as getAttachmentData * @borrows Avatar.setAttachmentData as setAttachmentData @@ -420,38 +433,72 @@ class MyAvatar : public Avatar { const bool DEFAULT_STRAFE_ENABLED = true; public: + /**jsdoc + * The DriveKeys API provides constant numeric values that represent different logical keys that drive your + * avatar and camera. + * + * @namespace DriveKeys + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {number} TRANSLATE_X - Move the user's avatar in the direction of its x-axis, if the camera isn't in + * independent or mirror modes. + * @property {number} TRANSLATE_Y - Move the user's avatar in the direction of its y-axis, if the camera isn't in + * independent or mirror modes. + * @property {number} TRANSLATE_Z - Move the user's avatar in the direction of its z-axis, if the camera isn't in + * independent or mirror modes. + * @property {number} YAW - Rotate the user's avatar about its y-axis at a rate proportional to the control value, if the + * camera isn't in independent or mirror modes. + * @property {number} STEP_TRANSLATE_X - No action. + * @property {number} STEP_TRANSLATE_Y - No action. + * @property {number} STEP_TRANSLATE_Z - No action. + * @property {number} STEP_YAW - Rotate the user's avatar about its y-axis in a step increment, if the camera isn't in + * independent or mirror modes. + * @property {number} PITCH - Rotate the user's avatar head and attached camera about its negative x-axis (i.e., positive + * values pitch down) at a rate proportional to the control value, if the camera isn't in HMD, independent, or mirror + * modes. + * @property {number} ZOOM - Zoom the camera in or out. + * @property {number} DELTA_YAW - Rotate the user's avatar about its y-axis by an amount proportional to the control value, + * if the camera isn't in independent or mirror modes. + * @property {number} DELTA_PITCH - Rotate the user's avatar head and attached camera about its negative x-axis (i.e., + * positive values pitch down) by an amount proportional to the control value, if the camera isn't in HMD, independent, + * or mirror modes. + */ + /**jsdoc *

Logical keys that drive your avatar and camera.

* * - * + * * * - * - * - * - * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * + * + * + * * *
ValueNameDescription
ValueDescription
0TRANSLATE_XMove the user's avatar in the direction of its x-axis, if the - * camera isn't in independent or mirror modes.
1TRANSLATE_YMove the user's avatar in the direction of its y-axis, if the - * camera isn't in independent or mirror modes.
2TRANSLATE_ZMove the user's avatar in the direction of its z-axis, if the - * camera isn't in independent or mirror modes
3YAWRotate the user's avatar about its y-axis at a rate proportional to the - * control value, if the camera isn't in independent or mirror modes.
4STEP_TRANSLATE_XNo action.
5STEP_TRANSLATE_YNo action.
6STEP_TRANSLATE_ZNo action.
7STEP_YAWRotate the user's avatar about its y-axis in a step increment, if - * the camera isn't in independent or mirror modes.
8PITCHRotate the user's avatar head and attached camera about its negative - * x-axis (i.e., positive values pitch down) at a rate proportional to the control value, if the camera isn't in HMD, - * independent, or mirror modes.
9ZOOMZoom the camera in or out.
10DELTA_YAWRotate the user's avatar about its y-axis by an amount proportional - * to the control value, if the camera isn't in independent or mirror modes.
11DELTA_PITCHRotate the user's avatar head and attached camera about its - * negative x-axis (i.e., positive values pitch down) by an amount proportional to the control value, if the camera - * isn't in HMD, independent, or mirror modes.
{@link DriveKeys|DriveKeys.TRANSLATE_X}Move the user's avatar in the direction of its + * x-axis, if the camera isn't in independent or mirror modes.
{@link DriveKeys|DriveKeys.TRANSLATE_Y}Move the user's avatar in the direction of its + * -axis, if the camera isn't in independent or mirror modes.
{@link DriveKeys|DriveKeys.TRANSLATE_Z}Move the user's avatar in the direction of its + * z-axis, if the camera isn't in independent or mirror modes.
{@link DriveKeys|DriveKeys.YAW}Rotate the user's avatar about its y-axis at a rate + * proportional to the control value, if the camera isn't in independent or mirror modes.
{@link DriveKeys|DriveKeys.STEP_TRANSLATE_X}No action.
{@link DriveKeys|DriveKeys.STEP_TRANSLATE_Y}No action.
{@link DriveKeys|DriveKeys.STEP_TRANSLATE_Z}No action.
{@link DriveKeys|DriveKeys.STEP_YAW}Rotate the user's avatar about its y-axis in a + * step increment, if the camera isn't in independent or mirror modes.
{@link DriveKeys|DriveKeys.PITCH}Rotate the user's avatar head and attached camera + * about its negative x-axis (i.e., positive values pitch down) at a rate proportional to the control value, if the + * camera isn't in HMD, independent, or mirror modes.
{@link DriveKeys|DriveKeys.ZOOM}Zoom the camera in or out.
{@link DriveKeys|DriveKeys.DELTA_YAW}Rotate the user's avatar about its y-axis by an + * amount proportional to the control value, if the camera isn't in independent or mirror modes.
{@link DriveKeys|DriveKeys.DELTA_PITCH}Rotate the user's avatar head and attached + * camera about its negative x-axis (i.e., positive values pitch down) by an amount proportional to the control + * value, if the camera isn't in HMD, independent, or mirror modes.
- * @typedef {number} MyAvatar.DriveKeys + * @typedef {number} DriveKey */ enum DriveKeys { TRANSLATE_X = 0, @@ -480,7 +527,7 @@ public: * 0ForceSitAssumes the user is seated in the real world. Disables avatar * leaning regardless of what the avatar is doing in the virtual world (i.e., avatar always recenters). * 1ForceStandAssumes the user is standing in the real world. Enables avatar - * leaning regardless of what the avatar is doing in the virtual world (i.e. avatar leans, then if leans too far it + * leaning regardless of what the avatar is doing in the virtual world (i.e., avatar leans, then if leans too far it * recenters). * 2AutoInterface detects when the user is standing or seated in the real world. * Avatar leaning is disabled when the user is sitting (i.e., avatar always recenters), and avatar leaning is enabled @@ -607,7 +654,7 @@ public: *

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.overrideAnimation * @param {string} url - The URL to the animation file. Animation files may be in glTF or FBX format, but only need to * contain the avatar skeleton and animation data. glTF models may be in JSON or binary format (".gltf" or ".glb" URLs @@ -618,7 +665,7 @@ public: * @param {number} firstFrame - The frame to start the animation at. * @param {number} lastFrame - The frame to end the animation at. * @example Play a clapping animation on your avatar for three seconds. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx"; * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); * Script.setTimeout(function () { * MyAvatar.restoreAnimation(); @@ -641,7 +688,7 @@ public: * @param {number} firstFrame - The frame to start the animation at. * @param {number} lastFrame - The frame to end the animation at. * @example Override left hand animation for three seconds. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx"; * MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53); * Script.setTimeout(function () { * MyAvatar.restoreHandAnimation(); @@ -658,7 +705,7 @@ public: * animation, this function has no effect.

* @function MyAvatar.restoreAnimation * @example Play a clapping animation on your avatar for three seconds. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx"; * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); * Script.setTimeout(function () { * MyAvatar.restoreAnimation(); @@ -675,7 +722,7 @@ public: * @function MyAvatar.restoreHandAnimation * @param isLeft {boolean} Set to true if using the left hand * @example Override left hand animation for three seconds. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx"; * MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53); * Script.setTimeout(function () { * MyAvatar.restoreHandAnimation(); @@ -715,7 +762,7 @@ public: *

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards. + * Avatar Standards. * @function MyAvatar.overrideRoleAnimation * @param {string} role - The animation role to override * @param {string} url - The URL to the animation file. Animation files need to be in glTF or FBX format, but only need to @@ -733,7 +780,7 @@ public: * hanging at its sides when it is not moving, the avatar will stand and clap its hands. Note that just as it did before, as soon as the avatar * starts to move, the animation will smoothly blend into the walk animation used by the "walkFwd" animation role. * // An animation of the avatar clapping its hands while standing. Restore default after 30s. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; + * var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx"; * MyAvatar.overrideRoleAnimation("idleStand", ANIM_URL, 30, true, 0, 53); * Script.setTimeout(function () { * MyAvatar.restoreRoleAnimation(); @@ -1003,7 +1050,7 @@ public: /**jsdoc * Gets the value of a drive key, regardless of whether it is disabled. * @function MyAvatar.getRawDriveKey - * @param {MyAvatar.DriveKeys} key - The drive key. + * @param {DriveKey} key - The drive key. * @returns {number} The value of the drive key. */ Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; @@ -1013,11 +1060,10 @@ public: /**jsdoc * Disables the action associated with a drive key. * @function MyAvatar.disableDriveKey - * @param {MyAvatar.DriveKeys} key - The drive key to disable. + * @param {DriveKey} key - The drive key to disable. * @example Disable rotating your avatar using the keyboard for a couple of seconds. - * var YAW = 3; * print("Disable"); - * MyAvatar.disableDriveKey(YAW); + * MyAvatar.disableDriveKey(DriveKeys.YAW); * Script.setTimeout(function () { * print("Enable"); * MyAvatar.enableDriveKey(YAW); @@ -1029,14 +1075,14 @@ public: * Enables the action associated with a drive key. The action may have been disabled with * {@link MyAvatar.disableDriveKey|disableDriveKey}. * @function MyAvatar.enableDriveKey - * @param {MyAvatar.DriveKeys} key - The drive key to enable. + * @param {DriveKey} key - The drive key to enable. */ Q_INVOKABLE void enableDriveKey(DriveKeys key); /**jsdoc * Checks whether a drive key is disabled. * @function MyAvatar.isDriveKeyDisabled - * @param {DriveKeys} key - The drive key to check. + * @param {DriveKey} key - The drive key to check. * @returns {boolean} true if the drive key is disabled, false if it isn't. */ Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const; @@ -1137,7 +1183,7 @@ public: /**jsdoc * Gets information on the avatar your avatar is currently looking at. * @function MyAvatar.getTargetAvatar - * @returns {AvatarData} Information on the avatar being looked at. + * @returns {ScriptAvatar} Information on the avatar being looked at, null if no avatar is being looked at. */ // FIXME: The return type doesn't have a conversion to a script value so the function always returns undefined in // JavaScript. Note: When fixed, JSDoc is needed for the return type. @@ -1746,57 +1792,57 @@ public: void prepareAvatarEntityDataForReload(); /**jsdoc - * Turn the avatar's head until it faces the target point within the 90/-90 degree range. - * Once this method is called, API calls will have full control of the head for a limited time. - * If this method is not called for two seconds, the engine will regain control of the head. - * @function MyAvatar.setHeadLookAt - * @param {Vec3} lookAtTarget - The target point in world coordinates. - */ + * Turns the avatar's head until it faces the target point within a +90/-90 degree range. + * Once this method is called, API calls have full control of the head for a limited time. + * If this method is not called for 2 seconds, the engine regains control of the head. + * @function MyAvatar.setHeadLookAt + * @param {Vec3} lookAtTarget - The target point in world coordinates. + */ Q_INVOKABLE void setHeadLookAt(const glm::vec3& lookAtTarget); /**jsdoc - * Returns the current head look at target point in world coordinates. - * @function MyAvatar.getHeadLookAt - * @returns {Vec3} The head's look at target in world coordinates. - */ + * Gets the current target point of the head's look direction in world coordinates. + * @function MyAvatar.getHeadLookAt + * @returns {Vec3} The head's look-at target in world coordinates. + */ Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; } /**jsdoc - * When this function is called the engine regains control of the head immediately. - * @function MyAvatar.releaseHeadLookAtControl - */ + * Returns control of the avatar's head to the engine, and releases control from API calls. + * @function MyAvatar.releaseHeadLookAtControl + */ Q_INVOKABLE void releaseHeadLookAtControl(); /**jsdoc - * Force the avatar's eyes to look to the specified location. - * Once this method is called, API calls will have full control of the eyes for a limited time. - * If this method is not called for two seconds, the engine will regain control of the eyes. - * @function MyAvatar.setEyesLookAt - * @param {Vec3} lookAtTarget - The target point in world coordinates. - */ + * Forces the avatar's eyes to look at a specified location. Once this method is called, API calls + * full control of the eyes for a limited time. If this method is not called for 2 seconds, + * the engine regains control of the eyes. + * @function MyAvatar.setEyesLookAt + * @param {Vec3} lookAtTarget - The target point in world coordinates. + */ Q_INVOKABLE void setEyesLookAt(const glm::vec3& lookAtTarget); /**jsdoc - * Returns the current eyes look at target point in world coordinates. - * @function MyAvatar.getEyesLookAt - * @returns {Vec3} The eyes's look at target in world coordinates. - */ + * Gets the current target point of the eyes look direction in world coordinates. + * @function MyAvatar.getEyesLookAt + * @returns {Vec3} The eyes' look-at target in world coordinates. + */ Q_INVOKABLE glm::vec3 getEyesLookAt() { return _eyesLookAtTarget.get(); } /**jsdoc - * When this function is called the engine regains control of the eyes immediately. - * @function MyAvatar.releaseEyesLookAtControl - */ + * Returns control of the avatar's eyes to the engine, and releases control from API calls. + * @function MyAvatar.releaseEyesLookAtControl + */ Q_INVOKABLE void releaseEyesLookAtControl(); /**jsdoc - * Aims the pointing directional blending towards the provided target point. - * The "point" reaction should be triggered before using this method. - * MyAvatar.beginReaction("point") - * Returns true if the target point lays in front of the avatar. - * @function MyAvatar.setPointAt - * @param {Vec3} pointAtTarget - The target point in world coordinates. - */ + * Sets the point-at target for the "point" reaction that may be started with {@link MyAvatar.beginReaction}. + * The point-at target is set only if it is in front of the avatar. + *

Note: The "point" reaction should be started before calling this method.

+ * @function MyAvatar.setPointAt + * @param {Vec3} pointAtTarget - The target to point at, in world coordinates. + * @returns {boolean} true if the target point was set, false if it wasn't. + */ Q_INVOKABLE bool setPointAt(const glm::vec3& pointAtTarget); glm::quat getLookAtRotation() { return _lookAtYaw * _lookAtPitch; } @@ -1840,14 +1886,17 @@ public: /**jsdoc * Gets details of all avatar entities. + *

Warning: Potentially an expensive call. Do not use if possible.

* @function MyAvatar.getAvatarEntityData - * @returns {AvatarEntityMap} Details of the avatar entities. + * @returns {AvatarEntityMap} Details of all avatar entities. * @example Report the current avatar entities. * var avatarEntityData = MyAvatar.getAvatarEntityData(); * print("Avatar entities: " + JSON.stringify(avatarEntityData)); */ AvatarEntityMap getAvatarEntityData() const override; + AvatarEntityMap getAvatarEntityDataNonDefault() const override; + /**jsdoc * Sets all avatar entities from an object. * @function MyAvatar.setAvatarEntityData @@ -1871,13 +1920,13 @@ public: /**jsdoc * Enables and disables flow simulation of physics on the avatar's hair, clothes, and body parts. See - * {@link https://docs.projectathena.dev/create/avatars/add-flow.html|Add Flow to Your Avatar} for more + * {@link https://docs.vircadia.dev/create/avatars/add-flow.html|Add Flow to Your Avatar} for more * information. * @function MyAvatar.useFlow * @param {boolean} isActive - true if flow simulation is enabled on the joint, false if it isn't. * @param {boolean} isCollidable - true to enable collisions in the flow simulation, false to * disable. - * @param {Object} [physicsConfig>] - Physics configurations for particular entity + * @param {Object} [physicsConfig] - Physics configurations for particular entity * and avatar joints. * @param {Object} [collisionsConfig] - Collision configurations for particular * entity and avatar joints. @@ -1899,26 +1948,25 @@ public: Q_INVOKABLE QVariantList getCollidingFlowJoints(); /**jsdoc - * Starts a sitting action for the avatar + * Starts a sitting action for the avatar. * @function MyAvatar.beginSit - * @param {Vec3} position - The point in space where the avatar will sit. - * @param {Quat} rotation - Initial absolute orientation of the avatar once is seated. + * @param {Vec3} position - The position where the avatar should sit. + * @param {Quat} rotation - The initial orientation of the seated avatar. */ Q_INVOKABLE void beginSit(const glm::vec3& position, const glm::quat& rotation); /**jsdoc - * Ends a sitting action for the avatar + * Ends a sitting action for the avatar. * @function MyAvatar.endSit * @param {Vec3} position - The position of the avatar when standing up. - * @param {Quat} rotation - The absolute rotation of the avatar once the sitting action ends. + * @param {Quat} rotation - The orientation of the avatar when standing up. */ Q_INVOKABLE void endSit(const glm::vec3& position, const glm::quat& rotation); /**jsdoc - * Gets whether the avatar is in a seated pose. The seated pose is set by calling the - * MyAvatar::beginSit method. + * Gets whether the avatar is in a seated pose. The seated pose is set by calling {@link MyAvatar.beginSit}. * @function MyAvatar.isSeated - * @returns {boolean} true if the avatar is in a seated pose. + * @returns {boolean} true if the avatar is in a seated pose, false if it isn't. */ Q_INVOKABLE bool isSeated() { return _characterController.getSeated(); } @@ -2003,7 +2051,8 @@ public slots: float getGravity(); /**jsdoc - * Moves the avatar to a new position and/or orientation in the domain, while taking into account Avatar leg-length. + * Moves the avatar to a new position and/or orientation in the domain, with safe landing, while taking into account avatar + * leg length. * @function MyAvatar.goToFeetLocation * @param {Vec3} position - The new position for the avatar, in world coordinates. * @param {boolean} [hasOrientation=false] - Set to true to set the orientation of the avatar. @@ -2021,21 +2070,21 @@ public slots: * @param {boolean} [hasOrientation=false] - Set to true to set the orientation of the avatar. * @param {Quat} [orientation=Quat.IDENTITY] - The new orientation for the avatar. * @param {boolean} [shouldFaceLocation=false] - Set to true to position the avatar a short distance away from - * @param {boolean} [withSafeLanding=true] - Set to false MyAvatar::safeLanding will not be called (used when teleporting). * the new position and orientate the avatar to face the position. + * @param {boolean} [withSafeLanding=true] - Set to false to disable safe landing when teleporting. */ void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false, bool withSafeLanding = true); /**jsdoc - * Moves the avatar to a new position and (optional) orientation in the domain. + * Moves the avatar to a new position and (optional) orientation in the domain, with safe landing. * @function MyAvatar.goToLocation * @param {MyAvatar.GoToProperties} target - The goto target. */ void goToLocation(const QVariant& properties); /**jsdoc - * Moves the avatar to a new position and then enables collisions. + * Moves the avatar to a new position, with safe landing, and enables collisions. * @function MyAvatar.goToLocationAndEnableCollisions * @param {Vec3} position - The new position for the avatar, in world coordinates. */ @@ -2236,7 +2285,7 @@ public slots: /**jsdoc * Gets the URL of the override animation graph. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.getAnimGraphOverrideUrl * @returns {string} The URL of the override animation graph JSON file. "" if there is no override animation @@ -2246,7 +2295,7 @@ public slots: /**jsdoc * Sets the animation graph to use in preference to the default animation graph. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.setAnimGraphOverrideUrl * @param {string} url - The URL of the animation graph JSON file to use. Set to "" to clear an override. @@ -2255,7 +2304,7 @@ public slots: /**jsdoc * Gets the URL of animation graph (i.e., the avatar animation JSON) that's currently being used for avatar animations. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.getAnimGraphUrl * @returns {string} The URL of the current animation graph JSON file. @@ -2266,7 +2315,7 @@ public slots: /**jsdoc * Sets the current animation graph (i.e., the avatar animation JSON) to use for avatar animations and makes it the default. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.setAnimGraphUrl * @param {string} url - The URL of the animation graph JSON file to use. @@ -2297,43 +2346,52 @@ public slots: virtual void setModelScale(float scale) override; /**jsdoc - * MyAvatar.getTriggerReactions - * Returns a list of reactions names that can be triggered using MyAvatar.triggerReaction(). - * @returns {string[]} Array of reaction names. + * Gets the list of reactions names that can be triggered using {@link MyAvatar.triggerReaction}. + *

See also: {@link MyAvatar.getBeginEndReactions}. + * @function MyAvatar.getTriggerReactions + * @returns {string[]} List of reaction names that can be triggered using {@link MyAvatar.triggerReaction}. + * @example List the available trigger reactions. + * print("Trigger reactions:", JSON.stringify(MyAvatar.getTriggerReactions())); */ QStringList getTriggerReactions() const; /**jsdoc - * MyAvatar.getBeginReactions - * Returns a list of reactions names that can be enabled using MyAvatar.beginReaction() and MyAvatar.endReaction(). - * @returns {string[]} Array of reaction names. + * Gets the list of reactions names that can be enabled using {@link MyAvatar.beginReaction} and + * {@link MyAvatar.endReaction}. + *

See also: {@link MyAvatar.getTriggerReactions}. + * @function MyAvatar.getBeginEndReactions + * @returns {string[]} List of reaction names that can be enabled using {@link MyAvatar.beginReaction} and + * {@link MyAvatar.endReaction}. + * @example List the available begin-end reactions. + * print("Begin-end reactions:", JSON.stringify(MyAvatar.getBeginEndReactions())); */ QStringList getBeginEndReactions() const; /**jsdoc - * MyAvatar.triggerReaction - * Plays the given reaction on the avatar, once the reaction is complete it will automatically complete. Only reaction names returned from MyAvatar.getTriggerReactions() are available. - * @param {string} reactionName - reaction name - * @returns {bool} false if the given reaction is not supported. + * Plays a reaction on the avatar. Once the reaction is complete it will stop playing. + *

Only reaction names returned by {@link MyAvatar.getTriggerReactions} are available.

+ * @function MyAvatar.triggerReaction + * @param {string} reactionName - The reaction to trigger. + * @returns {boolean} true if the reaction was played, false if the reaction is not supported. */ bool triggerReaction(QString reactionName); /**jsdoc - * MyAvatar.beginReaction - * Plays the given reaction on the avatar. The avatar will continue to play the reaction until stopped via the MyAvatar.endReaction() call or superseeded by another reaction. - * Only reaction names returned from MyAvatar.getBeginEndReactions() are available. - * NOTE: the caller is responsible for calling the corresponding MyAvatar.endReaction(), otherwise the avatar might become stuck in the reaction forever. - * @param {string} reactionName - reaction name - * @returns {bool} false if the given reaction is not supported. + * Starts playing a reaction on the avatar. The reaction will continue to play until stopped using + * {@link MyAvatar.endReaction} or superseded by another reaction. + *

Only reactions returned by {@link MyAvatar.getBeginEndReactions} are available.

+ * @function MyAvatar.beginReaction + * @param {string} reactionName - The reaction to start playing. + * @returns {boolean} true if the reaction was started, false if the reaction is not supported. */ bool beginReaction(QString reactionName); /**jsdoc - * MyAvatar.endReaction - * Used to stop a given reaction that was started via MyAvatar.beginReaction(). - * @param {string} reactionName - reaction name - * @returns {bool} false if the given reaction is not supported. + * Stops playing a reaction that was started using {@link MyAvatar.beginReaction}. + * @function MyAvatar.endReaction + * @param {string} reactionName - The reaction to stop playing. + * @returns {boolean} true if the reaction was stopped, false if the reaction is not supported. */ bool endReaction(QString reactionName); @@ -2644,7 +2702,7 @@ private: bool _enableFlying { false }; bool _flyingPrefDesktop { true }; - bool _flyingPrefHMD { false }; + bool _flyingPrefHMD { true }; bool _wasPushing { false }; bool _isPushing { false }; bool _isBeingPushed { false }; @@ -2970,19 +3028,19 @@ private: // correctly stored in _cachedAvatarEntityBlobs. These come from loadAvatarEntityDataFromSettings() and // setAvatarEntityData(). These changes need to be extracted from _cachedAvatarEntityBlobs and applied to // real EntityItems. - std::vector _entitiesToDelete; - std::vector _entitiesToAdd; - std::vector _entitiesToUpdate; + std::vector _entitiesToDelete; + std::vector _entitiesToAdd; + std::vector _entitiesToUpdate; // // The _cachedAvatarEntityBlobsToDelete/Add/Update lists are for changes whose "authoritative sources" are // already reflected in real EntityItems. These changes need to be propagated to _cachedAvatarEntityBlobs // and eventually to settings. - std::vector _cachedAvatarEntityBlobsToDelete; - std::vector _cachedAvatarEntityBlobsToAddOrUpdate; - std::vector _cachedAvatarEntityBlobUpdatesToSkip; + std::vector _cachedAvatarEntityBlobsToDelete; + std::vector _cachedAvatarEntityBlobsToAddOrUpdate; + std::vector _cachedAvatarEntityBlobUpdatesToSkip; // // Also these lists for tracking delayed changes to blobs and Settings - mutable std::set _staleCachedAvatarEntityBlobs; + mutable std::set _staleCachedAvatarEntityBlobs; // // keep a ScriptEngine around so we don't have to instantiate on the fly (these are very slow to create/delete) mutable std::mutex _scriptEngineLock; diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 3a25721528..997dcfe685 100755 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -407,13 +407,13 @@ void MyCharacterController::clearDetailedMotionStates() { } void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { - _detailedMotionStates[i]->forceActive(); + for (auto& detailedMotionState : _detailedMotionStates) { + detailedMotionState->forceActive(); } if (_pendingFlags & PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION) { _pendingFlags &= ~PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { - transaction.objectsToRemove.push_back(_detailedMotionStates[i]); + for (auto& detailedMotionState : _detailedMotionStates) { + transaction.objectsToRemove.push_back(detailedMotionState); } // NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove // See AvatarManager::handleProcessedPhysicsTransaction() diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 1b88a518c8..4b6f85de1c 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -134,11 +134,11 @@ void MyHead::simulate(float deltaTime) { userInputMapper->getActionStateValid(controller::Action::MOUTHSMILE_L) || userInputMapper->getActionStateValid(controller::Action::MOUTHSMILE_R); - bool eyesTracked = - userInputMapper->getPoseState(controller::Action::LEFT_EYE).valid && - userInputMapper->getPoseState(controller::Action::RIGHT_EYE).valid; - MyAvatar* myAvatar = static_cast(_owningAvatar); + bool eyesTracked = + myAvatar->getControllerPoseInSensorFrame(controller::Action::LEFT_EYE).valid && + myAvatar->getControllerPoseInSensorFrame(controller::Action::RIGHT_EYE).valid; + int leftEyeJointIndex = myAvatar->getJointIndex("LeftEye"); int rightEyeJointIndex = myAvatar->getJointIndex("RightEye"); bool eyeJointsOverridden = myAvatar->getIsJointOverridden(leftEyeJointIndex) || myAvatar->getIsJointOverridden(rightEyeJointIndex); diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 50f6369dbe..ae453d5b00 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -116,10 +116,10 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { int32_t bytesRead = Avatar::parseDataFromBuffer(buffer); - for (size_t i = 0; i < _detailedMotionStates.size(); i++) { + for (auto& detailedMotionState : _detailedMotionStates) { // NOTE: we activate _detailedMotionStates is because they are KINEMATIC // and Bullet will automagically call DetailedMotionState::getWorldTransform() when active. - _detailedMotionStates[i]->forceActive(); + detailedMotionState->forceActive(); } if (_moving && _motionState) { _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); @@ -561,9 +561,18 @@ void OtherAvatar::handleChangedAvatarEntityData() { _avatarEntitiesLock.withReadLock([&] { packedAvatarEntityData = _packedAvatarEntityData; }); - foreach (auto entityID, recentlyRemovedAvatarEntities) { - if (!packedAvatarEntityData.contains(entityID)) { - entityTree->deleteEntity(entityID, true, true); + if (!recentlyRemovedAvatarEntities.empty()) { + std::vector idsToDelete; + idsToDelete.reserve(recentlyRemovedAvatarEntities.size()); + foreach (auto entityID, recentlyRemovedAvatarEntities) { + if (!packedAvatarEntityData.contains(entityID)) { + idsToDelete.push_back(entityID); + } + } + if (!idsToDelete.empty()) { + bool force = true; + bool ignoreWarnings = true; + entityTree->deleteEntitiesByID(idsToDelete, force, ignoreWarnings); } } diff --git a/interface/src/graphics/WorldBox.h b/interface/src/graphics/WorldBox.h index 114777ba0f..4d53652c0e 100644 --- a/interface/src/graphics/WorldBox.h +++ b/interface/src/graphics/WorldBox.h @@ -19,8 +19,6 @@ #include #include "Menu.h" - - class WorldBoxRenderData { public: typedef render::Payload Payload; @@ -29,8 +27,6 @@ public: int _val = 0; static render::ItemID _item; // unique WorldBoxRenderData - - static void renderWorldBox(RenderArgs* args, gpu::Batch& batch); }; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 81616e5773..ae476b8142 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -72,7 +72,7 @@ int main(int argc, const char* argv[]) { } QCommandLineParser parser; - parser.setApplicationDescription("High Fidelity"); + parser.setApplicationDescription("Vircadia"); QCommandLineOption versionOption = parser.addVersionOption(); QCommandLineOption helpOption = parser.addHelpOption(); @@ -218,12 +218,12 @@ int main(int argc, const char* argv[]) { } qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled(); - if (ual.isEnabled()) { + qDebug() << "Crash handler logger is enabled:" << ual.isCrashMonitorEnabled(); + if (ual.isCrashMonitorEnabled()) { auto crashHandlerStarted = startCrashHandler(argv[0]); qDebug() << "Crash handler started:" << crashHandlerStarted; } - const QString& applicationName = getInterfaceSharedMemoryName(); bool instanceMightBeRunning = true; #ifdef Q_OS_WIN @@ -379,7 +379,7 @@ int main(int argc, const char* argv[]) { PROFILE_SYNC_END(startup, "app full ctor", ""); #if defined(Q_OS_LINUX) - app.setWindowIcon(QIcon(PathUtils::resourcesPath() + "images/hifi-logo.svg")); + app.setWindowIcon(QIcon(PathUtils::resourcesPath() + "images/vircadia-logo.svg")); #endif QTimer exitTimer; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 3a7b67a8e9..1f940f761d 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -99,9 +99,11 @@ PickFilter getPickFilter(unsigned int filter) { * @property {Vec3} [dirOffset] - Synonym for direction. * @property {Quat} [orientation] - Alternative property for specifying direction. The value is applied to the * default direction value. - * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or {@link Picks.getPickScriptParameters}. A ray pick's type is {@link PickType.Ray}. - * @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale (usually an avatar or an entity). - * Its value is the original scale of the parent at the moment the pick was created, and is used to scale the pointer which owns this pick, if any. + * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or + * {@link Picks.getPickScriptParameters}. A ray pick's type is {@link PickType.Ray}. + * @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale + * (usually an avatar or an entity). Its value is the original scale of the parent at the moment the pick was created, and + * is used to scale the pointer which owns this pick, if any. */ std::shared_ptr PickScriptingInterface::buildRayPick(const QVariantMap& propMap) { #if defined (Q_OS_ANDROID) @@ -170,7 +172,8 @@ std::shared_ptr PickScriptingInterface::buildRayPick(const QVariantMa * means no maximum. * @property {Vec3} [tipOffset=0,0.095,0] - The position of the stylus tip relative to the hand position at default avatar * scale. - * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or {@link Picks.getPickScriptParameters}. A stylus pick's type is {@link PickType.Stylus}. + * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or + * {@link Picks.getPickScriptParameters}. A stylus pick's type is {@link PickType.Stylus}. */ std::shared_ptr PickScriptingInterface::buildStylusPick(const QVariantMap& propMap) { bilateral::Side side = bilateral::Side::Invalid; @@ -204,7 +207,8 @@ std::shared_ptr PickScriptingInterface::buildStylusPick(const QVarian return std::make_shared(side, filter, maxDistance, enabled, tipOffset); } -// NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API. +// NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar +// should not be removed from the pick API. /**jsdoc * The properties of a parabola pick. * @@ -245,9 +249,11 @@ std::shared_ptr PickScriptingInterface::buildStylusPick(const QVarian * with the avatar or other parent. * @property {boolean} [scaleWithAvatar=true] - Synonym for scalewithParent. *

Deprecated: This property is deprecated and will be removed.

- * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or {@link Picks.getPickScriptParameters}. A parabola pick's type is {@link PickType.Parabola}. - * @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale (usually an avatar or an entity). - * Its value is the original scale of the parent at the moment the pick was created, and is used to rescale the pick, and/or the pointer which owns this pick, if any. + * @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or + * {@link Picks.getPickScriptParameters}. A parabola pick's type is {@link PickType.Parabola}. + * @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale + * (usually an avatar or an entity). Its value is the original scale of the parent at the moment the pick was created, and + * is used to rescale the pick and the pointer which owns this pick, if any. */ std::shared_ptr PickScriptingInterface::buildParabolaPick(const QVariantMap& propMap) { bool enabled = false; diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index e26b91b9a2..58ed3326ec 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -52,6 +52,10 @@ * Read-only. *

Warning: Not yet implemented.

* + * @property {FilterFlags} PICK_BYPASS_IGNORE - Allows pick to intersect entities even when their + * ignorePickIntersection property value is true. For debug purposes. + * Read-only. + * * @property {IntersectionType} INTERSECTED_NONE - Intersected nothing. Read-only. * @property {IntersectionType} INTERSECTED_ENTITY - Intersected an entity. Read-only. * @property {IntersectionType} INTERSECTED_LOCAL_ENTITY - Intersected a local entity. Read-only. @@ -87,6 +91,8 @@ class PickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT) + Q_PROPERTY(unsigned int PICK_BYPASS_IGNORE READ PICK_BYPASS_IGNORE CONSTANT) + Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_LOCAL_ENTITY READ INTERSECTED_LOCAL_ENTITY CONSTANT) @@ -147,19 +153,20 @@ public: * Gets the current properties of the pick. * @function Picks.getPickProperties * @param {number} id - The ID of the pick. - * @returns {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties} Properties of the pick, per the pick type. + * @returns {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties} + * Properties of the pick, per the pick type. */ Q_INVOKABLE QVariantMap getPickProperties(unsigned int uid) const; /**jsdoc - * Gets the parameters that were passed in to {@link Picks.createPick} to create the pick, - * if the pick was created through a script. - * Note that these properties do not reflect the current state of the pick. - * See {@link Picks.getPickProperties}. - * @function Picks.getPickScriptParameters - * @param {number} id - The ID of the pick. - * @returns {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties} User-provided properties, per the pick type. - */ + * Gets the parameters that were passed in to {@link Picks.createPick} to create the pick, if the pick was created through + * a script. Note that these properties do not reflect the current state of the pick. + * See {@link Picks.getPickProperties}. + * @function Picks.getPickScriptParameters + * @param {number} id - The ID of the pick. + * @returns {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties} + * Script-provided properties, per the pick type. + */ Q_INVOKABLE QVariantMap getPickScriptParameters(unsigned int uid) const; /**jsdoc @@ -282,6 +289,8 @@ public: unsigned int getPerFrameTimeBudget() const; void setPerFrameTimeBudget(unsigned int numUsecs); + static constexpr unsigned int PICK_BYPASS_IGNORE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_BYPASS_IGNORE); } + public slots: /**jsdoc diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 3520aacbd0..a3aeb314e5 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -89,10 +89,13 @@ QVariantMap PointerScriptingInterface::getPointerScriptParameters(unsigned int u * @property {Pointers.StylusPointerModel} [model] - Override some or all of the default stylus model properties. * @property {boolean} [hover=false] - true if the pointer generates {@link Entities} hover events, * false if it doesn't. - * @property {PickType} pointerType - The type of pointer when getting these properties from {@link Pointers.getPointerProperties} or {@link Pointers.getPointerScriptParameters}. A stylus pointer's type is {@link PickType.Stylus}. - * @property {number} [pickID] - Returned from {@link Pointers.getPointerProperties}. The ID of the pick created alongside this pointer. + * @property {PickType} pointerType - The type of the stylus pointer returned from {@link Pointers.getPointerProperties} + * or {@link Pointers.getPointerScriptParameters}. A stylus pointer's type is {@link PickType(0)|PickType.Stylus}. + * @property {number} [pickID] - The ID of the pick created alongside this pointer, returned from + * {@link Pointers.getPointerProperties}. * @see {@link Picks.StylusPickProperties} for additional properties from the underlying stylus pick. */ + /**jsdoc * The properties of a stylus pointer model. * @typedef {object} Pointers.StylusPointerModel @@ -208,8 +211,10 @@ std::shared_ptr PointerScriptingInterface::buildStylus(const QVariant& * false if it doesn't. * @property {Pointers.Trigger[]} [triggers=[]] - A list of ways that a {@link Controller} action or function should trigger * events on the entity or overlay currently intersected. - * @property {PickType} pointerType - The type of pointer when getting these properties from {@link Pointers.getPointerProperties} or {@link Pointers.getPointerScriptParameters}. A laser pointer's type is {@link PickType.Ray}. - * @property {number} [pickID] - Returned from {@link Pointers.getPointerProperties}. The ID of the pick created alongside this pointer. + * @property {PickType} pointerType - The type of pointer returned from {@link Pointers.getPointerProperties} or + * {@link Pointers.getPointerScriptParameters}. A laser pointer's type is {@link PickType(0)|PickType.Ray}. + * @property {number} [pickID] - The ID of the pick created alongside this pointer, returned from + * {@link Pointers.getPointerProperties}. * @see {@link Picks.RayPickProperties} for additional properties from the underlying ray pick. */ std::shared_ptr PointerScriptingInterface::buildLaserPointer(const QVariant& properties) { @@ -401,8 +406,10 @@ std::shared_ptr PointerScriptingInterface::buildLaserPointer(const QVar * false if it doesn't. * @property {Pointers.Trigger[]} [triggers=[]] - A list of ways that a {@link Controller} action or function should trigger * events on the entity or overlay currently intersected. - * @property {PickType} pointerType - The type of pointer when getting these properties from {@link Pointers.getPointerProperties} or {@link Pointers.getPointerScriptParameters}. A parabola pointer's type is {@link PickType.Parabola}. - * @property {number} [pickID] - Returned from {@link Pointers.getPointerProperties}. The ID of the pick created alongside this pointer. + * @property {PickType} pointerType - The type of pointer returned from {@link Pointers.getPointerProperties} or + * {@link Pointers.getPointerScriptParameters}. A parabola pointer's type is {@link PickType(0)|PickType.Parabola}. + * @property {number} [pickID] - The ID of the pick created alongside this pointer, returned from + * {@link Pointers.getPointerProperties}. * @see {@link Picks.ParabolaPickProperties} for additional properties from the underlying parabola pick. */ std::shared_ptr PointerScriptingInterface::buildParabolaPointer(const QVariant& properties) { diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 2d2f3f6dae..555136e7a7 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -161,13 +161,14 @@ public: Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get()->removePointer(uid); } /**jsdoc - * Gets the parameters that were passed in to {@link Pointers.createPointer} to create the pointer, - * if the pointer was created through a script. - * Note that these properties do not reflect the current state of the pointer. - * See {@link Pointers.getPointerProperties}. + * Gets the parameters that were passed in to {@link Pointers.createPointer} to create the pointer when the pointer was + * created through a script. + *

Note: These properties do not reflect the current state of the pointer. To get the current state + * of the pointer, see {@link Pointers.getPointerProperties}. * @function Pointers.getPointerScriptParameters * @param {number} id - The ID of the pointer. - * @returns {Pointers.RayPointerProperties|Picks.ParabolaPointerProperties|Picks.StylusPointerProperties} User-provided properties, per the pointer type. + * @returns {Pointers.RayPointerProperties|Pointers.ParabolaPointerProperties|Pointers.StylusPointerProperties} + * Script-provided properties, per the pointer type. */ Q_INVOKABLE QVariantMap getPointerScriptParameters(unsigned int uid) const; diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 3ad637d0e6..288137e166 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -46,6 +46,7 @@ class AccountServicesScriptingInterface : public QObject { * @hifi-avatar * * @namespace AccountServices + * * @property {string} username - The user name of the user logged in. If there is no user logged in, it is * "Unknown user". Read-only. * @property {boolean} loggedIn - true if the user is logged in, otherwise false. @@ -61,6 +62,86 @@ class AccountServicesScriptingInterface : public QObject { * — typically "https://metaverse.highfidelity.com". Read-only. */ + /**jsdoc + * The Account API provides functions that give information on user connectivity, visibility, and asset + * download progress. + * + * @deprecated This API is the same as the {@link AccountServices} API and will be removed. + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @namespace Account + * + * @property {string} username - The user name of the user logged in. If there is no user logged in, it is + * "Unknown user". Read-only. + * @property {boolean} loggedIn - true if the user is logged in, otherwise false. + * Read-only. + * @property {string} findableBy - The user's visibility to other users: + *

    + *
  • "none" — user appears offline.
  • + *
  • "friends" — user is visible only to friends.
  • + *
  • "connections" — user is visible to friends and connections.
  • + *
  • "all" — user is visible to everyone.
  • + *
+ * @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in + * — typically "https://metaverse.highfidelity.com". Read-only. + * + * @borrows AccountServices.getDownloadInfo as getDownloadInfo + * @borrows AccountServices.updateDownloadInfo as updateDownloadInfo + * @borrows AccountServices.isLoggedIn as isLoggedIn + * @borrows AccountServices.checkAndSignalForAccessToken as checkAndSignalForAccessToken + * @borrows AccountServices.logOut as logOut + * + * @borrows AccountServices.connected as connected + * @borrows AccountServices.disconnected as disconnected + * @borrows AccountServices.myUsernameChanged as myUsernameChanged + * @borrows AccountServices.downloadInfoChanged as downloadInfoChanged + * @borrows AccountServices.findableByChanged as findableByChanged + * @borrows AccountServices.loggedInChanged as loggedInChanged + */ + + /**jsdoc + * The GlobalServices API provides functions that give information on user connectivity, visibility, and asset + * download progress. + * + * @deprecated This API is the same as the {@link AccountServices} API and will be removed. + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @namespace GlobalServices + * + * @property {string} username - The user name of the user logged in. If there is no user logged in, it is + * "Unknown user". Read-only. + * @property {boolean} loggedIn - true if the user is logged in, otherwise false. + * Read-only. + * @property {string} findableBy - The user's visibility to other users: + *
    + *
  • "none" — user appears offline.
  • + *
  • "friends" — user is visible only to friends.
  • + *
  • "connections" — user is visible to friends and connections.
  • + *
  • "all" — user is visible to everyone.
  • + *
+ * @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in + * — typically "https://metaverse.highfidelity.com". Read-only. + * + * @borrows AccountServices.getDownloadInfo as getDownloadInfo + * @borrows AccountServices.updateDownloadInfo as updateDownloadInfo + * @borrows AccountServices.isLoggedIn as isLoggedIn + * @borrows AccountServices.checkAndSignalForAccessToken as checkAndSignalForAccessToken + * @borrows AccountServices.logOut as logOut + * + * @borrows AccountServices.connected as connected + * @borrows AccountServices.disconnected as disconnected + * @borrows AccountServices.myUsernameChanged as myUsernameChanged + * @borrows AccountServices.downloadInfoChanged as downloadInfoChanged + * @borrows AccountServices.findableByChanged as findableByChanged + * @borrows AccountServices.loggedInChanged as loggedInChanged + */ + Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged) Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged) Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged) diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index c0a6b64421..af7ac8165b 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -60,10 +60,11 @@ bool ClipboardScriptingInterface::importEntities( return retVal; } -QVector ClipboardScriptingInterface::pasteEntities(glm::vec3 position) { +QVector ClipboardScriptingInterface::pasteEntities(glm::vec3 position, const QString& entityHostType) { QVector retVal; BLOCKING_INVOKE_METHOD(qApp, "pasteEntities", Q_RETURN_ARG(QVector, retVal), + Q_ARG(const QString&, entityHostType), Q_ARG(float, position.x), Q_ARG(float, position.y), Q_ARG(float, position.z)); diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 9e72d9ea15..9660b2158b 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -117,10 +117,11 @@ public: * Pastes the contents of the clipboard into the domain. * @function Clipboard.pasteEntities * @param {Vec3} position - The position to paste the clipboard contents at. + * @param {Entities.EntityHostType} [entityHostType="domain"] - The type of entities to create. * @returns {Uuid[]} The IDs of the new entities that were created as a result of the paste operation. If entities couldn't * be created then an empty array is returned. */ - Q_INVOKABLE QVector pasteEntities(glm::vec3 position); + Q_INVOKABLE QVector pasteEntities(glm::vec3 position, const QString& entityHostType = "domain"); }; #endif // hifi_ClipboardScriptingInterface_h diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index ae4af48cd6..f78f7853ca 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -30,21 +30,6 @@ * @property {InteractiveWindow.DockArea} LEFT - Dock to the left edge of the Interface window. * @property {InteractiveWindow.DockArea} RIGHT - Dock to the right edge of the Interface window. */ -/**jsdoc - *

A docking location of an InteractiveWindow.

- * - * - * - * - * - * - * - * - * - * - *
ValueNameDescription
0TOPDock to the top edge of the Interface window.
1BOTTOMDock to the bottom edge of the Interface window.
2LEFTDock to the left edge of the Interface window.
3RIGHTDock to the right edge of the Interface window.
- * @typedef {number} InteractiveWindow.DockArea - */ static const QVariantMap DOCK_AREA { { "TOP", DockArea::TOP }, { "BOTTOM", DockArea::BOTTOM }, @@ -53,13 +38,17 @@ static const QVariantMap DOCK_AREA { }; /**jsdoc - * The possible "relative position anchors" of an InteractiveWindow. Used when defining the `relativePosition` property of an `InteractiveWindow`. + * The possible relative position anchors of an InteractiveWindow relative to the Interface window. * @typedef {object} InteractiveWindow.RelativePositionAnchors - * @property {InteractiveWindow.RelativePositionAnchor} NO_ANCHOR - Specifies that the position of the `InteractiveWindow` will not be relative to any part of the Interface window. - * @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window. - * @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window. - * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window. - * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} NO_ANCHOR - Position is not relative to any part of the Interface + * window. + * @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Position is offset from the top left of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Position is offset from the top right of the Interface + * window. + * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Position offset from the bottom right of the Interface + * window. + * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Position is offset from the bottom left of the Interface + * window. */ static const QVariantMap RELATIVE_POSITION_ANCHOR { { "NO_ANCHOR", RelativePositionAnchor::NO_ANCHOR }, @@ -89,21 +78,6 @@ int DesktopScriptingInterface::getHeight() { * @property {InteractiveWindow.PresentationMode} NATIVE - The window is displayed separately from the Interface window, as its * own separate window. */ -/**jsdoc - *

A display mode for an InteractiveWindow.

- * - * - * - * - * - * - * - * - *
ValueNameDescription
0VIRTUALThe window is displayed inside Interface: in the desktop window in - * desktop mode or on the HUD surface in HMD mode.
1NATIVEThe window is displayed separately from the Interface window, as its - * own separate window.
- * @typedef {number} InteractiveWindow.PresentationMode - */ QVariantMap DesktopScriptingInterface::getPresentationMode() { static QVariantMap presentationModes { { "VIRTUAL", Virtual }, diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h index c25f382891..e57d7a6805 100644 --- a/interface/src/scripting/DesktopScriptingInterface.h +++ b/interface/src/scripting/DesktopScriptingInterface.h @@ -22,7 +22,7 @@ /**jsdoc * The Desktop API provides the dimensions of the computer screen, sets the opacity of the HUD surface, and * enables QML and HTML windows to be shown inside or outside of Interface. - * + * * @namespace Desktop * * @hifi-interface @@ -42,8 +42,8 @@ * @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top, * bottom, left, or right of the Interface window. * Read-only. - * @property {InteractiveWindow.RelativePositionAnchors} RelativePositionAnchor - The possible "relative position anchors" for an {@link InteractiveWindow}: top left, - * top right, bottom right, or bottom left of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchors} RelativePositionAnchor - The possible relative position anchors for an + * {@link InteractiveWindow}: none, top left, top right, bottom right, or bottom left of the Interface window. * Read-only. */ class DesktopScriptingInterface : public QObject, public Dependency { @@ -84,7 +84,7 @@ public: * @param {string} url - The QML file that specifies the window content. The QML file can use a WebView * control (defined by "WebView.qml" included in the Interface install) to embed an HTML web page (complete with * EventBridge object). - * @param {InteractiveWindow.Properties} [properties] - Initial window properties. + * @param {InteractiveWindow.WindowProperties} [properties] - Initial window properties. * @returns {InteractiveWindow} A new window object. * @example Open a dialog in its own window separate from Interface. * var nativeWindow = Desktop.createWindow(Script.resourcesPath() + 'qml/OverlayWindowTest.qml', { diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.h b/interface/src/scripting/DialogsManagerScriptingInterface.h index b223799cfe..ee1baeecf5 100644 --- a/interface/src/scripting/DialogsManagerScriptingInterface.h +++ b/interface/src/scripting/DialogsManagerScriptingInterface.h @@ -14,19 +14,56 @@ #include +/**jsdoc + * The DialogsMamnager API provides facilities to work with some key dialogs. + * + * @namespace DialogsManager + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + */ + class DialogsManagerScriptingInterface : public QObject { Q_OBJECT public: DialogsManagerScriptingInterface(); static DialogsManagerScriptingInterface* getInstance(); + + /**jsdoc + * Currently performs no action. + * @function DialogsManager.showFeed + */ Q_INVOKABLE void showFeed(); public slots: + /**jsdoc + * Shows the "Goto" dialog. + * @function DialogsManager.showAddressBar + */ void showAddressBar(); + + /**jsdoc + * Hides the "Goto" dialog. + * @function DialogsManager.hideAddressBar + */ void hideAddressBar(); + + /**jsdoc + * Shows the login dialog. + * @function DialogsManager.showLoginDialog + */ void showLoginDialog(); signals: + /**jsdoc + * Triggered when the "Goto" dialog is opened or closed. + *

Warning: Currently isn't always triggered.

+ * @function DialogsManager.addressBarShown + * @param {boolean} visible - true if the Goto dialog has been opened, false if it has been + * closed. + * @returns {Signal} + */ void addressBarShown(bool visible); }; diff --git a/interface/src/scripting/PerformanceScriptingInterface.h b/interface/src/scripting/PerformanceScriptingInterface.h index 92d4273dfb..8a7e403e5d 100644 --- a/interface/src/scripting/PerformanceScriptingInterface.h +++ b/interface/src/scripting/PerformanceScriptingInterface.h @@ -18,6 +18,18 @@ #include "../RefreshRateManager.h" +/**jsdoc + * The Performance API provides control and information on graphics performance settings. + * + * @namespace Performance + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {Performance.PerformancePreset} performancePreset - The current graphics performance preset. + * @property {Performance.RefreshRateProfile} refreshRateProfile - The current refresh rate profile. + */ class PerformanceScriptingInterface : public QObject { Q_OBJECT Q_PROPERTY(PerformancePreset performancePreset READ getPerformancePreset WRITE setPerformancePreset NOTIFY settingsChanged) @@ -25,6 +37,23 @@ class PerformanceScriptingInterface : public QObject { public: + /**jsdoc + *

Graphics performance presets.

+ * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0UNKNOWNCustom settings of world detail, rendering effects, and refresh + * rate.
1LOWLow world detail, no rendering effects, lo refresh rate.
2MIDMedium world detail, some rendering effects, medium refresh + * rate.
3HIGHMaximum world detail, all rendering effects, high refresh rate.
+ * @typedef {number} Performance.PerformancePreset + */ // PerformanceManager PerformancePreset tri state level enums enum PerformancePreset { UNKNOWN = PerformanceManager::PerformancePreset::UNKNOWN, @@ -34,6 +63,23 @@ public: }; Q_ENUM(PerformancePreset) + /**jsdoc + *

Refresh rate profile.

+ * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0ECOLow refresh rate, which is reduced when Interface doesn't have focus or + * is minimized.
1INTERACTIVEMedium refresh rate, which is reduced when Interface doesn't have + * focus or is minimized.
2REALTIMEHigh refresh rate, even when Interface doesn't have focus or is + * minimized.
+ * @typedef {number} Performance.RefreshRateProfile + */ // Must match RefreshRateManager enums enum RefreshRateProfile { ECO = RefreshRateManager::RefreshRateProfile::ECO, @@ -47,19 +93,81 @@ public: public slots: + /**jsdoc + * Sets graphics performance to a preset. + * @function Performance.setPerformancePreset + * @param {Performance.PerformancePreset} performancePreset - The graphics performance preset to to use. + */ void setPerformancePreset(PerformancePreset performancePreset); + + /**jsdoc + * Gets the current graphics performance preset in use. + * @function Performance.getPerformancePreset + * @returns {Performance.PerformancePreset} The current graphics performance preset in use. + */ PerformancePreset getPerformancePreset() const; + + /**jsdoc + * Gets the names of the graphics performance presets. + * @function Performance.getPerformancePresetNames + * @returns {string[]} The names of the graphics performance presets. The array index values correspond to + * {@link Performance.PerformancePreset} values. + */ QStringList getPerformancePresetNames() const; + + /**jsdoc + * Sets the curfrent refresh rate profile. + * @function Performance.setRefreshRateProfile + * @param {Performance.RefreshRateProfile} refreshRateProfile - The refresh rate profile. + */ void setRefreshRateProfile(RefreshRateProfile refreshRateProfile); + + /**jsdoc + * Gets the current refresh rate profile in use. + * @function Performance.getRefreshRateProfile + * @returns {Performance.RefreshRateProfile} The refresh rate profile. + */ RefreshRateProfile getRefreshRateProfile() const; + + /**jsdoc + * Gets the names of the refresh rate profiles. + * @function Performance.getRefreshRateProfileNames + * @returns {string[]} The names of the refresh rate profiles. The array index values correspond to + * {@link Performance.RefreshRateProfile} values. + */ QStringList getRefreshRateProfileNames() const; + + /**jsdoc + * Gets the current target refresh rate, in Hz, per the current refresh rate profile and refresh rate regime if in desktop + * mode; a higher rate if in VR mode. + * @function Performance.getActiveRefreshRate + * @returns {number} The current target refresh rate, in Hz. + */ int getActiveRefreshRate() const; + + /**jsdoc + * Gets the current user experience mode. + * @function Performance.getUXMode + * @returns {UXMode} The current user experience mode. + */ RefreshRateManager::UXMode getUXMode() const; + + /**jsdoc + * Gets the current refresh rate regime that's in effect. + * @function Performance.getRefreshRateRegime + * @returns {RefreshRateRegime} The current refresh rate regime. + */ RefreshRateManager::RefreshRateRegime getRefreshRateRegime() const; signals: + + /**jsdoc + * Triggered when the performance preset or refresh rate profile is changed. + * @function Performance.settingsChanged + * @returns {Signal} + */ void settingsChanged(); private: diff --git a/interface/src/scripting/RatesScriptingInterface.h b/interface/src/scripting/RatesScriptingInterface.h index 5658ed99a0..6feef94d17 100644 --- a/interface/src/scripting/RatesScriptingInterface.h +++ b/interface/src/scripting/RatesScriptingInterface.h @@ -14,6 +14,41 @@ #include +/**jsdoc + * The Rates API provides some information on current rendering performance. + * + * @namespace Rates + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {number} render - The rate at which new GPU frames are being created, in Hz. + * Read-only. + * @property {number} present - The rate at which the display plugin is presenting to the display device, in Hz. + * Read-only. + * @property {number} newFrame - The rate at which the display plugin is presenting new GPU frames, in Hz. + * Read-only. + * @property {number} dropped - The rate at which the display plugin is dropping GPU frames, in Hz. + * Read-only. + * @property {number} simulation - The rate at which the game loop is running, in Hz. + * Read-only. + * + * @example Report current rendering rates. + * // The rates to report. + * var rates = [ + * "render", + * "present", + * "newFrame", + * "dropped", + * "simulation" + * ]; + * + * // Report the rates. + * for (var i = 0; i < rates.length; i++) { + * print("Rates." + rates[i], "=", Rates[rates[i]]); + * } + */ class RatesScriptingInterface : public QObject { Q_OBJECT diff --git a/interface/src/scripting/ScreenshareScriptingInterface.cpp b/interface/src/scripting/ScreenshareScriptingInterface.cpp new file mode 100644 index 0000000000..3bf8336fe4 --- /dev/null +++ b/interface/src/scripting/ScreenshareScriptingInterface.cpp @@ -0,0 +1,348 @@ +// +// ScreenshareScriptingInterface.cpp +// interface/src/scripting/ +// +// Created by Milad Nazeri and Zach Fox on 2019-10-23. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "EntityScriptingInterface.h" +#include "ScreenshareScriptingInterface.h" + +static const int SCREENSHARE_INFO_REQUEST_RETRY_TIMEOUT_MS = 300; +ScreenshareScriptingInterface::ScreenshareScriptingInterface() { + auto esi = DependencyManager::get(); + if (!esi) { + return; + } + + // This signal/slot connection is used when the screen share local web entity sends an event bridge message. + QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, &ScreenshareScriptingInterface::onWebEventReceived); + + _requestScreenshareInfoRetryTimer = new QTimer; + _requestScreenshareInfoRetryTimer->setSingleShot(true); + _requestScreenshareInfoRetryTimer->setInterval(SCREENSHARE_INFO_REQUEST_RETRY_TIMEOUT_MS); + connect(_requestScreenshareInfoRetryTimer, &QTimer::timeout, this, &ScreenshareScriptingInterface::requestScreenshareInfo); + + // This packet listener handles the packet containing information about the latest zone ID in which we are allowed to share. + auto nodeList = DependencyManager::get(); + PacketReceiver& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacketOnClient"); +}; + +ScreenshareScriptingInterface::~ScreenshareScriptingInterface() { + stopScreenshare(); +} + +void ScreenshareScriptingInterface::processAvatarZonePresencePacketOnClient(QSharedPointer message) { + QUuid zone = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (zone.isNull()) { + qWarning() << "Ignoring avatar zone presence packet that doesn't specify a zone."; + return; + } + + // Set the last known authorized screenshare zone ID to the zone that the Domain Server just told us about. + _lastAuthorizedZoneID = zone; + + // If we had previously started the screenshare process but knew that we weren't going to be authorized to screenshare, + // let's continue the screenshare process here. + if (_waitingForAuthorization) { + requestScreenshareInfo(); + } +} + +static const int MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES = 5; +void ScreenshareScriptingInterface::requestScreenshareInfo() { + // If the screenshare zone that we're currently in (i.e. `startScreenshare()` was called) is different from + // the zone in which we are authorized to screenshare... + // ...return early here and wait for the DS to send us a packet containing this zone's ID. + if (_screenshareZoneID != _lastAuthorizedZoneID) { + qDebug() << "Client not yet authorized to screenshare. Waiting for authorization message from domain server..."; + _waitingForAuthorization = true; + return; + } + + _waitingForAuthorization = false; + + _requestScreenshareInfoRetries++; + + if (_requestScreenshareInfoRetries >= MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES) { + qDebug() << "Maximum number of retries for screenshare info exceeded. Screenshare will not function."; + return; + } + + // Don't continue with any more of this logic if we can't get the `AccountManager` or `AddressManager`. + auto accountManager = DependencyManager::get(); + if (!accountManager) { + return; + } + auto addressManager = DependencyManager::get(); + if (!addressManager) { + return; + } + + // Construct and send a request to the Metaverse to obtain the information + // necessary to start the screen sharing process. + // This request requires: + // 1. The domain ID of the domain in which the user's avatar is present + // 2. User authentication information that is automatically included when `sendRequest()` is passed + // with the `AccountManagerAuth::Required` argument. + // Note that this request will only return successfully if the Domain Server has already registered + // the user paired with the current domain with the Metaverse. + // See `DomainServer::screensharePresence()` for more info about that. + + QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID()); + QString requestURLPath = "api/v1/domains/%1/screenshare"; + JSONCallbackParameters callbackParams; + callbackParams.callbackReceiver = this; + callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet"; + callbackParams.errorCallbackMethod = "handleFailedScreenshareInfoGet"; + accountManager->sendRequest( + requestURLPath.arg(currentDomainID), + AccountManagerAuth::Required, + QNetworkAccessManager::GetOperation, + callbackParams + ); +} + +static const EntityTypes::EntityType LOCAL_SCREENSHARE_WEB_ENTITY_TYPE = EntityTypes::Web; +static const uint8_t LOCAL_SCREENSHARE_WEB_ENTITY_FPS = 30; +// This is going to be a good amount of work to make this work dynamically for any screensize. +// V1 will have only hardcoded values. +// The `z` value here is dynamic. +static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0128f, -0.0918f, 0.0f); +static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(3.6790f, 2.0990f, 0.0100f); +static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL = + "https://content.highfidelity.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html"; +static const QString LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE = "local"; +void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID, + const QUuid& smartboardEntityID, + const bool& isPresenter) { + // We must start a new QProcess from the main thread. + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "startScreenshare", Q_ARG(const QUuid&, screenshareZoneID), + Q_ARG(const QUuid&, smartboardEntityID), Q_ARG(const bool&, isPresenter)); + return; + } + + // These three private member variables are set now so that they may be used later during asynchronous + // callbacks. + _screenshareZoneID = screenshareZoneID; + _smartboardEntityID = smartboardEntityID; + _isPresenter = isPresenter; + + // If we are presenting, and the screenshare process is already running, don't do anything else here. + if (_isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) { + return; + } + + // If we're presenting... + if (_isPresenter) { + // ...make sure we first reset this `std::unique_ptr`. + _screenshareProcess.reset(new QProcess(this)); + + // Ensure that the screenshare executable exists where we expect it to. + // Error out and reset the screen share state machine if the executable doesn't exist. + QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH); + if (!screenshareExecutable.exists() || !(screenshareExecutable.isFile() || screenshareExecutable.isBundle())) { + qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH; + stopScreenshare(); + emit screenshareError(); + return; + } + } + + if (_requestScreenshareInfoRetryTimer && _requestScreenshareInfoRetryTimer->isActive()) { + _requestScreenshareInfoRetryTimer->stop(); + } + + _requestScreenshareInfoRetries = 0; + requestScreenshareInfo(); +} + +void ScreenshareScriptingInterface::stopScreenshare() { + // We can only deal with our Screen Share `QProcess` on the main thread. + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopScreenshare"); + return; + } + + // If the retry timer is active, stop it. + if (_requestScreenshareInfoRetryTimer && _requestScreenshareInfoRetryTimer->isActive()) { + _requestScreenshareInfoRetryTimer->stop(); + } + + // If the Screen Share process is running... + if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) { + //...terminate it and make sure that scripts know we terminated it by emitting + // `screenshareProcessTerminated()`. + _screenshareProcess->terminate(); + } + + // Delete the local web entity if we know about it here. + if (!_screenshareViewerLocalWebEntityUUID.isNull()) { + auto esi = DependencyManager::get(); + if (esi) { + esi->deleteEntity(_screenshareViewerLocalWebEntityUUID); + } + } + + // Reset all private member variables related to screen share here. + _screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}"; + _token = ""; + _projectAPIKey = ""; + _sessionID = ""; + _isPresenter = false; + _waitingForAuthorization = false; +} + +// Called when the Metaverse returns the information necessary to start/view a screen share. +void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkReply* reply) { + // Read the reply and get it into a format we understand. + QString answer = reply->readAll(); + QByteArray answerByteArray = answer.toUtf8(); + QJsonDocument answerJSONObject = QJsonDocument::fromJson(answerByteArray); + + // This Metaverse endpoint will always return a status key/value pair of "success" if things went well. + QString status = answerJSONObject["status"].toString(); + if (status != "success") { + qDebug() << "Error when retrieving screenshare info via HTTP. Error:" << reply->errorString(); + stopScreenshare(); + emit screenshareError(); + return; + } + + // Store the information necessary to start/view a screen share in these private member variables. + _token = answerJSONObject["token"].toString(); + _projectAPIKey = answerJSONObject["projectApiKey"].toString(); + _sessionID = answerJSONObject["sessionID"].toString(); + + // Make sure we have all of the info that we need. + if (_token.isEmpty() || _projectAPIKey.isEmpty() || _sessionID.isEmpty()) { + qDebug() << "Not all Screen Share information was retrieved from the backend. Stopping..."; + stopScreenshare(); + emit screenshareError(); + return; + } + + // If we're presenting: + // 1. Build a list of arguments that we're going to pass to the screen share Electron app. + // 2. Make sure we connect a signal/slot to know when the user quits the Electron app. + // 3. Start the screen share Electron app with the list of args from (1). + if (_isPresenter) { + QStringList arguments; + arguments << " "; + arguments << "--token=" + _token << " "; + arguments << "--projectAPIKey=" + _projectAPIKey << " "; + arguments << "--sessionID=" + _sessionID << " "; + + connect(_screenshareProcess.get(), QOverload::of(&QProcess::finished), + [=](int exitCode, QProcess::ExitStatus exitStatus) { + stopScreenshare(); + emit screenshareProcessTerminated(); + }); + + _screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments); + } + + // Make sure we can grab the entity scripting interface. Error out if we can't. + auto esi = DependencyManager::get(); + if (!esi) { + stopScreenshare(); + emit screenshareError(); + return; + } + + // If, for some reason, we already have a record of a screen share local Web entity, delete it. + if (!_screenshareViewerLocalWebEntityUUID.isNull()) { + esi->deleteEntity(_screenshareViewerLocalWebEntityUUID); + } + + // Set up the entity properties associated with the screen share local Web entity. + EntityItemProperties localScreenshareWebEntityProps; + localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE); + localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS); + glm::vec3 localPosition(LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION); + localPosition.z = _localWebEntityZOffset; + localScreenshareWebEntityProps.setLocalPosition(localPosition); + localScreenshareWebEntityProps.setSourceUrl(LOCAL_SCREENSHARE_WEB_ENTITY_URL); + localScreenshareWebEntityProps.setParentID(_smartboardEntityID); + localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS); + + // The lines below will be used when writing the feature to support scaling the Smartboard entity to any arbitrary size. + //EntityPropertyFlags desiredSmartboardProperties; + //desiredSmartboardProperties += PROP_POSITION; + //desiredSmartboardProperties += PROP_DIMENSIONS; + //EntityItemProperties smartboardProps = esi->getEntityProperties(_smartboardEntityID, desiredSmartboardProperties); + + // Add the screen share local Web entity to Interface's entity tree. + // When the Web entity loads the page specified by `LOCAL_SCREENSHARE_WEB_ENTITY_URL`, it will broadcast an Event Bridge + // message, which we will consume inside `ScreenshareScriptingInterface::onWebEventReceived()`. + _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE); +} + +void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply* reply) { + if (_requestScreenshareInfoRetries >= MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES) { + qDebug() << "Failed to get screenshare info via HTTP after" << MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES << "retries. Error:" << reply->errorString(); + stopScreenshare(); + emit screenshareError(); + return; + } + + _requestScreenshareInfoRetryTimer->start(); +} + +// This function will handle _all_ web events received via `EntityScriptingInterface::webEventReceived()`, including +// those not related to screen sharing. +void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, const QVariant& message) { + // Bail early if the entity that sent the Web event isn't the one we care about. + if (entityID == _screenshareViewerLocalWebEntityUUID) { + // Bail early if we can't grab the Entity Scripting Interface. + auto esi = DependencyManager::get(); + if (!esi) { + return; + } + + // Web events received from the screen share Web JS will always be in stringified JSON format. + QByteArray jsonByteArray = QVariant(message).toString().toUtf8(); + QJsonDocument jsonObject = QJsonDocument::fromJson(jsonByteArray); + + // It should never happen where the screen share Web JS sends a message without the `app` key's value + // set to "screenshare". + if (jsonObject["app"] != "screenshare") { + return; + } + + // The screen share Web JS only sends a message with one method: "eventBridgeReady". Handle it here. + if (jsonObject["method"] == "eventBridgeReady") { + // Stuff a JSON object full of information necessary for the screen share local Web entity + // to connect to the screen share session associated with the room in which the user's avatar is standing. + QJsonObject responseObject; + responseObject.insert("app", "screenshare"); + responseObject.insert("method", "receiveConnectionInfo"); + QJsonObject responseObjectData; + responseObjectData.insert("token", _token); + responseObjectData.insert("projectAPIKey", _projectAPIKey); + responseObjectData.insert("sessionID", _sessionID); + responseObject.insert("data", responseObjectData); + + esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap()); + } + } +} diff --git a/interface/src/scripting/ScreenshareScriptingInterface.h b/interface/src/scripting/ScreenshareScriptingInterface.h new file mode 100644 index 0000000000..ec8f3cd619 --- /dev/null +++ b/interface/src/scripting/ScreenshareScriptingInterface.h @@ -0,0 +1,92 @@ +// +// ScreenshareScriptingInterface.h +// interface/src/scripting/ +// +// Created by Milad Nazeri and Zach Fox on 2019-10-23. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScreenshareScriptingInterface_h +#define hifi_ScreenshareScriptingInterface_h + +#include +#include +#include +#include + +#include +#include + +class ScreenshareScriptingInterface : public QObject, public Dependency { + Q_OBJECT + Q_PROPERTY(float localWebEntityZOffset MEMBER _localWebEntityZOffset NOTIFY localWebEntityZOffsetChanged) +public: + ScreenshareScriptingInterface(); + ~ScreenshareScriptingInterface(); + + Q_INVOKABLE void startScreenshare(const QUuid& screenshareZoneID, const QUuid& smartboardEntityID, const bool& isPresenter = false); + Q_INVOKABLE void stopScreenshare(); + +signals: + void screenshareError(); + void screenshareProcessTerminated(); + void startScreenshareViewer(); + void localWebEntityZOffsetChanged(const float& newZOffset); + +private slots: + void processAvatarZonePresencePacketOnClient(QSharedPointer message); + void onWebEventReceived(const QUuid& entityID, const QVariant& message); + void handleSuccessfulScreenshareInfoGet(QNetworkReply* reply); + void handleFailedScreenshareInfoGet(QNetworkReply* reply); + +private: +#if DEV_BUILD +#ifdef Q_OS_WIN + const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-win32-x64/hifi-screenshare.exe" }; +#elif defined(Q_OS_MAC) + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/../Resources/hifi-screenshare.app" }; +#else + // This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked. + const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-other-os/hifi-screenshare" }; +#endif +#else +#ifdef Q_OS_WIN + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.exe" }; +#elif defined(Q_OS_MAC) + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/../Resources/hifi-screenshare.app" }; +#else + // This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked. + const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare" }; +#endif +#endif + + QTimer* _requestScreenshareInfoRetryTimer{ nullptr }; + int _requestScreenshareInfoRetries{ 0 }; + void requestScreenshareInfo(); + + // Empirically determined. The default value here can be changed in Screenshare scripts, which enables faster iteration when we discover + // positional issues with various Smartboard entities. + // The following four values are closely linked: + // 1. The z-offset of whiteboard polylines (`STROKE_FORWARD_OFFSET_M` in `drawSphereClient.js`). + // 2. The z-offset of the screenshare local web entity (`LOCAL_WEB_ENTITY_Z_OFFSET` in `smartboardZoneClient.js`). + // 3. The z-offset of the screenshare "glass bezel" (`DEFAULT_SMARTBOARD_SCREENSHARE_GLASS_PROPS` in `smartboardZoneClient.js`). + // 4. The z-offset of the screenshare "status icon" (handled in the screenshare JSON file). + float _localWebEntityZOffset{ 0.0375f }; + + std::unique_ptr _screenshareProcess{ nullptr }; + QUuid _screenshareViewerLocalWebEntityUUID; + QString _token{ "" }; + QString _projectAPIKey{ "" }; + QString _sessionID{ "" }; + QUuid _screenshareZoneID; + QUuid _smartboardEntityID; + bool _isPresenter{ false }; + + QUuid _lastAuthorizedZoneID; + bool _waitingForAuthorization{ false }; +}; + +#endif // hifi_ScreenshareScriptingInterface_h diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index d2147ac5cc..32f837668d 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -17,7 +17,7 @@ GameplayObjects::GameplayObjects() { } bool GameplayObjects::addToGameplayObjects(const QUuid& avatarID) { - containsData = true; + _containsData = true; if (std::find(_avatarIDs.begin(), _avatarIDs.end(), avatarID) == _avatarIDs.end()) { _avatarIDs.push_back(avatarID); } @@ -29,7 +29,7 @@ bool GameplayObjects::removeFromGameplayObjects(const QUuid& avatarID) { } bool GameplayObjects::addToGameplayObjects(const EntityItemID& entityID) { - containsData = true; + _containsData = true; if (std::find(_entityIDs.begin(), _entityIDs.end(), entityID) == _entityIDs.end()) { _entityIDs.push_back(entityID); } diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h index 4386ee5ee6..f477a25b42 100644 --- a/interface/src/scripting/SelectionScriptingInterface.h +++ b/interface/src/scripting/SelectionScriptingInterface.h @@ -26,7 +26,7 @@ class GameplayObjects { public: GameplayObjects(); - bool getContainsData() const { return containsData; } + bool getContainsData() const { return _containsData; } std::vector getAvatarIDs() const { return _avatarIDs; } bool addToGameplayObjects(const QUuid& avatarID); @@ -37,7 +37,7 @@ public: bool removeFromGameplayObjects(const EntityItemID& entityID); private: - bool containsData { false }; + bool _containsData { false }; std::vector _avatarIDs; std::vector _entityIDs; }; diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index e9535ceb4e..849caa8427 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -145,7 +145,8 @@ signals: /**jsdoc * Triggered when a certified avatar entity's ownership check requested via - * {@link WalletScriptingInterface.proveAvatarEntityOwnershipVerification|proveAvatarEntityOwnershipVerification} succeeds. + * {@link WalletScriptingInterface.proveAvatarEntityOwnershipVerification|proveAvatarEntityOwnershipVerification} or + * {@link ContextOverlay.requestOwnershipVerification} succeeds. * @function WalletScriptingInterface.ownershipVerificationSuccess * @param {Uuid} entityID - The ID of the avatar entity checked. * @returns {Signal} @@ -154,7 +155,8 @@ signals: /**jsdoc * Triggered when a certified avatar entity's ownership check requested via - * {@link WalletScriptingInterface.proveAvatarEntityOwnershipVerification|proveAvatarEntityOwnershipVerification} fails. + * {@link WalletScriptingInterface.proveAvatarEntityOwnershipVerification|proveAvatarEntityOwnershipVerification} or + * {@link ContextOverlay.requestOwnershipVerification} fails. * @function WalletScriptingInterface.ownershipVerificationFailed * @param {Uuid} entityID - The ID of the avatar entity checked. * @returns {Signal} diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index f4aa36e2f4..434adf0bc8 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -29,6 +29,7 @@ #include "Menu.h" #include "OffscreenUi.h" #include "commerce/QmlCommerce.h" +#include "NetworkingConstants.h" static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation"; @@ -411,6 +412,10 @@ QString WindowScriptingInterface::checkVersion() { return QCoreApplication::applicationVersion(); } +QString WindowScriptingInterface::getUserAgent() { + return NetworkingConstants::VIRCADIA_USER_AGENT; +} + QString WindowScriptingInterface::protocolSignature() { return protocolVersionsSignatureBase64(); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 341b012c2d..28a725f3cb 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -206,7 +206,7 @@ public slots: void browseAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); /**jsdoc - * Prompts the user to specify the path and name of a file to save to. Displays a model dialog that navigates the directory + * Prompts the user to specify the path and name of a file to save to. Displays a modal dialog that navigates the directory * tree and allows the user to type in a file name. * @function Window.save * @param {string} [title=""] - The title to display at the top of the dialog. @@ -222,7 +222,7 @@ public slots: QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); /**jsdoc - * Prompts the user to specify the path and name of a file to save to. Displays a non-model dialog that navigates the + * Prompts the user to specify the path and name of a file to save to. Displays a non-modal dialog that navigates the * directory tree and allows the user to type in a file name. A {@link Window.saveFileChanged|saveFileChanged} signal is * emitted when a file is specified; no signal is emitted if the user cancels the dialog. * @function Window.saveAsync @@ -295,6 +295,13 @@ public slots: */ QString checkVersion(); + /**jsdoc + * Gets Interface's user agent. + * @function Window.getUserAgent + * @returns {string} Interface's user agent. + */ + QString getUserAgent(); + /**jsdoc * Gets the signature for Interface's protocol version. * @function Window.protocolSignature @@ -325,7 +332,7 @@ public slots: * @param {number} [aspectRatio=0] - The width/height ratio of the snapshot required. If the value is 0, the * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the * dimensions is adjusted in order to match the aspect ratio. - * @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user + * @param {string} [filename=""] - If a filename is not provided, the image is saved as "vircadia-snap-by-<user * name>-on-YYYY-MM-DD_HH-MM-SS". *

Still images are saved in JPEG or PNG format according to the extension provided — ".jpg", * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of @@ -364,7 +371,7 @@ public slots: * @function Window.takeSecondaryCameraSnapshot * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * signal. - * @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user + * @param {string} [filename=""] - If a filename is not provided, the image is saved as "vircadia-snap-by-<user * name>-on-YYYY-MM-DD_HH-MM-SS". *

Images are saved in JPEG or PNG format according to the extension provided — ".jpg", * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of @@ -383,7 +390,7 @@ public slots: * otherwise it is saved as an equirectangular image. * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * signal. - * @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user + * @param {string} [filename=""] - If a filename is not provided, the image is saved as "vircadia-snap-by-<user * name>-on-YYYY-MM-DD_HH-MM-SS". *

Images are saved in JPEG or PNG format according to the extension provided — ".jpg", * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of @@ -818,8 +825,12 @@ signals: /**jsdoc * Triggered when "minimized" state of the Interface window changes. * @function Window.minimizedChanged - * @param {bool} isMinimized - true if the Interface window is now minimized; false otherwise. + * @param {boolean} isMinimized - true if the Interface window is minimized, false if it isn't. * @returns {Signal} + * @example Report the "minimized" state of the Interface window when it changes. + * function onWindowMinimizedChanged(minimized) { + * print("Window minimized: " + minimized); + * } * * Window.minimizedChanged.connect(onWindowMinimizedChanged); */ diff --git a/interface/src/ui/AvatarCertifyBanner.cpp b/interface/src/ui/AvatarCertifyBanner.cpp index 5101188885..3fe2ed2027 100644 --- a/interface/src/ui/AvatarCertifyBanner.cpp +++ b/interface/src/ui/AvatarCertifyBanner.cpp @@ -62,16 +62,7 @@ void AvatarCertifyBanner::show(const QUuid& avatarID) { void AvatarCertifyBanner::clear() { if (_active) { - auto entityTreeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = entityTreeRenderer->getTree(); - if (!entityTree) { - return; - } - - entityTree->withWriteLock([&] { - entityTree->deleteEntity(_bannerID); - }); - + DependencyManager::get()->deleteEntity(_bannerID); _active = false; } } diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 0a655de5e5..8c8e3bc3c3 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -4,6 +4,7 @@ // // Created by Clement on 1/18/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -109,11 +110,34 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) { } } +void DialogsManager::setMetaverseLoginState() { + // We're only turning off the domain login trigger but the actual domain auth URL is still saved. + // So we can continue the domain login if desired. + _isDomainLogin = false; +} + +void DialogsManager::setDomainLoginState() { + _isDomainLogin = true; +} + +void DialogsManager::setDomainLogin(bool isDomainLogin, const QString& domain) { + _isDomainLogin = isDomainLogin; + if (!domain.isEmpty()) { + _domainLoginDomain = domain; + } +} + void DialogsManager::toggleLoginDialog() { + setDomainLogin(false); LoginDialog::toggleAction(); } void DialogsManager::showLoginDialog() { + + // ####### TODO: May be called from script via DialogsManagerScriptingInterface. Need to handle the case that it's already + // displayed and may be the domain login version. + + setDomainLogin(false); LoginDialog::showWithSelection(); } @@ -121,10 +145,22 @@ void DialogsManager::hideLoginDialog() { LoginDialog::hide(); } + +void DialogsManager::showDomainLoginDialog(const QString& domain) { + setDomainLogin(true, domain); + LoginDialog::showWithSelection(); +} + +// #######: TODO: Domain version of toggleLoginDialog()? + +// #######: TODO: Domain version of hideLoginDialog()? + + void DialogsManager::showUpdateDialog() { UpdateDialog::show(); } + void DialogsManager::octreeStatsDetails() { if (!_octreeStatsDialog) { _octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 949c86c240..864174296e 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -4,6 +4,7 @@ // // Created by Clement on 1/18/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -40,6 +41,10 @@ public: QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } void setAddressBarVisible(bool addressBarVisible); + void setMetaverseLoginState(); + void setDomainLoginState(); + bool getIsDomainLogin() { return _isDomainLogin; } + QString getDomainLoginDomain() { return _domainLoginDomain; } public slots: void showAddressBar(); @@ -49,6 +54,7 @@ public slots: void toggleLoginDialog(); void showLoginDialog(); void hideLoginDialog(); + void showDomainLoginDialog(const QString& domain = ""); void octreeStatsDetails(); void lodTools(); void hmdTools(bool showTools); @@ -82,6 +88,10 @@ private: QPointer _domainConnectionDialog; bool _dialogCreatedWhileShown { false }; bool _addressBarVisible { false }; + + void setDomainLogin(bool isDomainLogin, const QString& domain = ""); + bool _isDomainLogin { false }; + QString _domainLoginDomain; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index 6cc26e2409..0ac1f05737 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -129,8 +129,8 @@ void InteractiveWindow::emitMainWindowResizeEvent() { } /**jsdoc - * A set of properties used when creating an InteractiveWindow. - * @typedef {object} InteractiveWindow.Properties + * Property values used when creating an InteractiveWindow. + * @typedef {object} InteractiveWindow.WindowProperties * @property {string} [title="InteractiveWindow] - The title of the window. * @property {Vec2} [position] - The initial position of the window, in pixels. * @property {Vec2} [size] - The initial size of the window, in pixels @@ -142,13 +142,36 @@ void InteractiveWindow::emitMainWindowResizeEvent() { * @property {InteractiveWindow.PresentationWindowInfo} [presentationWindowInfo] - Controls how a NATIVE window is * displayed. If used, the window is docked to the specified edge of the Interface window, otherwise the window is * displayed as its own separate window. - * @property {InteractiveWindow.AdditionalFlags} [additionalFlags=0] - Window behavior flags in addition to "native window flags" (minimize/maximize/close), - * set at window creation. Possible flag values are provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}. - * Additional flag values can be found on Qt's website at https://doc.qt.io/qt-5/qt.html#WindowType-enum. - * @property {InteractiveWindow.OverrideFlags} [overrideFlags=0] - Window behavior flags instead of the default window flags. - * Set at window creation. Possible flag values are provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}. - * Additional flag values can be found on Qt's website at https://doc.qt.io/qt-5/qt.html#WindowType-enum. + * @property {InteractiveWindow.Flags} [additionalFlags=0] - Customizes window behavior. + * @property {InteractiveWindow.OverrideFlags} [overrideFlags=0] - Customizes window controls. + + * @property {InteractiveWindow.RelativePositionAnchor} [relativePositionAnchor] - The anchor for the + * relativePosition, if used. + * @property {Vec2} [relativePosition] - The position of the window, relative to the relativePositionAnchor, in + * pixels. Excludes the window frame. + * @property {boolean} [isFullScreenWindow] - true to make the window full screen. */ +/**jsdoc + *

A set of flags customizing InteractiveWindow controls. The value is constructed by using the | + * (bitwise OR) operator on the individual flag values..

+ * + * + * + * + * + * + * + *
ValueNameDescription
0x00000001WindowDisplays the window as a window rather than a dialog.
0x00001000WindowTitleHintAdds a title bar. + *
0x00002000WindowSystemMenuHintAdds a window system menu. + *
0x00004000WindowMinimizeButtonHintAdds a minimize button. + *
0x00008000WindowMaximizeButtonHintAdds a maximize button. + *
0x00040000WindowStaysOnTopHintThe window stays on top of other windows. + * Not used on Windows. + *
0x08000000WindowCloseButtonHintAdds a close button. + *
+ * @typedef {number} InteractiveWindow.OverrideFlags + */ +// OverrideFlags is per InteractiveWindow.qml. InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties, bool restricted) { InteractiveWindowPresentationMode presentationMode = InteractiveWindowPresentationMode::Native; diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index fb10aac444..7c8897059f 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -76,12 +76,42 @@ namespace InteractiveWindowEnums { }; Q_ENUM_NS(InteractiveWindowFlags); + /**jsdoc + *

A display mode for an InteractiveWindow.

+ * + * + * + * + * + * + * + * + *
ValueNameDescription
0VIRTUALThe window is displayed inside Interface: in the desktop window in + * desktop mode or on the HUD surface in HMD mode.
1NATIVEThe window is displayed separately from the Interface window, as its + * own separate window.
+ * @typedef {number} InteractiveWindow.PresentationMode + */ enum InteractiveWindowPresentationMode { Virtual, Native }; Q_ENUM_NS(InteractiveWindowPresentationMode); + /**jsdoc + *

A docking location of an InteractiveWindow.

+ * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0TOPDock to the top edge of the Interface window.
1BOTTOMDock to the bottom edge of the Interface window.
2LEFTDock to the left edge of the Interface window.
3RIGHTDock to the right edge of the Interface window.
+ * @typedef {number} InteractiveWindow.DockArea + */ enum DockArea { TOP, BOTTOM, @@ -90,6 +120,24 @@ namespace InteractiveWindowEnums { }; Q_ENUM_NS(DockArea); + /**jsdoc + *

The anchor for a relative position of an InteractiveWindow.

+ * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0NO_ANCHORPosition is not relative to any part of the Interface window.
1TOP_LEFTPosition is offset from the top left of the Interface window.
2TOP_RIGHTPosition is offset from the top right of the Interface window.
3BOTTOM_RIGHTPosition offset from the bottom right of the Interface + * window.
4BOTTOM_LEFFTPosition is offset from the bottom left of the Interface + * window.
+ * @typedef {number} InteractiveWindow.RelativePositionAnchor + */ enum RelativePositionAnchor { NO_ANCHOR, TOP_LEFT, @@ -110,13 +158,18 @@ using namespace InteractiveWindowEnums; *

Create using {@link Desktop.createWindow}.

* * @class InteractiveWindow + * @hideconstructor * * @hifi-interface * @hifi-client-entity * @hifi-avatar * * @property {string} title - The title of the window. - * @property {Vec2} position - The position of the window, in pixels. + * @property {Vec2} position - The absolute position of the window, in pixels. + * @property {InteractiveWindow.RelativePositionAnchor} relativePositionAnchor - The anchor for the + * relativePosition, if used. + * @property {Vec2} relativePosition - The position of the window, relative to the relativePositionAnchor, in + * pixels. Excludes the window frame. * @property {Vec2} size - The size of the window, in pixels. * @property {boolean} visible - true if the window is visible, false if it isn't. * @property {InteractiveWindow.PresentationMode} presentationMode - The presentation mode of the window: @@ -186,24 +239,24 @@ public slots: * @example Send and receive messages with a QML window. * // JavaScript file. * - * var qmlWindow = Desktop.createWindow(Script.resolvePath("QMLWindow.qml"), { - * title: "QML Window", + * var interactiveWindow = Desktop.createWindow(Script.resolvePath("InteractiveWindow.qml"), { + * title: "Interactive Window", * size: { x: 400, y: 300 } * }); * - * qmlWindow.fromQml.connect(function (message) { + * interactiveWindow.fromQml.connect(function (message) { * print("Message received: " + message); * }); * * Script.setTimeout(function () { - * qmlWindow.sendToQml("Hello world!"); + * interactiveWindow.sendToQml("Hello world!"); * }, 2000); * * Script.scriptEnding.connect(function () { - * qmlWindow.close(); + * interactiveWindow.close(); * }); * @example - * // QML file, "QMLWindow.qml". + * // QML file, "InteractiveWindow.qml". * * import QtQuick 2.5 * import QtQuick.Controls 1.4 @@ -227,7 +280,7 @@ public slots: /**jsdoc * Sends a message to an embedded HTML web page. To receive the message, the HTML page's script must connect to the - * EventBridge that is automatically provided to the script: + * EventBridge that is automatically provided for the script: *
EventBridge.scriptEventReceived.connect(function(message) {
      *     ...
      * });
@@ -239,8 +292,8 @@ public slots: /**jsdoc * @function InteractiveWindow.emitWebEvent - * @param {object|string} message - The message. - * @deprecated This function is deprecated and will be removed from the API. + * @param {object|string} message - Message. + * @deprecated This function is deprecated and will be removed. */ void emitWebEvent(const QVariant& webMessage); @@ -318,9 +371,9 @@ signals: /**jsdoc * @function InteractiveWindow.scriptEventReceived - * @param {object} message - The message. + * @param {object} message - Message. * @returns {Signal} - * @deprecated This signal is deprecated and will be removed from the API. + * @deprecated This signal is deprecated and will be removed. */ // InteractiveWindow content may include WebView requiring EventBridge. void scriptEventReceived(const QVariant& message); @@ -337,9 +390,8 @@ signals: protected slots: /**jsdoc * @function InteractiveWindow.qmlToScript - * @param {object} message - * @returns {Signal} - * @deprecated This signal is deprecated and will be removed from the API. + * @param {object} message - Message. + * @deprecated This method is deprecated and will be removed. */ void qmlToScript(const QVariant& message); diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index c0e96fe8bb..b45a62ae3a 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -4,6 +4,7 @@ // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -24,7 +25,9 @@ #include #include "AccountManager.h" +#include "DomainAccountManager.h" #include "DependencyManager.h" +#include "DialogsManager.h" #include "Menu.h" #include "Application.h" @@ -38,12 +41,17 @@ const QUrl LOGIN_DIALOG = PathUtils::qmlUrl("OverlayLoginDialog.qml"); LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { auto accountManager = DependencyManager::get(); + auto domainAccountManager = DependencyManager::get(); // the login hasn't been dismissed yet if the user isn't logged in and is encouraged to login. #if !defined(Q_OS_ANDROID) connect(accountManager.data(), &AccountManager::loginComplete, this, &LoginDialog::handleLoginCompleted); connect(accountManager.data(), &AccountManager::loginFailed, this, &LoginDialog::handleLoginFailed); + connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, + this, &LoginDialog::handleLoginCompleted); + connect(domainAccountManager.data(), &DomainAccountManager::loginFailed, + this, &LoginDialog::handleLoginFailed); connect(qApp, &Application::loginDialogFocusEnabled, this, &LoginDialog::focusEnabled); connect(qApp, &Application::loginDialogFocusDisabled, this, &LoginDialog::focusDisabled); connect(this, SIGNAL(dismissedLoginDialog()), qApp, SLOT(onDismissedLoginDialog())); @@ -91,14 +99,16 @@ void LoginDialog::toggleAction() { if (accountManager->isLoggedIn()) { // change the menu item to logout - loginAction->setText("Logout " + accountManager->getAccountInfo().getUsername()); + loginAction->setText("Metaverse: Logout " + accountManager->getAccountInfo().getUsername()); connection = connect(loginAction, &QAction::triggered, accountManager.data(), &AccountManager::logout); } else { // change the menu item to login - loginAction->setText("Log In / Sign Up"); + loginAction->setText("Metaverse: Log In / Sign Up"); connection = connect(loginAction, &QAction::triggered, [] { // if not in login state, show. if (!qApp->getLoginDialogPoppedUp()) { + auto dialogsManager = DependencyManager::get(); + dialogsManager->setMetaverseLoginState(); LoginDialog::showWithSelection(); } }); @@ -131,10 +141,15 @@ void LoginDialog::dismissLoginDialog() { } void LoginDialog::login(const QString& username, const QString& password) const { - qDebug() << "Attempting to login " << username; + qDebug() << "Attempting to login" << username; DependencyManager::get()->requestAccessToken(username, password); } +void LoginDialog::loginDomain(const QString& username, const QString& password) const { + qDebug() << "Attempting to login" << username << "into a domain"; + DependencyManager::get()->requestAccessToken(username, password); +} + void LoginDialog::loginThroughOculus() { qDebug() << "Attempting to login through Oculus"; if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) { @@ -410,3 +425,11 @@ void LoginDialog::signupFailed(QNetworkReply* reply) { emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE); } } + +bool LoginDialog::getDomainLoginRequested() const { + return DependencyManager::get()->getIsDomainLogin(); +} + +QString LoginDialog::getDomainLoginDomain() const { + return DependencyManager::get()->getDomainLoginDomain(); +} diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index 7c659a9320..9f4af5debb 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -4,6 +4,7 @@ // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -71,6 +72,7 @@ protected slots: Q_INVOKABLE QString oculusUserID() const; Q_INVOKABLE void login(const QString& username, const QString& password) const; + Q_INVOKABLE void loginDomain(const QString& username, const QString& password) const; Q_INVOKABLE void loginThroughSteam(); Q_INVOKABLE void linkSteam(); Q_INVOKABLE void createAccountFromSteam(QString username = QString()); @@ -81,6 +83,10 @@ protected slots: Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password); Q_INVOKABLE bool getLoginDialogPoppedUp() const; + + Q_INVOKABLE bool getDomainLoginRequested() const; + Q_INVOKABLE QString getDomainLoginDomain() const; + }; #endif // hifi_LoginDialog_h diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 4709cc0a9c..96c10be212 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -27,6 +27,7 @@ #include #include +#include #include const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" }; @@ -225,7 +226,7 @@ void ModelHandler::update() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); QNetworkReply* reply = networkAccessManager.head(request); connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); } @@ -278,7 +279,7 @@ void ModelHandler::queryNewFiles(QString marker) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(request); connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 7e11406808..9f8a8ec013 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -223,13 +223,11 @@ void setupPreferences() { preferences->addPreference(preference); } - /* - // FIXME: Remove setting completely or make available through JavaScript API? { auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); }; auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); }; preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter)); - }*/ + } // Snapshots static const QString SNAPSHOTS { "Snapshots" }; @@ -254,7 +252,15 @@ void setupPreferences() { auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); }; preferences->addPreference(new CheckPreference("Privacy", "Send data - High Fidelity uses information provided by your " "client to improve the product through the logging of errors, tracking of usage patterns, " - "installation and system details, and crash events. By allowing High Fidelity to collect " + "installation and system details. By allowing High Fidelity to collect this information " + "you are helping to improve the product. ", getter, setter)); + } + + { + auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableCrashLogger); }; + auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableCrashLogger, !value); }; + preferences->addPreference(new CheckPreference("Privacy", "Send crashes - Vircadia uses information provided by your " + "client to improve the product through crash reports. By allowing Vircadia to collect " "this information you are helping to improve the product. ", getter, setter)); } diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index bb9971e582..4882d6e5da 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -42,9 +42,9 @@ #include "Snapshot.h" #include "SnapshotUploader.h" -// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg +// filename format: vircadia-snap-by-%username%-on-%date%_%time%_@-%location%.jpg // %1 <= username, %2 <= date and time, %3 <= current location -const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2.jpg"; +const QString FILENAME_PATH_FORMAT = "vircadia-snap-by-%1-on-%2.jpg"; const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss"; const QString SNAPSHOTS_DIRECTORY = "Snapshots"; const QString URL = "highfidelity_url"; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8b9b9743f0..bbf22f9549 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -251,7 +251,7 @@ void Stats::updateStats(bool force) { SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); auto audioClient = DependencyManager::get().data(); - if (audioMixerNode || force) { + if (audioMixerNode) { STAT_UPDATE(audioMixerKbps, (int)roundf(audioMixerNode->getInboundKbps() + audioMixerNode->getOutboundKbps())); STAT_UPDATE(audioMixerPps, audioMixerNode->getInboundPPS() + @@ -261,6 +261,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerInPps, audioMixerNode->getInboundPPS()); STAT_UPDATE(audioMixerOutKbps, (int)roundf(audioMixerNode->getOutboundKbps())); STAT_UPDATE(audioMixerOutPps, audioMixerNode->getOutboundPPS()); + STAT_UPDATE(audioInboundPPS, (int)audioClient->getAudioInboundPPS()); STAT_UPDATE(audioAudioInboundPPS, (int)audioClient->getAudioInboundPPS()); STAT_UPDATE(audioSilentInboundPPS, (int)audioClient->getSilentInboundPPS()); STAT_UPDATE(audioOutboundPPS, (int)audioClient->getAudioOutboundPPS()); @@ -274,6 +275,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerOutPps, -1); STAT_UPDATE(audioOutboundPPS, -1); STAT_UPDATE(audioSilentOutboundPPS, -1); + STAT_UPDATE(audioInboundPPS, -1); STAT_UPDATE(audioAudioInboundPPS, -1); STAT_UPDATE(audioSilentInboundPPS, -1); } diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index b87e3a3dbc..62ec42ca1d 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -24,6 +24,10 @@ private: \ type _##name{ initialValue }; /**jsdoc + * The Stats API provides statistics on Interface and domain operation, per the statistics overlay. + * + *

Note: This API is primarily an internal diagnostics tool and is provided "as is".

+ * * @namespace Stats * * @hifi-interface @@ -32,157 +36,418 @@ private: \ * @hifi-server-entity * @hifi-assignment-client * - * @property {boolean} expanded - * @property {boolean} timingExpanded - Read-only. - * @property {string} monospaceFont - Read-only. + * @property {boolean} expanded - true if the statistics overlay should be in expanded form when the overlay is + * displayed, false if it shouldn't be expanded. + * @property {boolean} timingExpanded - true if timing details should be displayed when the statistics overlay is + * displayed in expanded form, false if timing details should not be displayed. Set by the menu item, + * Developer > Timing > Performance Timer > Display Timing Details. + * Read-only. + * @property {string} monospaceFont - The name of the monospace font used in the statistics overlay. + * Read-only. * - * @property {number} serverCount - Read-only. - * @property {number} renderrate - How often the app is creating new gpu::Frames. Read-only. - * @property {number} presentrate - How often the display plugin is presenting to the device. Read-only. - * @property {number} stutterrate - How often the display device is reprojecting old frames. Read-only. + * @property {number} serverCount - The number of servers that Interface is connected to. + * Read-only. + * @property {number} renderrate - The rate at which new GPU frames are being created, in Hz. + * Read-only. + * @property {number} presentrate - The rate at which the display plugin is presenting to the display device, in Hz. + * Read-only. + * @property {number} stutterrate - The rate at which the display plugin is reprojecting old GPU frames, in Hz. + * Read-only. * - * @property {number} appdropped - Read-only. - * @property {number} longsubmits - Read-only. - * @property {number} longrenders - Read-only. - * @property {number} longframes - Read-only. + * @property {number} appdropped - The number of times a frame has not been provided to the display device in time. + * Read-only. + * @property {number} longsubmits - The number of times the display device has taken longer than 11ms to return after being + * given a frame. + * Read-only. + * @property {number} longrenders - The number of times it has taken longer than 11ms to submit a new frame to the display + * device. + * Read-only. + * @property {number} longframes - The number of times longsubmits + longrenders has taken longer than 15ms. + * Read-only. * - * @property {number} presentnewrate - Read-only. - * @property {number} presentdroprate - Read-only. - * @property {number} gameLoopRate - Read-only. - * @property {number} avatarCount - Read-only. - * @property {number} heroAvatarCount - Read-only. - * @property {number} physicsObjectCount - Read-only. - * @property {number} updatedAvatarCount - Read-only. - * @property {number} updatedHeroAvatarCount - Read-only. - * @property {number} notUpdatedAvatarCount - Read-only. - * @property {number} packetInCount - Read-only. - * @property {number} packetOutCount - Read-only. - * @property {number} mbpsIn - Read-only. - * @property {number} mbpsOut - Read-only. - * @property {number} assetMbpsIn - Read-only. - * @property {number} assetMbpsOut - Read-only. - * @property {number} audioPing - Read-only. - * @property {number} avatarPing - Read-only. - * @property {number} entitiesPing - Read-only. - * @property {number} assetPing - Read-only. - * @property {number} messagePing - Read-only. - * @property {Vec3} position - Read-only. - * @property {number} speed - Read-only. - * @property {number} yaw - Read-only. - * @property {number} avatarMixerInKbps - Read-only. - * @property {number} avatarMixerInPps - Read-only. - * @property {number} avatarMixerOutKbps - Read-only. - * @property {number} avatarMixerOutPps - Read-only. - * @property {number} myAvatarSendRate - Read-only. - * - * @property {number} audioMixerInKbps - Read-only. - * @property {number} audioMixerInPps - Read-only. - * @property {number} audioMixerOutKbps - Read-only. - * @property {number} audioMixerOutPps - Read-only. - * @property {number} audioMixerKbps - Read-only. - * @property {number} audioMixerPps - Read-only. - * @property {number} audioOutboundPPS - Read-only. - * @property {number} audioSilentOutboundPPS - Read-only. - * @property {number} audioAudioInboundPPS - Read-only. - * @property {number} audioSilentInboundPPS - Read-only. - * @property {number} audioPacketLoss - Read-only. - * @property {string} audioCodec - Read-only. - * @property {string} audioNoiseGate - Read-only. - * @property {Vec2} audioInjectors - Read-only. - * @property {number} entityPacketsInKbps - Read-only. - * - * @property {number} downloads - Read-only. - * @property {number} downloadLimit - Read-only. - * @property {number} downloadsPending - Read-only. - * @property {string[]} downloadUrls - Read-only. - * @property {number} processing - Read-only. - * @property {number} processingPending - Read-only. - * @property {number} triangles - Read-only. - * @property {number} materialSwitches - Read-only. - * @property {number} itemConsidered - Read-only. - * @property {number} itemOutOfView - Read-only. - * @property {number} itemTooSmall - Read-only. - * @property {number} itemRendered - Read-only. - * @property {number} shadowConsidered - Read-only. - * @property {number} shadowOutOfView - Read-only. - * @property {number} shadowTooSmall - Read-only. - * @property {number} shadowRendered - Read-only. - * @property {string} sendingMode - Read-only. - * @property {string} packetStats - Read-only. - * @property {number} lodAngle - Read-only. - * @property {number} lodTargetFramerate - Read-only. - * @property {string} lodStatus - Read-only. - * @property {string} timingStats - Read-only. - * @property {string} gameUpdateStats - Read-only. - * @property {number} serverElements - Read-only. - * @property {number} serverInternal - Read-only. - * @property {number} serverLeaves - Read-only. - * @property {number} localElements - Read-only. - * @property {number} localInternal - Read-only. - * @property {number} localLeaves - Read-only. - * @property {number} rectifiedTextureCount - Read-only. - * @property {number} decimatedTextureCount - Read-only. - * @property {number} gpuBuffers - Read-only. - * @property {number} gpuBufferMemory - Read-only. - * @property {number} gpuTextures - Read-only. - * @property {number} glContextSwapchainMemory - Read-only. - * @property {number} qmlTextureMemory - Read-only. - * @property {number} texturePendingTransfers - Read-only. - * @property {number} gpuTextureMemory - Read-only. - * @property {number} gpuTextureResidentMemory - Read-only. - * @property {number} gpuTextureFramebufferMemory - Read-only. - * @property {number} gpuTextureResourceMemory - Read-only. - * @property {number} gpuTextureResourceIdealMemory - Read-only. - * @property {number} gpuTextureResourcePopulatedMemory - Read-only. - * @property {number} gpuTextureExternalMemory - Read-only. - * @property {string} gpuTextureMemoryPressureState - Read-only. - * @property {number} gpuFreeMemory - Read-only. - * @property {number} gpuFrameTime - Read-only. - * @property {number} batchFrameTime - Read-only. - * @property {number} engineFrameTime - Read-only. - * @property {number} avatarSimulationTime - Read-only. - * - * - * @property {number} x - * @property {number} y - * @property {number} z - * @property {number} width - * @property {number} height - * - * @property {number} opacity - * @property {boolean} enabled - * @property {boolean} visible - * - * @property {string} state - * @property {object} anchors - Read-only. - * @property {number} baselineOffset - * - * @property {boolean} clip - * - * @property {boolean} focus - * @property {boolean} activeFocus - Read-only. - * @property {boolean} activeFocusOnTab - * - * @property {number} rotation - * @property {number} scale - * @property {number} transformOrigin - * - * @property {boolean} smooth - * @property {boolean} antialiasing - * @property {number} implicitWidth - * @property {number} implicitHeight - * - * @property {object} layer - Read-only. + * @property {number} presentnewrate - The rate at which the display plugin is presenting new GPU frames, in Hz. + * Read-only. + * @property {number} presentdroprate - The rate at which the display plugin is dropping GPU frames, in Hz. + * Read-only. - * @property {number} stylusPicksCount - Read-only. - * @property {number} rayPicksCount - Read-only. - * @property {number} parabolaPicksCount - Read-only. - * @property {number} collisionPicksCount - Read-only. - * @property {Vec3} stylusPicksUpdated - Read-only. - * @property {Vec3} rayPicksUpdated - Read-only. - * @property {Vec3} parabolaPicksUpdated - Read-only. - * @property {Vec3} collisionPicksUpdated - Read-only. - * @property {bool} eventQueueDebuggingOn - Read-only. + * @property {number} gameLoopRate - The rate at which the game loop is running, in Hz. + * Read-only. + * @property {number} refreshRateTarget - The current target refresh rate, in Hz, per the current refreshRateMode + * and refreshRateRegime if in desktop mode; a higher rate if in VR mode. + * Read-only. + * @property {RefreshRateProfileName} refreshRateMode - The current refresh rate profile. + * Read-only. + * @property {RefreshRateRegimeName} refreshRateRegime - The current refresh rate regime. + * Read-only. + * @property {UXModeName} uxMode - The user experience (UX) mode that Interface is running in. + * Read-only. + * @property {number} avatarCount - The number of avatars in the domain other than the client's. + * Read-only. + * @property {number} heroAvatarCount - The number avatars in a "hero" zone in the domain, other than the client's. + * Read-only. + * @property {number} physicsObjectCount - The number of objects that have collisions enabled. + * Read-only. + * @property {number} updatedAvatarCount - The number of avatars in the domain, other than the client's, that were updated in + * the most recent game loop. + * Read-only. + * @property {number} updatedHeroAvatarCount - The number of avatars in a "hero" zone in the domain, other than the client's, + * that were updated in the most recent game loop. + * Read-only. + * @property {number} notUpdatedAvatarCount - The number of avatars in the domain, other than the client's, that weren't able + * to be updated in the most recent game loop because there wasn't enough time to. + * Read-only. + * @property {number} packetInCount - The number of packets being received from the domain server, in packets per second. + * Read-only. + * @property {number} packetOutCount - The number of packets being sent to the domain server, in packets per second. + * Read-only. + * @property {number} mbpsIn - The amount of data being received from the domain server, in megabits per second. + * Read-only. + * @property {number} mbpsOut - The amount of data being sent to the domain server, in megabits per second. + * Read-only. + @property {number} assetMbpsIn - The amount of data being received from the asset server, in megabits per second. + * 0.0 if not connected to an avatar mixer. + * Read-only. + * @property {number} assetMbpsOut - The amount of data being sent to the asset server, in megabits per second. + * 0.0 if not connected to an avatar mixer. + * Read-only. + * @property {number} audioPing - The ping time to the audio mixer, in ms. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} avatarPing - The ping time to the avatar mixer, in ms. + * -1 if not connected to an avatar mixer. + * Read-only. + * @property {number} entitiesPing - The average ping time to the entity servers, in ms. + * -1 if not connected to an entity server. + * Read-only. + * @property {number} assetPing - The ping time to the asset server, in ms. + * -1 if not connected to an asset server. + * Read-only. + * @property {number} messagePing - The ping time to the message mixer, in ms. + * -1 if not connected to a message mixer. + * Read-only. + * @property {Vec3} position - The position of the user's avatar. + * Read-only. + *

Note: Property not available in the API.

+ * @property {number} speed - The speed of the user's avatar, in m/s. + * Read-only. + * @property {number} yaw - The yaw of the user's avatar body, in degrees. + * Read-only. + * @property {number} avatarMixerInKbps - The amount of data being received from the avatar mixer, in kilobits per second. + * -1 if not connected to an avatar mixer. + * Read-only. + * @property {number} avatarMixerInPps - The number of packets being received from the avatar mixer, in packets per second. + * -1 if not connected to an avatar mixer. + * Read-only. + * @property {number} avatarMixerOutKbps - The amount of data being sent to the avatar mixer, in kilobits per second. + * -1 if not connected to an avatar mixer. + * Read-only. + * @property {number} avatarMixerOutPps - The number of packets being sent to the avatar mixer, in packets per second. + * -1 if not connected to an avatar mixer. + * Read-only. + * @property {number} myAvatarSendRate - The number of avatar packets being sent by the user's avatar, in packets per second. + * Read-only. + * + * @property {number} audioMixerInKbps - The amount of data being received from the audio mixer, in kilobits per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioMixerInPps - The number of packets being received from the audio mixer, in packets per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioMixerOutKbps - The amount of data being sent to the audio mixer, in kilobits per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioMixerOutPps - The number of packets being sent to the audio mixer, in packets per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioMixerKbps - The total amount of data being sent to and received from the audio mixer, in kilobits + * per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioMixerPps - The total number of packets being sent to and received from the audio mixer, in packets + * per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioOutboundPPS - The number of non-silent audio packets being sent by the user, in packets per second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioSilentOutboundPPS - The number of silent audio packets being sent by the user, in packets per + * second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioInboundPPS - The number of non-silent audio packets being received by the user, in packets per + * second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioAudioInboundPPS - The number of non-silent audio packets being received by the user, in packets per + * second. + * -1 if not connected to an audio mixer. + * Read-only. + *

Deprecated: This property is deprecated and will be removed. Use audioInboundPPS + * instead.

+ * @property {number} audioSilentInboundPPS - The number of silent audio packets being received by the user, in packets per + * second. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {number} audioPacketLoss - The number of audio packets being lost, sent to or received from the audio mixer, in %. + * -1 if not connected to an audio mixer. + * Read-only. + * @property {string} audioCodec - The name of the audio codec. + * Read-only. + * @property {string} audioNoiseGate - The status of the audio noise gate: "Open" or "Closed". + * Read-only. + * @property {Vec2} audioInjectors - The number of audio injectors, local and non-local. + * Read-only. + *

Note: Property not available in the API.

+ * @property {number} entityPacketsInKbps - The average amount of data being received from entity servers, in kilobits per + * second. (Multiply by the number of entity servers to get the total amount of data being received.) + * -1 if not connected to an entity server. + * Read-only. + * + * @property {number} downloads - The number of downloads in progress. + * Read-only. + * @property {number} downloadLimit - The maximum number of concurrent downloads. + * Read-only. + * @property {number} downloadsPending - The number of downloads pending. + * Read-only. + * @property {string[]} downloadUrls - The download URLs. + * Read-only. + *

Note: Property not available in the API.

+ * @property {number} processing - The number of completed downloads being processed. + * Read-only. + * @property {number} processingPending - The number of completed downloads waiting to be processed. + * Read-only. + * @property {number} triangles - The number of triangles in the rendered scene. + * Read-only. + * @property {number} drawcalls - The number of draw calls made for the rendered scene. + * Read-only. + * @property {number} materialSwitches - The number of material switches performed for the rendered scene. + * Read-only. + * @property {number} itemConsidered - The number of item considerations made for rendering. + * Read-only. + * @property {number} itemOutOfView - The number of items out of view. + * Read-only. + * @property {number} itemTooSmall - The number of items too small to render. + * Read-only. + * @property {number} itemRendered - The number of items rendered. + * Read-only. + * @property {number} shadowConsidered - The number of shadow considerations made for rendering. + * Read-only. + * @property {number} shadowOutOfView - The number of shadows out of view. + * Read-only. + * @property {number} shadowTooSmall - The number of shadows too small to render. + * Read-only. + * @property {number} shadowRendered - The number of shadows rendered. + * Read-only. + * @property {string} sendingMode - Description of the octree sending mode. + * Read-only. + * @property {string} packetStats - Description of the octree packet processing state. + * Read-only. + * @property {number} lodAngle - The target LOD angle, in degrees. + * Read-only. + * @property {number} lodTargetFramerate - The target LOD frame rate, in Hz. + * Read-only. + * @property {string} lodStatus - Description of the current LOD. + * Read-only. + * @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 timingExpanded is true. Only the top 10 items are provided if + * Developer > Timing > Performance Timer > Only Display Top 10 is enabled. + * Read-only. + * @property {string} gameUpdateStats - Details of the average time (ms) spent in different parts of the game loop. + * Read-only. + * @property {number} serverElements - The total number of elements in the server octree. + * Read-only. + * @property {number} serverInternal - The number of internal elements in the server octree. + * Read-only. + * @property {number} serverLeaves - The number of leaf elements in the server octree. + * Read-only. + * @property {number} localElements - The total number of elements in the client octree. + * Read-only. + * @property {number} localInternal - The number of internal elements in the client octree. + * Read-only. + * @property {number} localLeaves - The number of leaf elements in the client octree. + * Read-only. + * @property {number} rectifiedTextureCount - The number of textures that have been resized so that their dimensions is a power + * of 2 if smaller than 128 pixels, or a multiple of 128 if greater than 128 pixels. + * Read-only. + * @property {number} decimatedTextureCount - The number of textures that have been reduced in size because they were over the + * maximum allowed dimensions of 4096 pixels on desktop or 2048 pixels on mobile. + * Read-only. + * @property {number} gpuBuffers - The number of OpenGL buffer objects managed by the GPU back-end. + * Read-only. + * @property {number} gpuBufferMemory - The total memory size of the gpuBuffers, in MB. + * Read-only. + * @property {number} gpuTextures - The number of OpenGL textures managed by the GPU back-end. This is the sum of the number of + * textures managed for gpuTextureResidentMemory, gpuTextureResourceMemory, and + * gpuTextureFramebufferMemory. + * Read-only. + * @property {number} gpuTextureMemory - The total memory size of the gpuTextures, in MB. This is the sum of + * gpuTextureResidentMemory, gpuTextureResourceMemory, and + * gpuTextureFramebufferMemory. + * Read-only. + * @property {number} glContextSwapchainMemory - The estimated memory used by the default OpenGL frame buffer, in MB. + * Read-only. + * @property {number} qmlTextureMemory - The memory size of textures managed by the offscreen QML surface, in MB. + * Read-only. + * @property {number} texturePendingTransfers - The memory size of textures pending transfer to the GPU, in MB. + * Read-only. + * @property {number} gpuTextureResidentMemory - The memory size of the "strict" textures that always have their full + * resolution in GPU memory, in MB. + * Read-only. + * @property {number} gpuTextureFramebufferMemory - The memory size of the frame buffer on the GPU, in MB. + * Read-only. + * @property {number} gpuTextureResourceMemory - The amount of GPU memory that has been allocated for "variable" textures that + * don't necessarily always have their full resolution in GPU memory, in MB. + * Read-only. + * @property {number} gpuTextureResourceIdealMemory - The amount of memory that "variable" textures would take up if they were + * all completely loaded, in MB. + * Read-only. + * @property {number} gpuTextureResourcePopulatedMemory - How much of the GPU memory allocated has actually been populated, in +* MB. + * Read-only. + * @property {string} gpuTextureMemoryPressureState - The stats of the texture transfer engine. + *
    + *
  • "Undersubscribed": There is texture data that can fit in memory but that isn't on the GPU, so more + * GPU texture memory should be allocated if possible.
  • + *
  • "Transfer": More GPU texture memory has been allocated and texture data is being transferred.
  • + *
  • "Idle": Either all texture data has been transferred to the GPU or there is nor more space + * available.
  • + *
+ * Read-only. + * @property {number} gpuFreeMemory - The amount of GPU memory available after all allocations, in MB. + * Read-only. + *

Note: This is not a reliable number because OpenGL doesn't have an official method of getting this + * information.

+ * @property {number} gpuTextureExternalMemory - The estimated amount of memory consumed by textures being used but that are + * not managed by the GPU library, in MB. + * Read-only. + * @property {Vec2} gpuFrameSize - The dimensions of the frames being rendered, in pixels. + * Read-only. + *

Note: Property not available in the API.

+ * @property {number} gpuFrameTime - The time the GPU is spending on a frame, in ms. + * Read-only. + * @property {number} gpuFrameTimePerPixel - The time the GPU is spending on a pixel, in ns. + * Read-only. + * @property {number} batchFrameTime - The time being spent batch processing each frame, in ms. + * Read-only. + * @property {number} engineFrameTime - The time being spent in the render engine each frame, in ms. + * Read-only. + * @property {number} avatarSimulationTime - The time being spent simulating avatars each frame, in ms. + * Read-only. + * + * @property {number} stylusPicksCount - The number of stylus picks currently in effect. + * Read-only. + * @property {number} rayPicksCount - The number of ray picks currently in effect. + * Read-only. + * @property {number} parabolaPicksCount - The number of parabola picks currently in effect. + * Read-only. + * @property {number} collisionPicksCount - The number of collision picks currently in effect. + * Read-only. + * @property {Vec3} stylusPicksUpdated - The number of stylus pick intersection that were found in the most recent game loop: + *
    + *
  • x = entity intersections.
  • + *
  • y = avatar intersections.
  • + *
  • z = HUD intersections.
  • + *
+ * Read-only. + *

Note: Property not available in the API.

+ * @property {Vec3} rayPicksUpdated - The number of ray pick intersections that were found in the most recent game loop: + *
    + *
  • x = entity intersections.
  • + *
  • y = avatar intersections.
  • + *
  • z = HUD intersections.
  • + *
+ * Read-only. + *

Note: Property not available in the API.

+ * @property {Vec3} parabolaPicksUpdated - The number of parabola pick intersections that were found in the most recent game + * loop: + *
    + *
  • x = entity intersections.
  • + *
  • y = avatar intersections.
  • + *
  • z = HUD intersections.
  • + *
+ * Read-only. + *

Note: Property not available in the API.

+ * @property {Vec3} collisionPicksUpdated - The number of collision pick intersections that were found in the most recent game + * loop: + *
    + *
  • x = entity intersections.
  • + *
  • y = avatar intersections.
  • + *
  • z = HUD intersections.
  • + *
+ * Read-only. + *

Note: Property not available in the API.

+ * + * @property {boolean} eventQueueDebuggingOn - true if event queue statistics are provided, false if + * they're not. + * Read-only. + * @property {number} mainThreadQueueDepth - The number of events in the main thread's event queue. + * Only provided if eventQueueDebuggingOn is true. + * Read-only. + * @property {number} nodeListThreadQueueDepth - The number of events in the node list thread's event queue. + * Only provided if eventQueueDebuggingOn is true. + * Read-only. + * + * @comment The following property is from Stats.qml. It shouldn't be in the API. + * @property {string} bgColor + * Read-only. + *

Deprecated: This property is deprecated and will be removed.

+ * + * @comment The following properties are from QQuickItem. They shouldn't be in the API. + * @property {boolean} activeFocus + * Read-only. + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} activeFocusOnTab + *

Deprecated: This property is deprecated and will be removed.

+ * @property {object} anchors + * Read-only. + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} antialiasing + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} baselineOffset + *

Deprecated: This property is deprecated and will be removed.

+ * @property {object[]} children + * Read-only. + *

Note: Property not available in the API.

+ *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} clip + *

Deprecated: This property is deprecated and will be removed.

+ * @property {object} containmentMask + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} enabled + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} focus + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} height + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} implicitHeight + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} implicitWidth + *

Deprecated: This property is deprecated and will be removed.

+ * @property {object} layer + * Read-only. + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} opacity + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} rotation + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} scale + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} smooth + *

Deprecated: This property is deprecated and will be removed.

+ * @property {string} state + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} transformOrigin + *

Deprecated: This property is deprecated and will be removed.

+ * @property {boolean} visible + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} width + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} x + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} y + *

Deprecated: This property is deprecated and will be removed.

+ * @property {number} z + *

Deprecated: This property is deprecated and will be removed.

*/ // Properties from x onwards are QQuickItem properties. @@ -247,6 +512,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, audioMixerPps, 0) STATS_PROPERTY(int, audioOutboundPPS, 0) STATS_PROPERTY(int, audioSilentOutboundPPS, 0) + STATS_PROPERTY(int, audioInboundPPS, 0) STATS_PROPERTY(int, audioAudioInboundPPS, 0) STATS_PROPERTY(int, audioSilentInboundPPS, 0) STATS_PROPERTY(int, audioPacketLoss, 0) @@ -287,6 +553,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, localLeaves, 0) STATS_PROPERTY(int, rectifiedTextureCount, 0) STATS_PROPERTY(int, decimatedTextureCount, 0) + STATS_PROPERTY(int, gpuBuffers, 0) STATS_PROPERTY(int, gpuBufferMemory, 0) STATS_PROPERTY(int, gpuTextures, 0) @@ -352,37 +619,35 @@ public: QStringList downloadUrls () { return _downloadUrls; } public slots: + + /**jsdoc + * Updates statistics to make current values available to scripts even though the statistics overlay may not be displayed. + * (Many statistics values are normally updated only if the statistics overlay is displayed.) + *

Note: Not all statistics values are updated when the statistics overlay isn't displayed or + * expanded.

+ * @function Stats.forceUpdateStats + * @example Report avatar mixer data and packet rates. + * // The statistics to report. + * var stats = [ + * "avatarMixerInKbps", + * "avatarMixerInPps", + * "avatarMixerOutKbps", + * "avatarMixerOutPps" + * ]; + * + * // Update the statistics for the script. + * Stats.forceUpdateStats(); + * + * // Report the statistics. + * for (var i = 0; i < stats.length; i++) { + * print(stats[i], "=", Stats[stats[i]]); + * } + */ void forceUpdateStats() { updateStats(true); } signals: - /**jsdoc - * Triggered when the value of the longsubmits property changes. - * @function Stats.longsubmitsChanged - * @returns {Signal} - */ - void longsubmitsChanged(); - - /**jsdoc - * Triggered when the value of the longrenders property changes. - * @function Stats.longrendersChanged - * @returns {Signal} - */ - void longrendersChanged(); - - /**jsdoc - * Triggered when the value of the longframes property changes. - * @function Stats.longframesChanged - * @returns {Signal} - */ - void longframesChanged(); - - /**jsdoc - * Triggered when the value of the appdropped property changes. - * @function Stats.appdroppedChanged - * @returns {Signal} - */ - void appdroppedChanged(); + // Signals for properties... /**jsdoc * Triggered when the value of the expanded property changes. @@ -419,6 +684,41 @@ signals: */ void presentrateChanged(); + /**jsdoc + * Triggered when the value of the stutterrate property changes. + * @function Stats.stutterrateChanged + * @returns {Signal} + */ + void stutterrateChanged(); + + /**jsdoc + * Triggered when the value of the appdropped property changes. + * @function Stats.appdroppedChanged + * @returns {Signal} + */ + void appdroppedChanged(); + + /**jsdoc + * Triggered when the value of the longsubmits property changes. + * @function Stats.longsubmitsChanged + * @returns {Signal} + */ + void longsubmitsChanged(); + + /**jsdoc + * Triggered when the value of the longrenders property changes. + * @function Stats.longrendersChanged + * @returns {Signal} + */ + void longrendersChanged(); + + /**jsdoc + * Triggered when the value of the longframes property changes. + * @function Stats.longframesChanged + * @returns {Signal} + */ + void longframesChanged(); + /**jsdoc * Triggered when the value of the presentnewrate property changes. * @function Stats.presentnewrateChanged @@ -433,13 +733,6 @@ signals: */ void presentdroprateChanged(); - /**jsdoc - * Triggered when the value of the stutterrate property changes. - * @function Stats.stutterrateChanged - * @returns {Signal} - */ - void stutterrateChanged(); - /**jsdoc * Triggered when the value of the gameLoopRate property changes. * @function Stats.gameLoopRateChanged @@ -447,13 +740,6 @@ signals: */ void gameLoopRateChanged(); - /**jsdoc - * Trigered when - * @function Stats.numPhysicsBodiesChanged - * @returns {Signal} - */ - void physicsObjectCountChanged(); - /**jsdoc * Triggered when the value of the avatarCount property changes. * @function Stats.avatarCountChanged @@ -461,6 +747,34 @@ signals: */ void avatarCountChanged(); + /**jsdoc + * Triggered when the value of the refreshRateTarget property changes. + * @function Stats.refreshRateTargetChanged + * @returns {Signal} + */ + void refreshRateTargetChanged(); + + /**jsdoc + * Triggered when the value of the refreshRateMode property changes. + * @function Stats.refreshRateModeChanged + * @returns {Signal} + */ + void refreshRateModeChanged(); + + /**jsdoc + * Triggered when the value of the refreshRateRegime property changes. + * @function Stats.refreshRateRegimeChanged + * @returns {Signal} + */ + void refreshRateRegimeChanged(); + + /**jsdoc + * Triggered when the value of the uxMode property changes. + * @function Stats.uxModeChanged + * @returns {Signal} + */ + void uxModeChanged(); + /**jsdoc * Triggered when the value of the heroAvatarCount property changes. * @function Stats.heroAvatarCountChanged @@ -468,6 +782,13 @@ signals: */ void heroAvatarCountChanged(); + /**jsdoc + * Triggered when the value of the physicsObjectCount property changes. + * @function Stats.physicsObjectCountChanged + * @returns {Signal} + */ + void physicsObjectCountChanged(); + /**jsdoc * Triggered when the value of the updatedAvatarCount property changes. * @function Stats.updatedAvatarCountChanged @@ -678,10 +999,19 @@ signals: */ void audioSilentOutboundPPSChanged(); + /**jsdoc + * Triggered when the value of the audioInboundPPS property changes. + * @function Stats.audioInboundPPSChanged + * @returns {Signal} + */ + void audioInboundPPSChanged(); + /**jsdoc * Triggered when the value of the audioAudioInboundPPS property changes. * @function Stats.audioAudioInboundPPSChanged * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. Use + * {@link Stats.audioInboundPPSChanged|audioInboundPPSChanged} instead. */ void audioAudioInboundPPSChanged(); @@ -727,7 +1057,6 @@ signals: */ void entityPacketsInKbpsChanged(); - /**jsdoc * Triggered when the value of the downloads property changes. * @function Stats.downloadsChanged @@ -778,11 +1107,10 @@ signals: void trianglesChanged(); /**jsdoc - * Triggered when the value of the drawcalls property changes. - * This - * @function Stats.drawcallsChanged - * @returns {Signal} - */ + * Triggered when the value of the drawcalls property changes. + * @function Stats.drawcallsChanged + * @returns {Signal} + */ void drawcallsChanged(); /**jsdoc @@ -883,6 +1211,20 @@ signals: */ void lodStatusChanged(); + /**jsdoc + * Triggered when the value of the timingStats property changes. + * @function Stats.timingStatsChanged + * @returns {Signal} + */ + void timingStatsChanged(); + + /**jsdoc + * Triggered when the value of the gameUpdateStats property changes. + * @function Stats.gameUpdateStatsChanged + * @returns {Signal} + */ + void gameUpdateStatsChanged(); + /**jsdoc * Triggered when the value of the serverElements property changes. * @function Stats.serverElementsChanged @@ -926,39 +1268,18 @@ signals: void localLeavesChanged(); /**jsdoc - * Triggered when the value of the timingStats property changes. - * @function Stats.timingStatsChanged + * Triggered when the value of the rectifiedTextureCount property changes. + * @function Stats.rectifiedTextureCountChanged * @returns {Signal} */ - void timingStatsChanged(); + void rectifiedTextureCountChanged(); /**jsdoc - * Triggered when the value of the gameUpdateStats property changes. - * @function Stats.gameUpdateStatsChanged + * Triggered when the value of the decimatedTextureCount property changes. + * @function Stats.decimatedTextureCountChanged * @returns {Signal} */ - void gameUpdateStatsChanged(); - - /**jsdoc - * Triggered when the value of the glContextSwapchainMemory property changes. - * @function Stats.glContextSwapchainMemoryChanged - * @returns {Signal} - */ - void glContextSwapchainMemoryChanged(); - - /**jsdoc - * Triggered when the value of the qmlTextureMemory property changes. - * @function Stats.qmlTextureMemoryChanged - * @returns {Signal} - */ - void qmlTextureMemoryChanged(); - - /**jsdoc - * Triggered when the value of the texturePendingTransfers property changes. - * @function Stats.texturePendingTransfersChanged - * @returns {Signal} - */ - void texturePendingTransfersChanged(); + void decimatedTextureCountChanged(); /**jsdoc * Triggered when the value of the gpuBuffers property changes. @@ -981,6 +1302,27 @@ signals: */ void gpuTexturesChanged(); + /**jsdoc + * Triggered when the value of the glContextSwapchainMemory property changes. + * @function Stats.glContextSwapchainMemoryChanged + * @returns {Signal} + */ + void glContextSwapchainMemoryChanged(); + + /**jsdoc + * Triggered when the value of the qmlTextureMemory property changes. + * @function Stats.qmlTextureMemoryChanged + * @returns {Signal} + */ + void qmlTextureMemoryChanged(); + + /**jsdoc + * Triggered when the value of the texturePendingTransfers property changes. + * @function Stats.texturePendingTransfersChanged + * @returns {Signal} + */ + void texturePendingTransfersChanged(); + /**jsdoc * Triggered when the value of the gpuTextureMemory property changes. * @function Stats.gpuTextureMemoryChanged @@ -1045,15 +1387,8 @@ signals: void gpuFreeMemoryChanged(); /**jsdoc - * Triggered when the value of the gpuFrameTime property changes. - * @function Stats.gpuFrameTimeChanged - * @returns {Signal} - */ - void gpuFrameTimeChanged(); - - /**jsdoc - * Triggered when the value of the gpuFrameTime property changes. - * @function Stats.gpuFrameTimeChanged + * Triggered when the value of the gpuFrameSize property changes. + * @function Stats.gpuFrameSizeChanged * @returns {Signal} */ void gpuFrameSizeChanged(); @@ -1063,6 +1398,13 @@ signals: * @function Stats.gpuFrameTimeChanged * @returns {Signal} */ + void gpuFrameTimeChanged(); + + /**jsdoc + * Triggered when the value of the gpuFrameTimePerPixel property changes. + * @function Stats.gpuFrameTimePerPixelChanged + * @returns {Signal} + */ void gpuFrameTimePerPixelChanged(); /**jsdoc @@ -1086,250 +1428,6 @@ signals: */ void avatarSimulationTimeChanged(); - /**jsdoc - * Triggered when the value of the rectifiedTextureCount property changes. - * @function Stats.rectifiedTextureCountChanged - * @returns {Signal} - */ - void rectifiedTextureCountChanged(); - - /**jsdoc - * Triggered when the value of the decimatedTextureCount property changes. - * @function Stats.decimatedTextureCountChanged - * @returns {Signal} - */ - void decimatedTextureCountChanged(); - - - void refreshRateTargetChanged(); - - void refreshRateModeChanged(); - - void refreshRateRegimeChanged(); - - void uxModeChanged(); - - // QQuickItem signals. - - /**jsdoc - * Triggered when the parent item changes. - * @function Stats.parentChanged - * @param {object} parent - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the x property changes. - * @function Stats.xChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the y property changes. - * @function Stats.yChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the z property changes. - * @function Stats.zChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the width property changes. - * @function Stats.widthChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the height property changes. - * @function Stats.heightChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the opacity property changes. - * @function Stats.opacityChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the enabled property changes. - * @function Stats.enabledChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the visibleChanged property changes. - * @function Stats.visibleChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the list of visible children changes. - * @function Stats.visibleChildrenChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the state property changes. - * @function Stats.stateChanged - * @paramm {string} state - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the position and size of the rectangle containing the children changes. - * @function Stats.childrenRectChanged - * @param {Rect} childrenRect - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the baselineOffset property changes. - * @function Stats.baselineOffsetChanged - * @param {number} baselineOffset - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the clip property changes. - * @function Stats.clipChanged - * @param {boolean} clip - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the focus property changes. - * @function Stats.focusChanged - * @param {boolean} focus - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the activeFocus property changes. - * @function Stats.activeFocusChanged - * @param {boolean} activeFocus - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the activeFocusOnTab property changes. - * @function Stats.activeFocusOnTabChanged - * @param {boolean} activeFocusOnTab - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the rotation property changes. - * @function Stats.rotationChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the scaleChanged property changes. - * @function Stats.scaleChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the transformOrigin property changes. - * @function Stats.transformOriginChanged - * @param {number} transformOrigin - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the smooth property changes. - * @function Stats.smoothChanged - * @param {boolean} smooth - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the antialiasing property changes. - * @function Stats.antialiasingChanged - * @param {boolean} antialiasing - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the implicitWidth property changes. - * @function Stats.implicitWidthChanged - * @returns {Signal} - */ - - /**jsdoc - * Triggered when the value of the implicitHeight property changes. - * @function Stats.implicitHeightChanged - * @returns {Signal} - */ - - /**jsdoc - * @function Stats.windowChanged - * @param {object} window - * @returns {Signal} - */ - - - // QQuickItem functions. - - /**jsdoc - * @function Stats.grabToImage - * @param {object} callback - * @param {Size} [targetSize=0,0] - * @returns {boolean} - */ - - /**jsdoc - * @function Stats.contains - * @param {Vec2} point - * @returns {boolean} - */ - - /**jsdoc - * @function Stats.mapFromItem - * @param {object} item - */ - - /**jsdoc - * @function Stats.mapToItem - * @param {object} item - */ - - /**jsdoc - * @function Stats.mapFromGlobal - * @param {object} global - */ - - /**jsdoc - * @function Stats.mapToGlobal - * @param {object} global - */ - - /**jsdoc - * @function Stats.forceActiveFocus - * @param {number} [reason=7] - */ - - /**jsdoc - * @function Stats.nextItemInFocusChain - * @param {boolean} [forward=true] - * @returns {object} - */ - - /**jsdoc - * @function Stats.childAt - * @param {number} x - * @param {number} y - * @returns {object} - */ - - /**jsdoc - * @function Stats.update - */ - /**jsdoc * Triggered when the value of the stylusPicksCount property changes. * @function Stats.stylusPicksCountChanged @@ -1387,11 +1485,11 @@ signals: void collisionPicksUpdatedChanged(); /**jsdoc - * Triggered when the value of the eventQueueDebuggingOn property changes. - * @function Stats.eventQueueDebuggingOn + * Triggered when the value of the mainThreadQueueDepth property changes. + * @function Stats.mainThreadQueueDepthChanged * @returns {Signal} */ - void eventQueueDebuggingOnChanged(); + void mainThreadQueueDepthChanged(); /**jsdoc * Triggered when the value of the nodeListThreadQueueDepth property changes. @@ -1401,11 +1499,294 @@ signals: void nodeListThreadQueueDepthChanged(); /**jsdoc - * Triggered when the value of the nodeListThreadQueueDepth property changes. - * @function Stats.nodeListThreadQueueDepth + * Triggered when the value of the eventQueueDebuggingOn property changes. + * @function Stats.eventQueueDebuggingOnChanged * @returns {Signal} */ - void mainThreadQueueDepthChanged(); + void eventQueueDebuggingOnChanged(); + + + // Stats.qml signals: shouldn't be in the API. + + /**jsdoc + * Triggered when the value of the bgColor property changes. + * @function Stats.bgColorChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + + // QQuickItem signals: shouldn't be in the API. + + /**jsdoc + * Triggered when the value of the activeFocus property changes. + * @function Stats.activeFocusChanged + * @param {boolean} activeFocus - Active focus. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the activeFocusOnTab property changes. + * @function Stats.activeFocusOnTabChanged + * @param {boolean} activeFocusOnTab - Active focus on tab. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the antialiasing property changes. + * @function Stats.antialiasingChanged + * @param {boolean} antialiasing - Antialiasing. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the baselineOffset property changes. + * @function Stats.baselineOffsetChanged + * @param {number} baselineOffset - Baseline offset. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the children property changes. + * @function Stats.childrenChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the position and size of the rectangle containing the children changes. + * @function Stats.childrenRectChanged + * @param {Rect} childrenRect - Children rect. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + + /**jsdoc + * Triggered when the value of the clip property changes. + * @function Stats.clipChanged + * @param {boolean} clip - Clip. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the containmentMask property changes. + * @function Stats.containmentMaskChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the enabled property changes. + * @function Stats.enabledChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the focus property changes. + * @function Stats.focusChanged + * @param {boolean} focus - Focus. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the height property changes. + * @function Stats.heightChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the implicitHeight property changes. + * @function Stats.implicitHeightChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the implicitWidth property changes. + * @function Stats.implicitWidthChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the opacity property changes. + * @function Stats.opacityChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the parent item changes. + * @function Stats.parentChanged + * @param {object} parent - Parent. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the rotation property changes. + * @function Stats.rotationChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the scale property changes. + * @function Stats.scaleChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the smooth property changes. + * @function Stats.smoothChanged + * @param {boolean} smooth - Smooth. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the state property changes. + * @function Stats.stateChanged + * @paramm {string} state - State. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the transformOrigin property changes. + * @function Stats.transformOriginChanged + * @param {number} transformOrigin - Transformm origin. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the visibleChanged property changes. + * @function Stats.visibleChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the list of visible children changes. + * @function Stats.visibleChildrenChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the width property changes. + * @function Stats.widthChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the stats window changes. + * @function Stats.windowChanged + * @param {object} window - Window. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the x property changes. + * @function Stats.xChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the y property changes. + * @function Stats.yChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + /**jsdoc + * Triggered when the value of the z property changes. + * @function Stats.zChanged + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + + + // QQuickItem methods: shouldn't be in the API. + + /**jsdoc + * @function Stats.childAt + * @param {number} x - X. + * @param {number} y - Y. + * @returns {object} + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.contains + * @param {Vec2} point - Point + * @returns {boolean} + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.forceActiveFocus + * @param {number} [reason=7] - Reason + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.grabToImage + * @param {object} callback - Callback. + * @param {Size} [targetSize=0,0] - Target size. + * @returns {boolean} + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.mapFromGlobal + * @param {object} global - Global. + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.mapFromItem + * @param {object} item - Item. + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.mapToGlobal + * @param {object} global - Global. + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.mapToItem + * @param {object} item - Item + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.nextItemInFocusChain + * @param {boolean} [forward=true] - Forward. + * @returns {object} + * @deprecated This method is deprecated and will be removed. + */ + + /**jsdoc + * @function Stats.update + * @deprecated This method is deprecated and will be removed. + */ private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index e688d1c115..5d11b254fc 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,6 +29,22 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" +/**jsdoc + * The ContextOverlay API manages the "i" proof-of-provenance context overlay that appears on Marketplace items + * when a user right-clicks them. + * + * @namespace ContextOverlay + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {boolean} enabled - true if the context overlay is enabled to be displayed, false if it + * is disabled and will never be displayed. + * @property {Uuid} entityWithContextOverlay - The ID of the entity that the context overlay is currently displayed for, + * null if the context overlay is not currently displayed. + * @property {boolean} isInMarketplaceInspectionMode - Currently not used. + */ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT @@ -44,30 +60,140 @@ class ContextOverlayInterface : public QObject, public Dependency { QUuid _contextOverlayID { UNKNOWN_ENTITY_ID }; public: ContextOverlayInterface(); + + /**jsdoc + * Gets the ID of the entity that the context overlay is currently displayed for. + * @function ContextOverlay.getCurrentEntityWithContextOverlay + * @returns {Uuid} - The ID of the entity that the context overlay is currently displayed for, null if the + * context overlay is not currently displayed. + */ Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } + void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); } void setEnabled(bool enabled); bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } + + /**jsdoc + * Initiates a check on an avatar entity belongs to the user wearing it. The result is returned via + * {@link WalletScriptingInterface.ownershipVerificationSuccess} or + * {@link WalletScriptingInterface.ownershipVerificationFailed}. + *

Warning: Neither of these signals are triggered if the entity is not an avatar entity or is not + * certified.

+ * @function ContextOverlay.requestOwnershipVerification + * @param {Uuid} entityID - The ID of the entity to check. + */ Q_INVOKABLE void requestOwnershipVerification(const QUuid& entityID); + EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; } signals: + /**jsdoc + * Triggered when the user clicks on the context overlay. + * @function ContextOverlay.contextOverlayClicked + * @param {Uuid} id - The ID of the entity that the context overlay is for. + * @returns {Signal} + * @example Report when a context overlay is clicked. + * ContextOverlay.contextOverlayClicked.connect(function (id) { + * print("Context overlay clicked for:", id); + * }); + */ void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); public slots: + + /**jsdoc + * @function ContextOverlay.clickDownOnEntity + * @param {Uuid} id - Entity ID. + * @param {PointerEvent} event - Pointer event. + * @deprecated This method is deprecated and will be removed. + */ + // FIXME: Method shouldn't be in the API. void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * @function ContextOverlay.mouseReleaseOnEntity + * @param {Uuid} id - Entity ID. + * @param {PointerEvent} event - Pointer event. + * @deprecated This method is deprecated and will be removed. + */ + // FIXME: Method shouldn't be in the API. void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + /**jsdoc + * Displays or deletes the context overlay as appropriate for the target entity and a pointer event: the context overlay + * must be enabled and the pointer event must be a right-click; if so, then any current context overlay is deleted, and if + * the target entity should have a context overlay then it is displayed. + * @function ContextOverlay.createOrDestroyContextOverlay + * @param {Uuid} entityID - The target entity. + * @param {PointerEvent} pointerEvent - The pointer event. + * @returns {boolean} - true if the context overlay was deleted or displayed on the specified entity, + * false if no action was taken. + */ bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Deletes the context overlay and removes the entity highlight, if shown. + * @function ContextOverlay.destroyContextOverlay + * @param {Uuid} entityID - The ID of the entity. + * @param {PointerEvent} [event] - Not used. + * @returns {boolean} - true if the context overlay was deleted, false if it wasn't (e.g., it + * wasn't displayed). + */ bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); + + /**jsdoc + * @function ContextOverlay.contextOverlays_hoverEnterOverlay + * @param {Uuid} id - Overlay ID. + * @param {PointerEvent} event - Pointer event. + * @deprecated This method is deprecated and will be removed. + */ + // FIXME: Method shouldn't be in the API. void contextOverlays_hoverEnterOverlay(const QUuid& id, const PointerEvent& event); + + /**jsdoc + * @function ContextOverlay.contextOverlays_hoverLeaveOverlay + * @param {Uuid} id - Overlay ID. + * @param {PointerEvent} event - Pointer event. + * @deprecated This method is deprecated and will be removed. + */ + // FIXME: Method shouldn't be in the API. void contextOverlays_hoverLeaveOverlay(const QUuid& id, const PointerEvent& event); + + /**jsdoc + * @function ContextOverlay.contextOverlays_hoverEnterEntity + * @param {Uuid} id - Entity ID. + * @param {PointerEvent} event - Pointer event. + * @deprecated This method is deprecated and will be removed. + */ + // FIXME: Method shouldn't be in the API. void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); + + /**jsdoc + * @function ContextOverlay.contextOverlays_hoverLeaveEntity + * @param {Uuid} id - Entity ID. + * @param {PointerEvent} event - Pointer event. + * @deprecated This method is deprecated and will be removed. + */ + // FIXME: Method shouldn't be in the API. void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); + + /**jsdoc + * Checks with a context overlay should be displayed for an entity — in particular, whether the item has a non-empty + * certificate ID. + * @function ContextOverlay.contextOverlayFilterPassed + * @param {Uuid} entityID - The ID of the entity to check. + * @returns {boolean} - true if the context overlay should be shown for the entity, false if it + * shouldn't. + * @example Report whether the context overlay should be displayed for entities clicked. + * Entities.clickDownOnEntity.connect(function (id, event) { + * print("Item clicked:", id); + * print("Should display context overlay:", ContextOverlay.contextOverlayFilterPassed(id)); + * }); + */ bool contextOverlayFilterPassed(const EntityItemID& entityItemID); private slots: diff --git a/interface/src/workload/GameWorkload.cpp b/interface/src/workload/GameWorkload.cpp index afbd166c89..d9cda7f16a 100644 --- a/interface/src/workload/GameWorkload.cpp +++ b/interface/src/workload/GameWorkload.cpp @@ -9,6 +9,7 @@ // #include "GameWorkload.h" #include "GameWorkloadRenderer.h" +#include "SelectedWorkloadRenderer.h" #include #include #include @@ -35,6 +36,7 @@ public: model.addJob("PhysicsBoundary", regionTrackerOut); model.addJob("SpaceToRender"); + model.addJob("SelectedWorkloadRender"); out = regionTrackerOut; } diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index 2bb73999f1..f65bf88754 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -16,6 +16,7 @@ #include #include +#include "SelectedWorkloadRenderer.h" void GameSpaceToRender::configure(const Config& config) { _freezeViews = config.freezeViews; diff --git a/interface/src/workload/SelectedWorkloadRenderer.cpp b/interface/src/workload/SelectedWorkloadRenderer.cpp new file mode 100644 index 0000000000..29cdc46f35 --- /dev/null +++ b/interface/src/workload/SelectedWorkloadRenderer.cpp @@ -0,0 +1,88 @@ +// +// SelectedWorkloadRenderer.cpp +// +// Created by Andrew Meadows 2019.11.08 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "SelectedWorkloadRenderer.h" + +#include +#include + +#include + +#include "Application.h" +#include "GameWorkloadRenderer.h" +#include "scripting/SelectionScriptingInterface.h" + +void SelectedWorkloadRenderer::run(const workload::WorkloadContextPointer& runContext, Outputs& outputs) { + auto gameWorkloadContext = std::dynamic_pointer_cast(runContext); + if (!gameWorkloadContext) { + return; + } + auto space = gameWorkloadContext->_space; + if (!space) { + return; + } + + render::Transaction transaction; + auto scene = gameWorkloadContext->_scene; + + auto selection = DependencyManager::get(); + // Note: the "DebugWorkloadSelection" name is a secret hard-coded C++ debug feature. + // If you create such a named list using JS and the "Selection" API then it will be picked up here + // and the workload proxies for corresponding entities will be rendered. + GameplayObjects selectedObjects = selection->getList("DebugWorkloadSelection"); + + if (!selectedObjects.getContainsData()) { + // nothing to render + // clear item if it exists and bail + if (render::Item::isValidID(_spaceRenderItemID)) { + transaction.updateItem(_spaceRenderItemID, [](GameWorkloadRenderItem& item) { + item.setVisible(false); + }); + scene->enqueueTransaction(transaction); + } + return; + } + + std::vector entityIDs = selectedObjects.getEntityIDs(); + workload::indexed_container::Indices indices; + indices.reserve(entityIDs.size()); + + auto entityTreeRenderer = qApp->getEntities(); + auto entityTree = entityTreeRenderer->getTree(); + for (auto id : entityIDs) { + EntityItemPointer entity = entityTree->findEntityByID(id); + if (entity) { + indices.push_back(entity->getSpaceIndex()); + } + } + + workload::Proxy::Vector proxies; + proxies.reserve(indices.size()); + space->copySelectedProxyValues(proxies, indices); + + if (!render::Item::isValidID(_spaceRenderItemID)) { + _spaceRenderItemID = scene->allocateID(); + auto renderItem = std::make_shared(); + renderItem->editBound().setBox(glm::vec3(-16000.0f), 32000.0f); + transaction.resetItem(_spaceRenderItemID, std::make_shared(renderItem)); + } + + bool showProxies = true; + bool showViews = false; + bool visible = true; + workload::Views views(0); + transaction.updateItem(_spaceRenderItemID, [visible, showProxies, proxies, showViews, views](GameWorkloadRenderItem& item) { + item.setVisible(visible); + item.showProxies(showProxies); + item.setAllProxies(proxies); + item.showViews(showViews); + item.setAllViews(views); + }); + scene->enqueueTransaction(transaction); +} diff --git a/interface/src/workload/SelectedWorkloadRenderer.h b/interface/src/workload/SelectedWorkloadRenderer.h new file mode 100644 index 0000000000..9b3ac1005e --- /dev/null +++ b/interface/src/workload/SelectedWorkloadRenderer.h @@ -0,0 +1,32 @@ +// +// GameWorkloadRender.h +// +// Created by Sam Gateau on 2/20/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_SelectedWorkloadRenderer_h +#define hifi_SelectedWorkloadRenderer_h + +#include "GameWorkload.h" + +#include "GameWorkloadRenderer.h" + +class SelectedWorkloadRenderer { +public: + using Config = GameSpaceToRenderConfig; + using Outputs = render::Transaction; + using JobModel = workload::Job::ModelO; + + SelectedWorkloadRenderer() {} + + void configure(const Config& config) {} + void run(const workload::WorkloadContextPointer& renderContext, Outputs& outputs); + +protected: + render::ItemID _spaceRenderItemID{ render::Item::INVALID_ITEM_ID }; +}; + +#endif diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt index 48ae0485b5..50f0cbc16f 100644 --- a/launchers/darwin/CMakeLists.txt +++ b/launchers/darwin/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0) -set(ENV{MACOSX_DEPLOYMENT_TARGET} 10.9) +set(ENV{MACOSX_DEPLOYMENT_TARGET} 10.11) project(HQLauncher) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") include("cmake/macros/SetPackagingParameters.cmake") @@ -143,13 +143,13 @@ set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}) include(CPackComponent) set(CPACK_PACKAGE_NAME "HQ Launcher") -set(CPACK_PACKAGE_VENDOR "High Fidelity") +set(CPACK_PACKAGE_VENDOR "Vircadia") set(CPACK_PACKAGE_VERSION ${BUILD_VERSION}) set(CPACK_PACKAGE_FILE_NAME "HQ Launcher") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) -set(DMG_SUBFOLDER_NAME "High Fidelity") +set(DMG_SUBFOLDER_NAME "Vircadia") set(ESCAPED_DMG_SUBFOLDER_NAME "") set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc") diff --git a/launchers/qt/BUILD.md b/launchers/qt/BUILD.md new file mode 100644 index 0000000000..d08aa6bfa0 --- /dev/null +++ b/launchers/qt/BUILD.md @@ -0,0 +1,13 @@ +# Dependencies +- [cmake](https://cmake.org/download/): 3.9 + +# Windows +* Download `Visual Studio 2019` +`cmake -G "Visual Studio 16 2019" ..` + +# MacOS +* Install `Xcode` +`cmake -G Xcode ..` + + +If you wish to not use the compiled qml files, pass the `-DLAUNCHER_SOURCE_TREE_RESOURCES=On` argument to cmake. \ No newline at end of file diff --git a/launchers/qt/CMakeLists.txt b/launchers/qt/CMakeLists.txt index 596f3b7d5c..400615be90 100644 --- a/launchers/qt/CMakeLists.txt +++ b/launchers/qt/CMakeLists.txt @@ -276,12 +276,12 @@ if (APPLE) include(CPackComponent) set(CPACK_PACKAGE_NAME "HQ Launcher") - set(CPACK_PACKAGE_VENDOR "High Fidelity") + set(CPACK_PACKAGE_VENDOR "Vircadia") set(CPACK_PACKAGE_FILE_NAME "HQ Launcher") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) - set(DMG_SUBFOLDER_NAME "High Fidelity") + set(DMG_SUBFOLDER_NAME "Vircadia") set(ESCAPED_DMG_SUBFOLDER_NAME "") set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc") diff --git a/launchers/qt/readme.md b/launchers/qt/readme.md new file mode 100644 index 0000000000..ce4e846172 --- /dev/null +++ b/launchers/qt/readme.md @@ -0,0 +1,34 @@ +# HQ Launcher +Behavior of the HQ Launcher is as follows: +* Update the HQ Launcher to the latest version +* Sign up or sign in if is the user is not already signed in +* Download the latest Interface client +* Launch the user in the current HQ domain + +# directory structure + +## src/ - contains the c++ and objective-c. +* `BuildsRequest` - getting / parsing the build info from thunder api +* `CommandlineOptions` - parses and stores commandline arguments +* `Helper` - helper functions +* `Helper_darwin` - objective-c implemention of helper funcions +* `Helper_windows` - helper function that depend on windows api +* `Launcher` - initialized the Launcher Application and resources +* `LauncherInstaller_windows` - logic of how to install/uninstall HQ Launcher on windows +* `LauncherState` - hold majority of the logic of the launcher (signin, config file, updating, running launcher) + * config files hold the following saved data + * logged in + * home location +* `LauncherWindows` - wrapper for `QQuickWindow` that implements drag feature +* `LoginRequest` - checks the login credentials the user typed in. +* `NSTask+NSTaskExecveAdditions` - Extension of NSTask for replacing Launcher process with interface client process +* `PathUtils` - Helper class for getting relative paths for HQ Launcher +* `SignupRequest` - Determines if the users request to signup for a new account succeeded based on the entered credentials +* `Unzipper` - helper class for extracting zip files +* `UserSettingsRequest` - getting the users setting (home location) from metaverse + +## resources/ +* `images/`- Holds the images and icon that are used by the launcher +* `qml/` + * UI elements + * `QML_FILE_FOR_UI_STATE` variable in `LauncherState` defines what QML files are used by the Launcher. \ No newline at end of file diff --git a/launchers/qt/resources/qml/HFControls/HFTextLogo.qml b/launchers/qt/resources/qml/HFControls/HFTextLogo.qml index b8d06f16f1..f377c9a34d 100644 --- a/launchers/qt/resources/qml/HFControls/HFTextLogo.qml +++ b/launchers/qt/resources/qml/HFControls/HFTextLogo.qml @@ -2,7 +2,7 @@ import QtQuick 2.3 import QtQuick 2.1 Text { - text: "High Fidelity" + text: "Vircadia" font.bold: true font.family: "Graphik Semibold" font.pixelSize: 17 diff --git a/launchers/qt/src/BuildsRequest.h b/launchers/qt/src/BuildsRequest.h index 865d375d2a..cb33182cdb 100644 --- a/launchers/qt/src/BuildsRequest.h +++ b/launchers/qt/src/BuildsRequest.h @@ -4,10 +4,10 @@ #include struct Build { - QString tag{ QString::null }; + QString tag; int latestVersion{ 0 }; int buildNumber{ 0 }; - QString installerZipURL{ QString::null }; + QString installerZipURL; }; struct Builds { diff --git a/launchers/qt/src/Launcher.cpp b/launchers/qt/src/Launcher.cpp index 7522529ff9..bd54619487 100644 --- a/launchers/qt/src/Launcher.cpp +++ b/launchers/qt/src/Launcher.cpp @@ -21,7 +21,7 @@ Launcher::Launcher(int& argc, char**argv) : QGuiApplication(argc, argv) { _launcherWindow->rootContext()->setContextProperty("LauncherState", _launcherState.get()); _launcherWindow->rootContext()->setContextProperty("PathUtils", new PathUtils()); _launcherWindow->rootContext()->setContextProperty("Platform", platform); - _launcherWindow->setTitle("High Fidelity"); + _launcherWindow->setTitle("Vircadia"); _launcherWindow->setFlags(Qt::FramelessWindowHint | Qt::Window); _launcherWindow->setLauncherStatePtr(_launcherState); diff --git a/launchers/qt/src/LauncherInstaller_windows.cpp b/launchers/qt/src/LauncherInstaller_windows.cpp index b5558f972c..cf5b2b501f 100644 --- a/launchers/qt/src/LauncherInstaller_windows.cpp +++ b/launchers/qt/src/LauncherInstaller_windows.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -253,7 +253,7 @@ void LauncherInstaller::createApplicationRegistryKeys() { success = insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath); success = insertRegistryKey(REGISTRY_PATH, "DisplayVersion", std::string(LAUNCHER_BUILD_VERSION)); success = insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe); - success = insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity"); + success = insertRegistryKey(REGISTRY_PATH, "Publisher", "Vircadia"); auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); diff --git a/launchers/qt/src/LauncherState.cpp b/launchers/qt/src/LauncherState.cpp index 300bd37ef8..03e91ae58f 100644 --- a/launchers/qt/src/LauncherState.cpp +++ b/launchers/qt/src/LauncherState.cpp @@ -244,7 +244,7 @@ void LauncherState::getCurrentClientVersion() { if (match.hasMatch()) { _currentClientVersion = match.captured("version"); } else { - _currentClientVersion = QString::null; + _currentClientVersion.clear(); } qDebug() << "Current client version is: " << _currentClientVersion; diff --git a/launchers/qt/src/LauncherState.h b/launchers/qt/src/LauncherState.h index 39c4141b81..c0eb44cb08 100644 --- a/launchers/qt/src/LauncherState.h +++ b/launchers/qt/src/LauncherState.h @@ -176,7 +176,7 @@ private: QString _displayName; QString _applicationErrorMessage; QString _currentClientVersion; - QString _buildTag { QString::null }; + QString _buildTag; QString _contentCacheURL; QString _loginTokenResponse; QFile _clientZipFile; diff --git a/launchers/qt/src/UserSettingsRequest.h b/launchers/qt/src/UserSettingsRequest.h index 5827364377..a42a70df37 100644 --- a/launchers/qt/src/UserSettingsRequest.h +++ b/launchers/qt/src/UserSettingsRequest.h @@ -6,7 +6,7 @@ #include "LoginRequest.h" struct UserSettings { - QString homeLocation{ QString::null }; + QString homeLocation; }; class UserSettingsRequest : public QObject { diff --git a/launchers/qt/src/main.cpp b/launchers/qt/src/main.cpp index 75c35cd708..c9ce0caeb1 100644 --- a/launchers/qt/src/main.cpp +++ b/launchers/qt/src/main.cpp @@ -31,7 +31,7 @@ bool hasSuffix(const std::string& path, const std::string& suffix) { int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setOrganizationName("High Fidelity"); + QCoreApplication::setOrganizationName("Vircadia"); QCoreApplication::setApplicationName("HQ Launcher"); Q_INIT_RESOURCE(resources); diff --git a/launchers/win32/LauncherManager.cpp b/launchers/win32/LauncherManager.cpp index 60bcb0f004..6dfdc46c6f 100644 --- a/launchers/win32/LauncherManager.cpp +++ b/launchers/win32/LauncherManager.cpp @@ -310,7 +310,7 @@ BOOL LauncherManager::getAndCreatePaths(PathType type, CString& outPath) { outPath += DIRECTORY_NAME_INTERFACE; } else if (type == PathType::Content_Directory) { outPath += DIRECTORY_NAME_CONTENT; - } + } return (CreateDirectory(outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError()); } } @@ -377,7 +377,7 @@ BOOL LauncherManager::createConfigJSON() { return TRUE; } -LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain, +LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain, CString& content, bool& loggedIn, CString& organizationBuildTag) { CString configPath; getAndCreatePaths(PathType::Interface_Directory, configPath); @@ -388,7 +388,7 @@ LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, C } Json::Value config; configFile >> config; - if (config["version"].isString() && + if (config["version"].isString() && config["domain"].isString() && config["content"].isString()) { loggedIn = config["loggedIn"].asBool(); @@ -446,7 +446,7 @@ LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString CString url = _T("/organizations/") + hash + _T(".json"); LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(), true, L"orgs.highfidelity.com", url, - contentTypeJson, CStringA(), + contentTypeJson, CStringA(), response, false); if (error != LauncherUtils::ResponseError::NoError) { return error; @@ -557,7 +557,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch addToLog(_T("Already running most recent build. Launching interface.exe")); } else { addToLog(_T("Updating the launcher was not allowed --noUpdate")); - } + } if (isInstalled) { addToLog(_T("Installed version: ") + currentVersion); if (!newInterfaceVersion) { @@ -576,7 +576,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch } } _shouldWait = FALSE; - + } else { setFailed(true); CString msg; @@ -587,7 +587,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch } } -LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username, +LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username, const CString& password) { CStringA post = "grant_type=password&username="; post += username; @@ -599,9 +599,9 @@ LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString response; LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(), true, - L"metaverse.highfidelity.com", + L"metaverse.highfidelity.com", L"/oauth/token", - contentTypeText, post, + contentTypeText, post, response, true); if (error != LauncherUtils::ResponseError::NoError) { return error; @@ -629,7 +629,7 @@ BOOL LauncherManager::createApplicationRegistryKeys(int size) { success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayVersion", LauncherUtils::cStringToStd(_latestVersion)); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe); - success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity"); + success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "Vircadia"); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallDate", LauncherUtils::cStringToStd(CTime::GetCurrentTime().Format("%Y%m%d"))); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "EstimatedSize", (DWORD)size); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoModify", (DWORD)1); @@ -686,9 +686,9 @@ BOOL LauncherManager::extractApplication() { updateProgress(ProcessType::UnzipApplication, max(progress, 0.0f)); }; _currentProcess = ProcessType::UnzipApplication; - BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication, + BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication, LauncherUtils::cStringToStd(_applicationZipPath), - LauncherUtils::cStringToStd(installPath), + LauncherUtils::cStringToStd(installPath), onExtractFinished, onProgress); if (success) { addToLog(_T("Created thread for unzipping application.")); @@ -737,7 +737,7 @@ void LauncherManager::restartNewLauncher() { continueAction = ContinueActionOnStart::ContinueUpdate; } else if (_keepLoggingIn) { continueAction = ContinueActionOnStart::ContinueLogIn; - } + } CStringW params; params.Format(_T(" --restart --noUpdate --continueAction %s"), getContinueActionParam(continueAction)); LauncherUtils::launchApplication(_tempLauncherPath, params.GetBuffer()); diff --git a/libraries/animation/src/AnimBlendDirectional.cpp b/libraries/animation/src/AnimBlendDirectional.cpp index 4e7c67f276..4cc67683da 100644 --- a/libraries/animation/src/AnimBlendDirectional.cpp +++ b/libraries/animation/src/AnimBlendDirectional.cpp @@ -96,7 +96,9 @@ const AnimPoseVec& AnimBlendDirectional::evaluate(const AnimVariantMap& animVars } } _poses.resize(minSize); - blend4(minSize, &poseVecs[0][0], &poseVecs[1][0], &poseVecs[2][0], &poseVecs[3][0], &alphas[0], &_poses[0]); + if (minSize > 0) { + blend4(minSize, &poseVecs[0][0], &poseVecs[1][0], &poseVecs[2][0], &poseVecs[3][0], &alphas[0], &_poses[0]); + } // animation stack debug stats for (int i = 0; i < 9; i++) { diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 621323575f..2fd52ee036 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -26,6 +26,9 @@ static const int MAX_TARGET_MARKERS = 30; static const float JOINT_CHAIN_INTERP_TIME = 0.5f; +static QTime debounceJointWarningsClock; +static const int JOINT_WARNING_DEBOUNCE_TIME = 30000; // 30 seconds + static void lookupJointInfo(const AnimInverseKinematics::JointChainInfo& jointChainInfo, int indexA, int indexB, const AnimInverseKinematics::JointInfo** jointInfoA, @@ -91,6 +94,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const IKTargetVar& orig) : } AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimNode::Type::InverseKinematics, id) { + debounceJointWarningsClock.start(); } AnimInverseKinematics::~AnimInverseKinematics() { @@ -158,6 +162,14 @@ void AnimInverseKinematics::setTargetVars(const QString& jointName, const QStrin } } +bool debounceJointWarnings() { + if (debounceJointWarningsClock.elapsed() >= JOINT_WARNING_DEBOUNCE_TIME) { + debounceJointWarningsClock.restart(); + return true; + } + return false; +} + void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { _hipsTargetIndex = -1; @@ -172,7 +184,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: if (jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices targetVar.jointIndex = jointIndex; - } else { + } else if (debounceJointWarnings()) { qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton"; } } diff --git a/libraries/animation/src/AnimationCacheScriptingInterface.h b/libraries/animation/src/AnimationCacheScriptingInterface.h index 0ceb302913..fc31ecaa2b 100644 --- a/libraries/animation/src/AnimationCacheScriptingInterface.h +++ b/libraries/animation/src/AnimationCacheScriptingInterface.h @@ -38,6 +38,10 @@ class AnimationCacheScriptingInterface : public ScriptableResourceCache, public * @property {number} numCached - Total number of cached resource. Read-only. * @property {number} sizeTotal - Size in bytes of all resources. Read-only. * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers). + * Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers). + * Read-only. * * @borrows ResourceCache.getResourceList as getResourceList * @borrows ResourceCache.updateTotalSize as updateTotalSize diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index 303d0e0d9e..40fd534a71 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -23,6 +23,7 @@ class QScriptEngine; * Information about an animation resource, created by {@link AnimationCache.getAnimation}. * * @class AnimationObject + * @hideconstructor * * @hifi-interface * @hifi-client-entity @@ -57,9 +58,10 @@ public: }; /**jsdoc - * Joint rotations in one frame of an animation. + * Joint rotations in one frame of an {@link AnimationObject}. * * @class AnimationFrameObject + * @hideconstructor * * @hifi-interface * @hifi-client-entity diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 85b9ecdc42..06fe558964 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1093,6 +1093,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec + //stategraph vars based on input + const float INPUT_DEADZONE_THRESHOLD = 0.05f; + const float SLOW_SPEED_THRESHOLD = 1.5f; + const float HAS_MOMENTUM_THRESHOLD = 2.2f; + const float RESET_MOMENTUM_THRESHOLD = 0.05f; + if (ccState == CharacterControllerState::Hover) { if (_desiredState != RigRole::Hover) { _desiredStateAge = 0.0f; @@ -1171,6 +1177,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge += deltaTime; + if (_state == RigRole::Move) { glm::vec3 horizontalVel = localVel - glm::vec3(0.0f, localVel.y, 0.0f); if (glm::length(horizontalVel) > MOVE_ENTER_SPEED_THRESHOLD) { @@ -1244,6 +1251,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { @@ -1274,6 +1284,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Idle) { // default anim vars to notMoving and notTurning @@ -1297,6 +1310,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Hover) { // flying. @@ -1320,6 +1336,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::Takeoff) { // jumping in-air @@ -1351,6 +1370,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotInAir", false); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); } else if (_state == RigRole::InAir) { // jumping in-air @@ -1371,6 +1393,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTakeoff", true); _animVars.set("isSeated", false); _animVars.set("isNotSeated", true); + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); bool inAirRun = forwardSpeed > 0.1f; if (inAirRun) { @@ -1393,6 +1418,23 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("inAirAlpha", alpha); } else if (_state == RigRole::Seated) { + if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD) { + // seated not turning + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", true); + } else if (_previousControllerParameters.inputX > 0.0f) { + // seated turning right + _animVars.set("isSeatedTurningRight", true); + _animVars.set("isSeatedTurningLeft", false); + _animVars.set("isSeatedNotTurning", false); + } else { + // seated turning left + _animVars.set("isSeatedTurningRight", false); + _animVars.set("isSeatedTurningLeft", true); + _animVars.set("isSeatedNotTurning", false); + } + _animVars.set("isMovingForward", false); _animVars.set("isMovingBackward", false); _animVars.set("isMovingRight", false); @@ -1434,20 +1476,36 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastEnableInverseKinematics = _enableInverseKinematics; - //stategraph vars based on input - const float INPUT_DEADZONE_THRESHOLD = 0.05f; - const float SLOW_SPEED_THRESHOLD = 1.5f; + if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD && fabsf(_previousControllerParameters.inputZ) <= INPUT_DEADZONE_THRESHOLD) { // no WASD input if (fabsf(forwardSpeed) <= SLOW_SPEED_THRESHOLD && fabsf(lateralSpeed) <= SLOW_SPEED_THRESHOLD) { + + //reset this when stopped + if (fabsf(forwardSpeed) <= RESET_MOMENTUM_THRESHOLD && + fabsf(lateralSpeed) <= RESET_MOMENTUM_THRESHOLD) { + _isMovingWithMomentum = false; + } + + _animVars.set("isInputForward", false); _animVars.set("isInputBackward", false); _animVars.set("isInputRight", false); _animVars.set("isInputLeft", false); - _animVars.set("isNotInput", true); - _animVars.set("isNotInputSlow", true); + + // directly reflects input + _animVars.set("isNotInput", true); + + // no input + speed drops to SLOW_SPEED_THRESHOLD + // (don't transition run->idle - slow to walk first) + _animVars.set("isNotInputSlow", _isMovingWithMomentum); + + // no input + speed didn't get above HAS_MOMENTUM_THRESHOLD since last idle + // (brief inputs and movement adjustments) + _animVars.set("isNotInputNoMomentum", !_isMovingWithMomentum); + } else { _animVars.set("isInputForward", false); @@ -1456,8 +1514,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInputLeft", false); _animVars.set("isNotInput", true); _animVars.set("isNotInputSlow", false); + _animVars.set("isNotInputNoMomentum", false); } } else if (fabsf(_previousControllerParameters.inputZ) >= fabsf(_previousControllerParameters.inputX)) { + if (fabsf(forwardSpeed) > HAS_MOMENTUM_THRESHOLD) { + _isMovingWithMomentum = true; + } + if (_previousControllerParameters.inputZ > 0.0f) { // forward _animVars.set("isInputForward", true); @@ -1466,6 +1529,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInputLeft", false); _animVars.set("isNotInput", false); _animVars.set("isNotInputSlow", false); + _animVars.set("isNotInputNoMomentum", false); } else { // backward _animVars.set("isInputForward", false); @@ -1474,8 +1538,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInputLeft", false); _animVars.set("isNotInput", false); _animVars.set("isNotInputSlow", false); + _animVars.set("isNotInputNoMomentum", false); } } else { + if (fabsf(lateralSpeed) > HAS_MOMENTUM_THRESHOLD) { + _isMovingWithMomentum = true; + } + if (_previousControllerParameters.inputX > 0.0f) { // right if (!_headEnabled) { @@ -1489,6 +1558,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInputBackward", false); _animVars.set("isNotInput", false); _animVars.set("isNotInputSlow", false); + _animVars.set("isNotInputNoMomentum", false); } else { // left if (!_headEnabled) { @@ -1502,6 +1572,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isInputRight", false); _animVars.set("isNotInput", false); _animVars.set("isNotInputSlow", false); + _animVars.set("isNotInputNoMomentum", false); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index b2b9ecd5b4..60a2602316 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -330,6 +330,7 @@ protected: glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; + bool _isMovingWithMomentum{ false }; QUrl _animGraphURL; std::shared_ptr _animNode; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7494081c55..4e8c88560b 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -111,11 +111,17 @@ QList getAvailableDevices(QAudio::Mode mode, const QString& } if (defaultDesktopDevice.getDevice().isNull()) { - qCDebug(audioclient) << __FUNCTION__ << "Default device not found in list:" << defDeviceName - << "Setting Default to: " << devices.first().deviceName(); - defaultDesktopDevice = HifiAudioDeviceInfo(devices.first(), true, mode, HifiAudioDeviceInfo::desktop); + if (devices.size() > 0) { + qCDebug(audioclient) << __FUNCTION__ << "Default device not found in list:" << defDeviceName + << "Setting Default to: " << devices.first().deviceName(); + newDevices.push_front(HifiAudioDeviceInfo(devices.first(), true, mode, HifiAudioDeviceInfo::desktop)); + } else { + //current audio list is empty for some reason. + qCDebug(audioclient) << __FUNCTION__ << "Default device not found in list no alternative selection available"; + } + } else { + newDevices.push_front(defaultDesktopDevice); } - newDevices.push_front(defaultDesktopDevice); if (!hmdName.isNull()) { HifiAudioDeviceInfo hmdDevice; @@ -153,16 +159,20 @@ void AudioClient::checkDevices() { auto inputDevices = getAvailableDevices(QAudio::AudioInput, hmdInputName); auto outputDevices = getAvailableDevices(QAudio::AudioOutput, hmdOutputName); - Lock lock(_deviceMutex); - if (inputDevices != _inputDevices) { - _inputDevices.swap(inputDevices); - emit devicesChanged(QAudio::AudioInput, _inputDevices); - } + static const QMetaMethod devicesChangedSig= QMetaMethod::fromSignal(&AudioClient::devicesChanged); + //only emit once the scripting interface has connected to the signal + if (isSignalConnected(devicesChangedSig)) { + Lock lock(_deviceMutex); + if (inputDevices != _inputDevices) { + _inputDevices.swap(inputDevices); + emit devicesChanged(QAudio::AudioInput, _inputDevices); + } - if (outputDevices != _outputDevices) { - _outputDevices.swap(outputDevices); - emit devicesChanged(QAudio::AudioOutput, _outputDevices); - } + if (outputDevices != _outputDevices) { + _outputDevices.swap(outputDevices); + emit devicesChanged(QAudio::AudioOutput, _outputDevices); + } + } } HifiAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { @@ -325,9 +335,9 @@ AudioClient::AudioClient() { connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash - getAvailableDevices(QAudio::AudioInput, QString()); - getAvailableDevices(QAudio::AudioOutput, QString()); - + defaultAudioDeviceName(QAudio::AudioInput); + defaultAudioDeviceName(QAudio::AudioOutput); + // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; @@ -600,6 +610,14 @@ QString defaultAudioDeviceName(QAudio::Mode mode) { << " [" << deviceName << "] [" << "]"; #endif +#endif + +#ifdef Q_OS_LINUX + if ( mode == QAudio::AudioInput ) { + deviceName = QAudioDeviceInfo::defaultInputDevice().deviceName(); + } else { + deviceName = QAudioDeviceInfo::defaultOutputDevice().deviceName(); + } #endif return deviceName; } @@ -777,8 +795,11 @@ void AudioClient::start() { inputName = _hmdInputName; outputName = _hmdOutputName; } + + //initialize input to the dummy device to prevent starves + switchInputToAudioDevice(HifiAudioDeviceInfo()); + switchOutputToAudioDevice(defaultAudioDeviceForMode(QAudio::AudioOutput, QString())); - #if defined(Q_OS_ANDROID) connect(&_checkInputTimer, &QTimer::timeout, this, &AudioClient::checkInputTimeout); _checkInputTimer.start(CHECK_INPUT_READS_MSECS); @@ -1829,6 +1850,8 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice _audioInput->deleteLater(); _audioInput = NULL; _numInputCallbackBytes = 0; + + _inputDeviceInfo.setDevice(QAudioDeviceInfo()); } if (_dummyAudioInput) { @@ -2052,6 +2075,11 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi Lock localAudioLock(_localAudioMutex); _localSamplesAvailable.exchange(0, std::memory_order_release); + //wait on local injectors prep to finish running + if ( !_localPrepInjectorFuture.isFinished()) { + _localPrepInjectorFuture.waitForFinished(); + } + // cleanup any previously initialized device if (_audioOutput) { _audioOutputIODevice.close(); @@ -2075,6 +2103,8 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi delete[] _localOutputMixBuffer; _localOutputMixBuffer = NULL; + + _outputDeviceInfo.setDevice(QAudioDeviceInfo()); } // cleanup any resamplers @@ -2328,9 +2358,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, _localInjectorsStream.samplesAvailable(), samplesRequested); } } - + // prepare injectors for the next callback - QtConcurrent::run(QThreadPool::globalInstance(), [this] { + _audio->_localPrepInjectorFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { _audio->prepareLocalAudioInjectors(); }); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index e31b4789ce..09abb2c356 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -506,7 +507,8 @@ private: #endif AudioSolo _solo; - + + QFuture _localPrepInjectorFuture; QReadWriteLock _hmdNameLock; Mutex _checkDevicesMutex; QTimer* _checkDevicesTimer { nullptr }; diff --git a/libraries/audio-client/src/AudioIOStats.h b/libraries/audio-client/src/AudioIOStats.h index ffd7163586..9265ae3062 100644 --- a/libraries/audio-client/src/AudioIOStats.h +++ b/libraries/audio-client/src/AudioIOStats.h @@ -40,138 +40,183 @@ class AudioStreamStatsInterface : public QObject { Q_OBJECT /**jsdoc + * Statistics for an audio stream. + * + *

Provided in properties of the {@link AudioStats} API.

+ * * @class AudioStats.AudioStreamStats + * @hideconstructor * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {number} lossRate Read-only. - * @property {number} lossCount Read-only. - * @property {number} lossRateWindow Read-only. - * @property {number} lossCountWindow Read-only. - * @property {number} framesDesired Read-only. - * @property {number} framesAvailable Read-only. - * @property {number} framesAvailableAvg Read-only. - * @property {number} unplayedMsMax Read-only. - * @property {number} starveCount Read-only. - * @property {number} lastStarveDurationCount Read-only. - * @property {number} dropCount Read-only. - * @property {number} overflowCount Read-only. - * @property {number} timegapMsMax Read-only. - * @property {number} timegapMsAvg Read-only. - * @property {number} timegapMsMaxWindow Read-only. - * @property {number} timegapMsAvgWindow Read-only. + * @property {number} dropCount - The number of silent or old audio frames dropped. + * Read-only. + * @property {number} framesAvailable - The number of audio frames containing data available. + * Read-only. + * @property {number} framesAvailableAvg - The time-weighted average of audio frames containing data available. + * Read-only. + * @property {number} framesDesired - The desired number of audio frames for the jitter buffer. + * Read-only. + * @property {number} lastStarveDurationCount - The most recent number of consecutive times that audio frames have not been + * available for processing. + * Read-only. + * @property {number} lossCount - The total number of audio packets lost. + * Read-only. + * @property {number} lossCountWindow - The number of audio packets lost since the previous statistic. + * Read-only. + * @property {number} lossRate - The ratio of the total number of audio packets lost to the total number of audio packets + * expected. + * Read-only. + * @property {number} lossRateWindow - The ratio of the number of audio packets lost to the number of audio packets + * expected since the previous statistic. + * Read-only. + * @property {number} overflowCount - The number of times that the audio ring buffer has overflowed. + * Read-only. + * @property {number} starveCount - The total number of times that audio frames have not been available for processing. + * Read-only. + * @property {number} timegapMsAvg - The overall average time between data packets, in ms. + * Read-only. + * @property {number} timegapMsAvgWindow - The recent average time between data packets, in ms. + * Read-only. + * @property {number} timegapMsMax - The overall maximum time between data packets, in ms. + * Read-only. + * @property {number} timegapMsMaxWindow - The recent maximum time between data packets, in ms. + * Read-only. + * @property {number} unplayedMsMax - The duration of audio waiting to be played, in ms. + * Read-only. */ /**jsdoc + * Triggered when the ratio of the total number of audio packets lost to the total number of audio packets expected changes. * @function AudioStats.AudioStreamStats.lossRateChanged - * @param {number} lossRate + * @param {number} lossRate - The ratio of the total number of audio packets lost to the total number of audio packets + * expected. * @returns {Signal} */ AUDIO_PROPERTY(float, lossRate) /**jsdoc + * Triggered when the total number of audio packets lost changes. * @function AudioStats.AudioStreamStats.lossCountChanged - * @param {number} lossCount + * @param {number} lossCount - The total number of audio packets lost. * @returns {Signal} */ AUDIO_PROPERTY(float, lossCount) /**jsdoc + * Triggered when the ratio of the number of audio packets lost to the number of audio packets expected since the previous + * statistic changes. * @function AudioStats.AudioStreamStats.lossRateWindowChanged - * @param {number} lossRateWindow + * @param {number} lossRateWindow - The ratio of the number of audio packets lost to the number of audio packets expected + * since the previous statistic. * @returns {Signal} */ AUDIO_PROPERTY(float, lossRateWindow) /**jsdoc + * Triggered when the number of audio packets lost since the previous statistic changes. * @function AudioStats.AudioStreamStats.lossCountWindowChanged - * @param {number} lossCountWindow + * @param {number} lossCountWindow - The number of audio packets lost since the previous statistic. * @returns {Signal} */ AUDIO_PROPERTY(float, lossCountWindow) /**jsdoc + * Triggered when the desired number of audio frames for the jitter buffer changes. * @function AudioStats.AudioStreamStats.framesDesiredChanged - * @param {number} framesDesired + * @param {number} framesDesired - The desired number of audio frames for the jitter buffer. * @returns {Signal} */ AUDIO_PROPERTY(int, framesDesired) /**jsdoc + * Triggered when the number of audio frames containing data available changes. * @function AudioStats.AudioStreamStats.framesAvailableChanged - * @param {number} framesAvailable + * @param {number} framesAvailable - The number of audio frames containing data available. * @returns {Signal} */ AUDIO_PROPERTY(int, framesAvailable) /**jsdoc + * Triggered when the time-weighted average of audio frames containing data available changes. * @function AudioStats.AudioStreamStats.framesAvailableAvgChanged - * @param {number} framesAvailableAvg + * @param {number} framesAvailableAvg - The time-weighted average of audio frames containing data available. * @returns {Signal} */ AUDIO_PROPERTY(int, framesAvailableAvg) /**jsdoc + * Triggered when the duration of audio waiting to be played changes. * @function AudioStats.AudioStreamStats.unplayedMsMaxChanged - * @param {number} unplayedMsMax + * @param {number} unplayedMsMax - The duration of audio waiting to be played, in ms. * @returns {Signal} */ AUDIO_PROPERTY(float, unplayedMsMax) /**jsdoc + * Triggered when the total number of times that audio frames have not been available for processing changes. * @function AudioStats.AudioStreamStats.starveCountChanged - * @param {number} starveCount + * @param {number} starveCount - The total number of times that audio frames have not been available for processing. * @returns {Signal} */ AUDIO_PROPERTY(int, starveCount) /**jsdoc + * Triggered when the most recenbernumber of consecutive times that audio frames have not been available for processing + * changes. * @function AudioStats.AudioStreamStats.lastStarveDurationCountChanged - * @param {number} lastStarveDurationCount + * @param {number} lastStarveDurationCount - The most recent number of consecutive times that audio frames have not been + * available for processing. * @returns {Signal} */ AUDIO_PROPERTY(int, lastStarveDurationCount) /**jsdoc + * Triggered when the number of silent or old audio frames dropped changes. * @function AudioStats.AudioStreamStats.dropCountChanged - * @param {number} dropCount + * @param {number} dropCount - The number of silent or old audio frames dropped. * @returns {Signal} */ AUDIO_PROPERTY(int, dropCount) /**jsdoc + * Triggered when the number of times that the audio ring buffer has overflowed changes. * @function AudioStats.AudioStreamStats.overflowCountChanged - * @param {number} overflowCount + * @param {number} overflowCount - The number of times that the audio ring buffer has overflowed. * @returns {Signal} */ AUDIO_PROPERTY(int, overflowCount) /**jsdoc + * Triggered when the overall maximum time between data packets changes. * @function AudioStats.AudioStreamStats.timegapMsMaxChanged - * @param {number} timegapMsMax + * @param {number} timegapMsMax - The overall maximum time between data packets, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, timegapMsMax) /**jsdoc + * Triggered when the overall average time between data packets changes. * @function AudioStats.AudioStreamStats.timegapMsAvgChanged - * @param {number} timegapMsAvg + * @param {number} timegapMsAvg - The overall average time between data packets, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, timegapMsAvg) /**jsdoc + * Triggered when the recent maximum time between data packets changes. * @function AudioStats.AudioStreamStats.timegapMsMaxWindowChanged - * @param {number} timegapMsMaxWindow + * @param {number} timegapMsMaxWindow - The recent maximum time between data packets, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, timegapMsMaxWindow) /**jsdoc + * Triggered when the recent average time between data packets changes. * @function AudioStats.AudioStreamStats.timegapMsAvgWindowChanged - * @param {number} timegapMsAvgWindow + * @param {number} timegapMsAvgWindow - The recent average time between data packets, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, timegapMsAvgWindow) @@ -188,79 +233,106 @@ class AudioStatsInterface : public QObject { Q_OBJECT /**jsdoc - * Audio stats from the client. + * The AudioStats API provides statistics of the client and mixer audio. + * * @namespace AudioStats * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {number} pingMs Read-only. - * @property {number} inputReadMsMax Read-only. - * @property {number} inputUnplayedMsMax Read-only. - * @property {number} outputUnplayedMsMax Read-only. - * @property {number} sentTimegapMsMax Read-only. - * @property {number} sentTimegapMsAvg Read-only. - * @property {number} sentTimegapMsMaxWindow Read-only. - * @property {number} sentTimegapMsAvgWindow Read-only. - * @property {AudioStats.AudioStreamStats} clientStream Read-only. - * @property {AudioStats.AudioStreamStats} mixerStream Read-only. + * @property {AudioStats.AudioStreamStats} clientStream - Statistics of the client's audio stream. + * Read-only. + * @property {number} inputReadMsMax - The maximum duration of a block of audio data recently read from the microphone, in + * ms. + * Read-only. + * @property {number} inputUnplayedMsMax - The maximum duration of microphone audio recently in the input buffer waiting to + * be played, in ms. + * Read-only. + * @property {AudioStats.AudioStreamStats} mixerStream - Statistics of the audio mixer's stream. + * Read-only. + * @property {number} outputUnplayedMsMax - The maximum duration of output audio recently in the output buffer waiting to + * be played, in ms. + * Read-only. + * @property {number} pingMs - The current ping time to the audio mixer, in ms. + * Read-only. + * @property {number} sentTimegapMsAvg - The overall average time between sending data packets to the audio mixer, in ms. + * Read-only. + * @property {number} sentTimegapMsAvgWindow - The recent average time between sending data packets to the audio mixer, in + * ms. + * Read-only. + * @property {number} sentTimegapMsMax - The overall maximum time between sending data packets to the audio mixer, in ms. + * Read-only. + * @property {number} sentTimegapMsMaxWindow - The recent maximum time between sending data packets to the audio mixer, in + * ms. + * Read-only. */ /**jsdoc + * Triggered when the ping time to the audio mixer changes. * @function AudioStats.pingMsChanged - * @param {number} pingMs + * @param {number} pingMs - The ping time to the audio mixer, in ms. * @returns {Signal} */ AUDIO_PROPERTY(float, pingMs); /**jsdoc + * Triggered when the maximum duration of a block of audio data recently read from the microphone changes. * @function AudioStats.inputReadMsMaxChanged - * @param {number} inputReadMsMax + * @param {number} inputReadMsMax - The maximum duration of a block of audio data recently read from the microphone, in ms. * @returns {Signal} */ AUDIO_PROPERTY(float, inputReadMsMax); /**jsdoc + * Triggered when the maximum duration of microphone audio recently in the input buffer waiting to be played changes. * @function AudioStats.inputUnplayedMsMaxChanged - * @param {number} inputUnplayedMsMax + * @param {number} inputUnplayedMsMax - The maximum duration of microphone audio recently in the input buffer waiting to be + * played, in ms. * @returns {Signal} */ AUDIO_PROPERTY(float, inputUnplayedMsMax); /**jsdoc + * Triggered when the maximum duration of output audio recently in the output buffer waiting to be played changes. * @function AudioStats.outputUnplayedMsMaxChanged - * @param {number} outputUnplayedMsMax + * @param {number} outputUnplayedMsMax - The maximum duration of output audio recently in the output buffer waiting to be + * played, in ms. * @returns {Signal} */ AUDIO_PROPERTY(float, outputUnplayedMsMax); /**jsdoc + * Triggered when the overall maximum time between sending data packets to the audio mixer changes. * @function AudioStats.sentTimegapMsMaxChanged - * @param {number} sentTimegapMsMax + * @param {number} sentTimegapMsMax - The overall maximum time between sending data packets to the audio mixer, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, sentTimegapMsMax); /**jsdoc + * Triggered when the overall average time between sending data packets to the audio mixer changes. * @function AudioStats.sentTimegapMsAvgChanged - * @param {number} sentTimegapMsAvg + * @param {number} sentTimegapMsAvg - The overall average time between sending data packets to the audio mixer, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, sentTimegapMsAvg); /**jsdoc + * Triggered when the recent maximum time between sending data packets to the audio mixer changes. * @function AudioStats.sentTimegapMsMaxWindowChanged - * @param {number} sentTimegapMsMaxWindow + * @param {number} sentTimegapMsMaxWindow - The recent maximum time between sending data packets to the audio mixer, in ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, sentTimegapMsMaxWindow); /**jsdoc + * Triggered when the recent average time between sending data packets to the audio mixer changes. * @function AudioStats.sentTimegapMsAvgWindowChanged - * @param {number} sentTimegapMsAvgWindow + * @param {number} sentTimegapMsAvgWindow - The recent average time between sending data packets to the audio mixer, in + * ms. * @returns {Signal} */ AUDIO_PROPERTY(quint64, sentTimegapMsAvgWindow); @@ -287,18 +359,22 @@ public: signals: /**jsdoc + * Triggered when the mixer's stream statistics have been updated. * @function AudioStats.mixerStreamChanged * @returns {Signal} */ void mixerStreamChanged(); /**jsdoc + * Triggered when the client's stream statisticss have been updated. * @function AudioStats.clientStreamChanged * @returns {Signal} */ void clientStreamChanged(); /**jsdoc + * Triggered when the injector streams' statistics have been updated. + *

Note: The injector streams' statistics are currently not provided.

* @function AudioStats.injectorStreamsChanged * @returns {Signal} */ diff --git a/libraries/audio/src/AudioEffectOptions.h b/libraries/audio/src/AudioEffectOptions.h index e090832510..48fe44feb5 100644 --- a/libraries/audio/src/AudioEffectOptions.h +++ b/libraries/audio/src/AudioEffectOptions.h @@ -18,7 +18,7 @@ /**jsdoc * Audio effect options used by the {@link Audio} API. * - *

Create using new AudioEffectOptions(reverbOptions).

+ *

Create using new AudioEffectOptions(...).

* * @class AudioEffectOptions * @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - Reverberation options. diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index e5e32781b0..be7fecb450 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -25,13 +25,6 @@ #define ALIGN32 #endif -#ifndef MAX -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - // // Equal-gain crossfade // @@ -114,106 +107,49 @@ static const float nearFieldTable[NNEARFIELD][3] = { // { b0, b1, a1 } }; // -// Model the frequency-dependent attenuation of sound propogation in air. +// Parametric lowpass filter to model sound propogation or occlusion. // -// Fit using linear regression to a log-log model of lowpass cutoff frequency vs distance, -// loosely based on data from Handbook of Acoustics. Only the onset of significant -// attenuation is modelled, not the filter slope. +// lpf = 0.0 -> -3dB @ 50kHz +// lpf = 0.5 -> -3dB @ 1kHz +// lpf = 1.0 -> -3dB @ 20Hz // -// 1m -> -3dB @ 55kHz -// 10m -> -3dB @ 12kHz -// 100m -> -3dB @ 2.5kHz -// 1km -> -3dB @ 0.6kHz -// 10km -> -3dB @ 0.1kHz -// -static const int NLOWPASS = 64; -static const float lowpassTable[NLOWPASS][5] = { // { b0, b1, b2, a1, a2 } - // distance = 1 - { 0.999772371f, 1.399489756f, 0.454495527f, 1.399458985f, 0.454298669f }, - { 0.999631480f, 1.357609808f, 0.425210203f, 1.357549905f, 0.424901586f }, - { 0.999405154f, 1.311503050f, 0.394349994f, 1.311386830f, 0.393871368f }, - { 0.999042876f, 1.260674595f, 0.361869089f, 1.260450057f, 0.361136504f }, - // distance = 2 - { 0.998465222f, 1.204646525f, 0.327757118f, 1.204214978f, 0.326653886f }, - { 0.997548106f, 1.143019308f, 0.292064663f, 1.142195387f, 0.290436690f }, - { 0.996099269f, 1.075569152f, 0.254941286f, 1.074009405f, 0.252600301f }, - { 0.993824292f, 1.002389610f, 0.216688640f, 0.999469185f, 0.213433357f }, - // distance = 4 - { 0.990280170f, 0.924075266f, 0.177827150f, 0.918684864f, 0.173497723f }, - { 0.984818279f, 0.841917936f, 0.139164195f, 0.832151968f, 0.133748443f }, - { 0.976528670f, 0.758036513f, 0.101832398f, 0.740761682f, 0.095635899f }, - { 0.964216485f, 0.675305244f, 0.067243474f, 0.645654855f, 0.061110348f }, - // distance = 8 - { 0.946463038f, 0.596943020f, 0.036899688f, 0.547879974f, 0.032425772f }, - { 0.921823868f, 0.525770189f, 0.012060451f, 0.447952111f, 0.011702396f }, - { 0.890470015f, 0.463334299f, -0.001227816f, 0.347276405f, 0.005300092f }, - { 0.851335343f, 0.407521164f, -0.009353968f, 0.241900234f, 0.007602305f }, - // distance = 16 - { 0.804237360f, 0.358139558f, -0.014293332f, 0.130934213f, 0.017149373f }, - { 0.750073259f, 0.314581568f, -0.016625381f, 0.014505388f, 0.033524057f }, - { 0.690412072f, 0.275936128f, -0.017054561f, -0.106682490f, 0.055976129f }, - { 0.627245545f, 0.241342015f, -0.016246850f, -0.231302564f, 0.083643275f }, - // distance = 32 - { 0.562700627f, 0.210158533f, -0.014740899f, -0.357562697f, 0.115680957f }, - { 0.498787849f, 0.181982455f, -0.012925406f, -0.483461730f, 0.151306628f }, - { 0.437224055f, 0.156585449f, -0.011055180f, -0.607042210f, 0.189796534f }, - { 0.379336998f, 0.133834032f, -0.009281617f, -0.726580065f, 0.230469477f }, - // distance = 64 - { 0.326040627f, 0.113624970f, -0.007683443f, -0.840693542f, 0.272675696f }, - { 0.277861727f, 0.095845793f, -0.006291936f, -0.948380091f, 0.315795676f }, - { 0.234997480f, 0.080357656f, -0.005109519f, -1.049001190f, 0.359246807f }, - { 0.197386484f, 0.066993521f, -0.004122547f, -1.142236313f, 0.402493771f }, - // distance = 128 - { 0.164780457f, 0.055564709f, -0.003309645f, -1.228023442f, 0.445058962f }, - { 0.136808677f, 0.045870650f, -0.002646850f, -1.306498037f, 0.486530514f }, - { 0.113031290f, 0.037708627f, -0.002110591f, -1.377937457f, 0.526566783f }, - { 0.092980475f, 0.030881892f, -0.001679255f, -1.442713983f, 0.564897095f }, - // distance = 256 - { 0.076190239f, 0.025205585f, -0.001333863f, -1.501257246f, 0.601319206f }, - { 0.062216509f, 0.020510496f, -0.001058229f, -1.554025452f, 0.635694228f }, - { 0.050649464f, 0.016644994f, -0.000838826f, -1.601484205f, 0.667939837f }, - { 0.041120009f, 0.013475547f, -0.000664513f, -1.644091518f, 0.698022561f }, - // distance = 512 - { 0.033302044f, 0.010886252f, -0.000526217f, -1.682287704f, 0.725949783f }, - { 0.026911868f, 0.008777712f, -0.000416605f, -1.716488979f, 0.751761953f }, - { 0.021705773f, 0.007065551f, -0.000329788f, -1.747083800f, 0.775525335f }, - { 0.017476603f, 0.005678758f, -0.000261057f, -1.774431204f, 0.797325509f }, - // distance = 1024 - { 0.014049828f, 0.004558012f, -0.000206658f, -1.798860530f, 0.817261711f }, - { 0.011279504f, 0.003654067f, -0.000163610f, -1.820672082f, 0.835442043f }, - { 0.009044384f, 0.002926264f, -0.000129544f, -1.840138412f, 0.851979516f }, - { 0.007244289f, 0.002341194f, -0.000102586f, -1.857505967f, 0.866988864f }, - // distance = 2048 - { 0.005796846f, 0.001871515f, -0.000081250f, -1.872996926f, 0.880584038f }, - { 0.004634607f, 0.001494933f, -0.000064362f, -1.886811124f, 0.892876302f }, - { 0.003702543f, 0.001193324f, -0.000050993f, -1.899127955f, 0.903972829f }, - { 0.002955900f, 0.000951996f, -0.000040407f, -1.910108223f, 0.913975712f }, - // distance = 4096 - { 0.002358382f, 0.000759068f, -0.000032024f, -1.919895894f, 0.922981321f }, - { 0.001880626f, 0.000604950f, -0.000025383f, -1.928619738f, 0.931079931f }, - { 0.001498926f, 0.000481920f, -0.000020123f, -1.936394836f, 0.938355560f }, - { 0.001194182f, 0.000383767f, -0.000015954f, -1.943323983f, 0.944885977f }, - // distance = 8192 - { 0.000951028f, 0.000305502f, -0.000012651f, -1.949498943f, 0.950742822f }, - { 0.000757125f, 0.000243126f, -0.000010033f, -1.955001608f, 0.955991826f }, - { 0.000602572f, 0.000193434f, -0.000007957f, -1.959905036f, 0.960693085f }, - { 0.000479438f, 0.000153861f, -0.000006312f, -1.964274383f, 0.964901371f }, - // distance = 16384 - { 0.000381374f, 0.000122359f, -0.000005007f, -1.968167752f, 0.968666478f }, - { 0.000303302f, 0.000097288f, -0.000003972f, -1.971636944f, 0.972033562f }, - { 0.000241166f, 0.000077342f, -0.000003151f, -1.974728138f, 0.975043493f }, - { 0.000191726f, 0.000061475f, -0.000002500f, -1.977482493f, 0.977733194f }, - // distance = 32768 - { 0.000152399f, 0.000048857f, -0.000001984f, -1.979936697f, 0.980135969f }, - { 0.000121122f, 0.000038825f, -0.000001574f, -1.982123446f, 0.982281818f }, - { 0.000096252f, 0.000030849f, -0.000001249f, -1.984071877f, 0.984197728f }, - { 0.000076480f, 0.000024509f, -0.000000991f, -1.985807957f, 0.985907955f }, +static const int NLOWPASS = 32; +static const float lowpassTable[NLOWPASS+1][5] = { // { b0, b1, b2, a1, a2 } + { 0.9996582613f, 1.3644521648f, 0.4299107175f, 1.3643981990f, 0.4296229446f }, + { 0.9990601568f, 1.2627213717f, 0.3631477252f, 1.2625024258f, 0.3624268280f }, + { 0.9974547575f, 1.1378303854f, 0.2891398515f, 1.1369629374f, 0.2874620569f }, + { 0.9932384344f, 0.9872078424f, 0.2089943789f, 0.9839050501f, 0.2055356056f }, + { 0.9825457933f, 0.8153687744f, 0.1271135720f, 0.8036320348f, 0.1213961050f }, + { 0.9572356804f, 0.6404312275f, 0.0534129844f, 0.6033230637f, 0.0477568288f }, + { 0.9052878744f, 0.4902779401f, 0.0035032262f, 0.3924772681f, 0.0065917726f }, + { 0.8204774205f, 0.3736089028f, -0.0129974730f, 0.1678426876f, 0.0132461627f }, + { 0.7032096959f, 0.2836328681f, -0.0170877258f, -0.0810811878f, 0.0508360260f }, + { 0.5685067272f, 0.2128349296f, -0.0148937235f, -0.3461942779f, 0.1126422113f }, + { 0.4355093111f, 0.1558974062f, -0.0110025095f, -0.6105302595f, 0.1909344673f }, + { 0.3186188589f, 0.1108581568f, -0.0074653192f, -0.8569688248f, 0.2789805212f }, + { 0.2244962739f, 0.0766060095f, -0.0048289293f, -1.0745081373f, 0.3707814914f }, + { 0.1535044640f, 0.0516447640f, -0.0030384640f, -1.2590370066f, 0.4611477706f }, + { 0.1025113288f, 0.0341204303f, -0.0018818088f, -1.4113207964f, 0.5460707468f }, + { 0.0672016063f, 0.0221823522f, -0.0011552756f, -1.5347007285f, 0.6229294113f }, + { 0.0434202931f, 0.0142393067f, -0.0007060306f, -1.6334567973f, 0.6904103664f }, + { 0.0277383489f, 0.0090500025f, -0.0004305987f, -1.7118804671f, 0.7482382198f }, + { 0.0175636227f, 0.0057072537f, -0.0002624537f, -1.7738404438f, 0.7968488665f }, + { 0.0110441068f, 0.0035773504f, -0.0001599927f, -1.8226329785f, 0.8370944430f }, + { 0.0069069312f, 0.0022316608f, -0.0000975848f, -1.8609764152f, 0.8700174224f }, + { 0.0043012064f, 0.0013870046f, -0.0000595614f, -1.8910688315f, 0.8966974811f }, + { 0.0026696068f, 0.0008595333f, -0.0000363798f, -1.9146662133f, 0.9181589737f }, + { 0.0016526098f, 0.0005314445f, -0.0000222355f, -1.9331608518f, 0.9353226705f }, + { 0.0010209520f, 0.0003280036f, -0.0000135987f, -1.9476515008f, 0.9489868578f }, + { 0.0006297162f, 0.0002021591f, -0.0000083208f, -1.9590027292f, 0.9598262837f }, + { 0.0003879180f, 0.0001244611f, -0.0000050936f, -1.9678935939f, 0.9684008793f }, + { 0.0002387308f, 0.0000765601f, -0.0000031192f, -1.9748568416f, 0.9751690132f }, + { 0.0001468057f, 0.0000470631f, -0.0000019106f, -1.9803101382f, 0.9805020963f }, + { 0.0000902227f, 0.0000289155f, -0.0000011706f, -1.9845807858f, 0.9846987534f }, + { 0.0000554223f, 0.0000177584f, -0.0000007174f, -1.9879252038f, 0.9879976671f }, + { 0.0000340324f, 0.0000109027f, -0.0000004397f, -1.9905442465f, 0.9905887419f }, + { 0.0000208917f, 0.0000066920f, -0.0000002695f, -1.9925952275f, 0.9926225417f }, }; -static const float HALFPI = 1.570796327f; -static const float PI = 3.141592654f; -static const float TWOPI = 6.283185307f; - // // on x86 architecture, assume that SSE2 is present // @@ -811,44 +747,38 @@ static void splitf(float x, int& expn, float& frac) { expn = (bits.i >> IEEE754_MANT_BITS) - IEEE754_EXPN_BIAS; } -static void distanceBiquad(float distance, float& b0, float& b1, float& b2, float& a1, float& a2) { +static void lowpassBiquad(float lpf, float& b0, float& b1, float& b2, float& a1, float& a2) { // - // Computed from a lookup table quantized to distance = 2^(N/4) - // and reconstructed by piecewise linear interpolation. + // Computed from a lookup table and piecewise linear interpolation. // Approximation error < 0.25dB // + float x = lpf * NLOWPASS; - float x = distance; - x = MIN(x, 1<<30); - x *= x; - x *= x; // x = distance^4 - - // split x into e and frac, such that x = 2^(e+0) + frac * (2^(e+1) - 2^(e+0)) - int e; - float frac; - splitf(x, e, frac); + // split x into index and fraction + int i = (int)x; + float frac = x - (float)i; // clamp to table limits - if (e < 0) { - e = 0; + if (i < 0) { + i = 0; frac = 0.0f; } - if (e > NLOWPASS-2) { - e = NLOWPASS-2; + if (i > NLOWPASS-1) { + i = NLOWPASS-1; frac = 1.0f; } assert(frac >= 0.0f); assert(frac <= 1.0f); - assert(e+0 >= 0); - assert(e+1 < NLOWPASS); + assert(i+0 >= 0); + assert(i+1 <= NLOWPASS); // piecewise linear interpolation - b0 = lowpassTable[e+0][0] + frac * (lowpassTable[e+1][0] - lowpassTable[e+0][0]); - b1 = lowpassTable[e+0][1] + frac * (lowpassTable[e+1][1] - lowpassTable[e+0][1]); - b2 = lowpassTable[e+0][2] + frac * (lowpassTable[e+1][2] - lowpassTable[e+0][2]); - a1 = lowpassTable[e+0][3] + frac * (lowpassTable[e+1][3] - lowpassTable[e+0][3]); - a2 = lowpassTable[e+0][4] + frac * (lowpassTable[e+1][4] - lowpassTable[e+0][4]); + b0 = lowpassTable[i+0][0] + frac * (lowpassTable[i+1][0] - lowpassTable[i+0][0]); + b1 = lowpassTable[i+0][1] + frac * (lowpassTable[i+1][1] - lowpassTable[i+0][1]); + b2 = lowpassTable[i+0][2] + frac * (lowpassTable[i+1][2] - lowpassTable[i+0][2]); + a1 = lowpassTable[i+0][3] + frac * (lowpassTable[i+1][3] - lowpassTable[i+0][3]); + a2 = lowpassTable[i+0][4] + frac * (lowpassTable[i+1][4] - lowpassTable[i+0][4]); } // @@ -903,13 +833,13 @@ static void nearFieldGainCorrection(float azimuth, float distance, float& gainL, float d = (HRTF_NEARFIELD_MAX - distance) * (1.0f / (HRTF_NEARFIELD_MAX - HRTF_HEAD_RADIUS)); // angle of incidence at each ear - float angleL = azimuth + HALFPI; - float angleR = azimuth - HALFPI; + float angleL = azimuth + PI_OVER_TWO; + float angleR = azimuth - PI_OVER_TWO; if (angleL > +PI) { - angleL -= TWOPI; + angleL -= TWO_PI; } if (angleR < -PI) { - angleR += TWOPI; + angleR += TWO_PI; } assert(angleL >= -PI); assert(angleL <= +PI); @@ -968,7 +898,7 @@ static void nearFieldFilter(float gain, float& b0, float& b1, float& a1) { static void azimuthToIndex(float azimuth, int& index0, int& index1, float& frac) { // convert from radians to table units - azimuth *= (HRTF_AZIMUTHS / TWOPI); + azimuth *= (HRTF_AZIMUTHS / TWO_PI); if (azimuth < 0.0f) { azimuth += HRTF_AZIMUTHS; @@ -993,15 +923,15 @@ static void azimuthToIndex(float azimuth, int& index0, int& index1, float& frac) // compute new filters for a given azimuth, distance and gain static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int delay[4], - int index, float azimuth, float distance, float gain, int channel) { + int index, float azimuth, float distance, float gain, float lpf, int channel) { if (azimuth > PI) { - azimuth -= TWOPI; + azimuth -= TWO_PI; } assert(azimuth >= -PI); assert(azimuth <= +PI); - distance = MAX(distance, HRTF_NEARFIELD_MIN); + distance = std::max(distance, HRTF_NEARFIELD_MIN); // compute the azimuth correction at each ear float azimuthL = azimuth; @@ -1109,7 +1039,7 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela } else { - distanceBiquad(distance, b0, b1, b2, a1, a2); + lowpassBiquad(lpf, b0, b1, b2, a1, a2); bqCoef[0][channel+4] = b0; bqCoef[1][channel+4] = b1; @@ -1125,7 +1055,8 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela } } -void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames) { +void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames, + float lpfDistance) { assert(index >= 0); assert(index < HRTF_TABLES); @@ -1141,23 +1072,29 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, // apply global and local gain adjustment gain *= _gainAdjust; + // apply distance filter + float lpf = 0.5f * fastLog2f(std::max(distance, 1.0f)) / fastLog2f(std::max(lpfDistance, 2.0f)); + lpf = std::min(std::max(lpf, 0.0f), 1.0f); + // disable interpolation from reset state if (_resetState) { _azimuthState = azimuth; _distanceState = distance; _gainState = gain; + _lpfState = lpf; } // to avoid polluting the cache, old filters are recomputed instead of stored - setFilters(firCoef, bqCoef, delay, index, _azimuthState, _distanceState, _gainState, L0); + setFilters(firCoef, bqCoef, delay, index, _azimuthState, _distanceState, _gainState, _lpfState, L0); // compute new filters - setFilters(firCoef, bqCoef, delay, index, azimuth, distance, gain, L1); + setFilters(firCoef, bqCoef, delay, index, azimuth, distance, gain, lpf, L1); // new parameters become old _azimuthState = azimuth; _distanceState = distance; _gainState = gain; + _lpfState = lpf; // convert mono input to float for (int i = 0; i < HRTF_BLOCK; i++) { diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index 436d6318a5..6bbd9bc08a 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -14,6 +14,9 @@ #include #include +#include + +#include "AudioHelpers.h" static const int HRTF_AZIMUTHS = 72; // 360 / 5-degree steps static const int HRTF_TAPS = 64; // minimum-phase FIR coefficients @@ -34,6 +37,9 @@ static const float HRTF_HEAD_RADIUS = 0.0875f; // average human head in meters static const float ATTN_DISTANCE_REF = 2.0f; // distance where attn is 0dB static const float ATTN_GAIN_MAX = 16.0f; // max gain allowed by distance attn (+24dB) +// Distance filter +static const float LPF_DISTANCE_REF = 256.0f; // approximation of sound propogation in air + class AudioHRTF { public: @@ -47,8 +53,10 @@ public: // distance: source distance in meters // gain: gain factor for distance attenuation // numFrames: must be HRTF_BLOCK in this version + // lpfDistance: distance filter adjustment (distance to 1kHz lowpass in meters) // - void render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames); + void render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames, + float lpfDistance = LPF_DISTANCE_REF); // // Non-spatialized direct mix (accumulates into existing output) @@ -59,11 +67,14 @@ public: // // Fast path when input is known to be silent and state as been flushed // - void setParameterHistory(float azimuth, float distance, float gain) { + void setParameterHistory(float azimuth, float distance, float gain, float lpfDistance = LPF_DISTANCE_REF) { // new parameters become old _azimuthState = azimuth; _distanceState = distance; _gainState = gain; + + _lpfState = 0.5f * fastLog2f(std::max(distance, 1.0f)) / fastLog2f(std::max(lpfDistance, 2.0f)); + _lpfState = std::min(std::max(_lpfState, 0.0f), 1.0f); } // @@ -88,6 +99,7 @@ public: _azimuthState = 0.0f; _distanceState = 0.0f; _gainState = 0.0f; + _lpfState = 0.0f; // _gainAdjust is retained @@ -123,6 +135,7 @@ private: float _azimuthState = 0.0f; float _distanceState = 0.0f; float _gainState = 0.0f; + float _lpfState = 0.0f; // global and local gain adjustment float _gainAdjust = HRTF_GAIN; diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 62fdb9dcdc..205e1cba33 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -130,6 +130,7 @@ typedef QSharedPointer SharedSoundPointer; * * * @class SoundObject + * @hideconstructor * * @hifi-interface * @hifi-client-entity diff --git a/libraries/audio/src/SoundCacheScriptingInterface.h b/libraries/audio/src/SoundCacheScriptingInterface.h index ea767e00b4..28425f0406 100644 --- a/libraries/audio/src/SoundCacheScriptingInterface.h +++ b/libraries/audio/src/SoundCacheScriptingInterface.h @@ -39,6 +39,10 @@ class SoundCacheScriptingInterface : public ScriptableResourceCache, public Depe * @property {number} numCached - Total number of cached resource. Read-only. * @property {number} sizeTotal - Size in bytes of all resources. Read-only. * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers). + * Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers). + * Read-only. * * @borrows ResourceCache.getResourceList as getResourceList * @borrows ResourceCache.updateTotalSize as updateTotalSize diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp index 300a22983a..e00ae6dafc 100644 --- a/libraries/auto-updater/src/AutoUpdater.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include AutoUpdater::AutoUpdater() : @@ -36,24 +37,21 @@ void AutoUpdater::checkForUpdate() { this->getLatestVersionData(); } -const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); -const QUrl MASTER_BUILDS_XML_URL("https://highfidelity.com/dev-builds.xml"); - void AutoUpdater::getLatestVersionData() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl buildsURL; if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable) { - buildsURL = BUILDS_XML_URL; + buildsURL = NetworkingConstants::BUILDS_XML_URL; } else if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master) { - buildsURL = MASTER_BUILDS_XML_URL; + buildsURL = NetworkingConstants::MASTER_BUILDS_XML_URL; } QNetworkRequest latestVersionRequest(buildsURL); latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 868d49bb5c..16dc99ab49 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -333,18 +333,22 @@ void Avatar::setTargetScale(float targetScale) { } void Avatar::removeAvatarEntitiesFromTree() { + if (_packedAvatarEntityData.empty()) { + return; + } auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (entityTree) { - QList avatarEntityIDs; - _avatarEntitiesLock.withReadLock([&] { - avatarEntityIDs = _packedAvatarEntityData.keys(); - }); - entityTree->withWriteLock([&] { - for (const auto& entityID : avatarEntityIDs) { - entityTree->deleteEntity(entityID, true, true); - } - }); + std::vector ids; + ids.reserve(_packedAvatarEntityData.size()); + PackedAvatarEntityMap::const_iterator itr = _packedAvatarEntityData.constBegin(); + while (itr != _packedAvatarEntityData.constEnd()) { + ids.push_back(itr.key()); + ++itr; + } + bool force = true; + bool ignoreWarnings = true; + entityTree->deleteEntitiesByID(ids, force, ignoreWarnings); // locks tree } } @@ -1916,6 +1920,13 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { } } +/**jsdoc + * Information about a joint in an avatar's skeleton hierarchy. + * @typedef {object} SkeletonJoint + * @property {string} name - Joint name. + * @property {number} index - Joint index. + * @property {number} parentIndex - Index of this joint's parent (-1 if no parent). + */ QList Avatar::getSkeleton() { SkeletonModelPointer skeletonModel = _skeletonModel; if (skeletonModel) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 6e5f22f473..25dd347484 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -207,7 +207,7 @@ public: /**jsdoc * Gets the default rotation of a joint (in the current avatar) relative to its parent. *

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.getDefaultJointRotation * @param {number} index - The joint index. * @returns {Quat} The default rotation of the joint if the joint index is valid, otherwise {@link Quat(0)|Quat.IDENTITY}. @@ -218,7 +218,7 @@ public: * Gets the default translation of a joint (in the current avatar) relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.getDefaultJointTranslation * @param {number} index - The joint index. * @returns {Vec3} The default translation of the joint (in model coordinates) if the joint index is valid, otherwise @@ -442,7 +442,7 @@ public: void setOrientationViaScript(const glm::quat& orientation) override; /**jsdoc - * Gets the ID of the entity of avatar that the avatar is parented to. + * Gets the ID of the entity or avatar that the avatar is parented to. * @function MyAvatar.getParentID * @returns {Uuid} The ID of the entity or avatar that the avatar is parented to. {@link Uuid(0)|Uuid.NULL} if not parented. */ @@ -450,7 +450,7 @@ public: Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); } /**jsdoc - * Sets the ID of the entity of avatar that the avatar is parented to. + * Sets the ID of the entity or avatar that the avatar is parented to. * @function MyAvatar.setParentID * @param {Uuid} parentID - The ID of the entity or avatar that the avatar should be parented to. Set to * {@link Uuid(0)|Uuid.NULL} to unparent. @@ -480,14 +480,7 @@ public: /**jsdoc * Gets information on all the joints in the avatar's skeleton. * @function MyAvatar.getSkeleton - * @returns {MyAvatar.SkeletonJoint[]} Information about each joint in the avatar's skeleton. - */ - /**jsdoc - * Information about a single joint in an Avatar's skeleton hierarchy. - * @typedef {object} MyAvatar.SkeletonJoint - * @property {string} name - Joint name. - * @property {number} index - Joint index. - * @property {number} parentIndex - Index of this joint's parent (-1 if no parent). + * @returns {SkeletonJoint[]} Information about each joint in the avatar's skeleton. */ Q_INVOKABLE QList getSkeleton(); @@ -662,6 +655,7 @@ protected: std::vector> _attachmentsToDelete; float _bodyYawDelta { 0.0f }; // degrees/sec + float _seatedBodyYawDelta{ 0.0f }; // degrees/renderframe // These position histories and derivatives are in the world-frame. // The derivatives are the MEASURED results of all external and internal forces diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index b8bc7a03e8..ee0543fa6b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -96,17 +96,21 @@ void Head::simulate(float deltaTime) { const float BLINK_START_VARIABILITY = 0.25f; const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; + const float TALKING_LOUDNESS = 150.0f; + + _timeWithoutTalking += deltaTime; + if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) { + _timeWithoutTalking = 0.0f; + } + if (getProceduralAnimationFlag(HeadData::BlinkProceduralBlendshapeAnimation) && !getSuppressProceduralAnimationFlag(HeadData::BlinkProceduralBlendshapeAnimation)) { + // handle automatic blinks // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; - const float TALKING_LOUDNESS = 150.0f; const float BLINK_AFTER_TALKING = 0.25f; - _timeWithoutTalking += deltaTime; - if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) { - _timeWithoutTalking = 0.0f; - } else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) { + if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) { forceBlink = true; } if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { @@ -150,11 +154,13 @@ void Head::simulate(float deltaTime) { } else { _rightEyeBlink = FULLY_OPEN; _leftEyeBlink = FULLY_OPEN; + updateEyeLookAt(); } // use data to update fake Faceshift blendshape coefficients if (getProceduralAnimationFlag(HeadData::AudioProceduralBlendshapeAnimation) && !getSuppressProceduralAnimationFlag(HeadData::AudioProceduralBlendshapeAnimation)) { + // Update audio attack data for facial animation (eyebrows and mouth) float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz _audioAttack = audioAttackAveragingRate * _audioAttack + @@ -188,6 +194,7 @@ void Head::simulate(float deltaTime) { if (getProceduralAnimationFlag(HeadData::LidAdjustmentProceduralBlendshapeAnimation) && !getSuppressProceduralAnimationFlag(HeadData::LidAdjustmentProceduralBlendshapeAnimation)) { + // This controls two things, the eye brow and the upper eye lid, it is driven by the vertical up/down angle of the // eyes relative to the head. This is to try to help prevent sleepy eyes/crazy eyes. applyEyelidOffset(getOrientation()); diff --git a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h index 3bc98e72a0..0b89e9d59e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h @@ -16,6 +16,64 @@ #include "Avatar.h" +/**jsdoc + * Information about an avatar. + * + *

Create using {@link MyAvatar.getTargetAvatar} or {@link AvatarList.getAvatar}.

+ * + * @class ScriptAvatar + * @hideconstructor + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-assignment-client + * @hifi-server-entity + * + * @property {Vec3} position - The avatar's position. + * @property {number} scale - The target scale of the avatar without any restrictions on permissible values imposed by the + * domain. + * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar but + * is otherwise not used or changed by Interface. + * @property {number} bodyPitch - The pitch of the avatar's body, in degrees. + * @property {number} bodyYaw - The yaw of the avatar's body, in degrees. + * @property {number} bodyRoll - The roll of the avatar's body, in degrees. + * @property {Quat} orientation - The orientation of the avatar's body. + * @property {Quat} headOrientation - The orientation of the avatar's head. + * @property {number} headPitch - The pitch of the avatar's head relative to the body, in degrees. + * @property {number} headYaw - The yaw of the avatar's head relative to the body, in degrees. + * @property {number} headRoll - The roll of the avatar's head relative to the body, in degrees. + * + * @property {Vec3} velocity - The linear velocity of the avatar. + * @property {Vec3} angularVelocity - The angular velocity of the avatar. + * + * @property {Uuid} sessionUUID - The avatar's session ID. + * @property {string} displayName - The avatar's display name. + * @property {string} sessionDisplayName - The avatar's display name, sanitized and versioned, as defined by the avatar mixer. + * It is unique among all avatars present in the domain at the time. + * @property {boolean} isReplicated - Deprecated: This property is deprecated and will be + * removed. + * @property {boolean} lookAtSnappingEnabled - true if the avatar's eyes snap to look at another avatar's eyes + * when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. + * + * @property {string} skeletonModelURL - The avatar's FST file. + * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. + *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

+ * @property {string[]} jointNames - The list of joints in the avatar model. + * + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * domain. + * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting into + * the domain. + * + * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the + * avatar's size, orientation, and position in the virtual world. + * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the avatar. + * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the + * avatar. + * + * @property {Vec3} skeletonOffset - The rendering offset of the avatar. + */ class ScriptAvatar : public ScriptAvatarData { Q_OBJECT @@ -26,27 +84,138 @@ public: public slots: + /**jsdoc + * Gets the default rotation of a joint in the avatar relative to its parent. + *

For information on the joint hierarchy used, see + * Avatar Standards.

+ * @function ScriptAvatar.getDefaultJointRotation + * @param {number} index - The joint index. + * @returns {Quat} The default rotation of the joint if avatar data are available and the joint index is valid, otherwise + * {@link Quat(0)|Quat.IDENTITY}. + */ glm::quat getDefaultJointRotation(int index) const; + + /**jsdoc + * Gets the default translation of a joint in the avatar relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

For information on the joint hierarchy used, see + * Avatar Standards.

+ * @function ScriptAvatar.getDefaultJointTranslation + * @param {number} index - The joint index. + * @returns {Vec3} The default translation of the joint (in model coordinates) if avatar data are available and the joint + * index is valid, otherwise {@link Vec3(0)|Vec3.ZERO}. + */ glm::vec3 getDefaultJointTranslation(int index) const; + + /**jsdoc + * Gets the offset applied to the avatar for rendering. + * @function ScriptAvatar.getSkeletonOffset + * @returns {Vec3} The skeleton offset if avatar data are available, otherwise {@link Vec3(0)|Vec3.ZERO}. + */ glm::vec3 getSkeletonOffset() const; + + /**jsdoc + * Gets the position of a joint in the avatar. + * @function ScriptAvatar.getJointPosition + * @param {number} index - The index of the joint. + * @returns {Vec3} The position of the joint in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't + * available. + */ glm::vec3 getJointPosition(int index) const; + + /**jsdoc + * Gets the position of a joint in the current avatar. + * @function ScriptAvatar.getJointPosition + * @param {string} name - The name of the joint. + * @returns {Vec3} The position of the joint in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't + * available. + */ glm::vec3 getJointPosition(const QString& name) const; + + /**jsdoc + * Gets the position of the current avatar's neck in world coordinates. + * @function ScriptAvatar.getNeckPosition + * @returns {Vec3} The position of the neck in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't + * available. + */ glm::vec3 getNeckPosition() const; + + /**jsdoc + * Gets the current acceleration of the avatar. + * @function ScriptAvatar.getAcceleration + * @returns {Vec3} The current acceleration of the avatar, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't available.. + */ glm::vec3 getAcceleration() const; + + /**jsdoc + * Gets the ID of the entity or avatar that the avatar is parented to. + * @function ScriptAvatar.getParentID + * @returns {Uuid} The ID of the entity or avatar that the avatar is parented to. {@link Uuid(0)|Uuid.NULL} if not parented + * or avatar data aren't available. + */ QUuid getParentID() const; + + /**jsdoc + * Gets the joint of the entity or avatar that the avatar is parented to. + * @function ScriptAvatar.getParentJointIndex + * @returns {number} The joint of the entity or avatar that the avatar is parented to. 65535 or + * -1 if parented to the entity or avatar's position and orientation rather than a joint, or avatar data + * aren't available. + */ quint16 getParentJointIndex() const; + + /**jsdoc + * Gets information on all the joints in the avatar's skeleton. + * @function ScriptAvatar.getSkeleton + * @returns {SkeletonJoint[]} Information about each joint in the avatar's skeleton. + */ QVariantList getSkeleton() const; + + /**jsdoc + * @function ScriptAvatar.getSimulationRate + * @param {AvatarSimulationRate} [rateName=""] - Rate name. + * @returns {number} Simulation rate in Hz, or 0.0 if avatar data aren't available. + * @deprecated This function is deprecated and will be removed. + */ float getSimulationRate(const QString& rateName = QString("")) const; + + /**jsdoc + * Gets the position of the left palm in world coordinates. + * @function ScriptAvatar.getLeftPalmPosition + * @returns {Vec3} The position of the left palm in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't + * available. + */ glm::vec3 getLeftPalmPosition() const; + + /**jsdoc + * Gets the rotation of the left palm in world coordinates. + * @function ScriptAvatar.getLeftPalmRotation + * @returns {Quat} The rotation of the left palm in world coordinates, or {@link Quat(0)|Quat.IDENTITY} if the avatar data + * aren't available. + */ glm::quat getLeftPalmRotation() const; + + /**jsdoc + * Gets the position of the right palm in world coordinates. + * @function ScriptAvatar.getLeftPalmPosition + * @returns {Vec3} The position of the right palm in world coordinates, or {@link Vec3(0)|Vec3.ZERO} if avatar data aren't + * available. + */ glm::vec3 getRightPalmPosition() const; + + /**jsdoc + * Gets the rotation of the right palm in world coordinates. + * @function ScriptAvatar.getLeftPalmRotation + * @returns {Quat} The rotation of the right palm in world coordinates, or {@link Quat(0)|Quat.IDENTITY} if the avatar data + * aren't available. + */ glm::quat getRightPalmRotation() const; private: diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 93850197af..adb7222ee3 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -376,7 +376,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent tranlationChangedSince(lastSentTime) || parentInfoChangedSince(lastSentTime)); hasHandControllers = _controllerLeftHandMatrixCache.isValid() || _controllerRightHandMatrixCache.isValid(); - hasFaceTrackerInfo = !dropFaceTracking && getHasScriptedBlendshapes() && + hasFaceTrackerInfo = !dropFaceTracking && (getHasScriptedBlendshapes() || _headData->_hasInputDrivenBlendshapes) && (sendAll || faceTrackerInfoChangedSince(lastSentTime)); hasJointData = !sendMinimum; hasJointDefaultPoseFlags = hasJointData; @@ -3033,15 +3033,12 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) { - + // NOTE: requiresRemovalFromTree is unused bool removedEntity = false; - _avatarEntitiesLock.withWriteLock([this, &removedEntity, &entityID] { removedEntity = _packedAvatarEntityData.remove(entityID); }); - insertRemovedEntityID(entityID); - if (removedEntity && _clientTraitsHandler) { // we have a client traits handler, so we need to mark this removed instance trait as deleted // so that changes are sent next frame @@ -3055,6 +3052,12 @@ AvatarEntityMap AvatarData::getAvatarEntityData() const { return AvatarEntityMap(); } +AvatarEntityMap AvatarData::getAvatarEntityDataNonDefault() const { + // overridden where needed + // NOTE: the return value is expected to be a map of unfortunately-formatted-binary-blobs + return AvatarEntityMap(); +} + void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { // overridden where needed // avatarEntityData is expected to be a map of QByteArrays diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index cc2dd0b7da..2e25c9559c 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -532,19 +532,22 @@ class AvatarData : public QObject, public SpatiallyNestable { * size in the virtual world. Read-only. * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. * Read-only. - * @property {boolean} hasScriptedBlendshapes=false - Set this to true before using the {@link MyAvatar.setBlendshape} method, - * after you no longer want scripted control over the blendshapes set to back to false.
NOTE: this property will - * automatically become true if the Controller system has valid facial blendshape actions. - * @property {boolean} hasProceduralBlinkFaceMovement=true - By default avatars will blink automatically by animating facial - * blendshapes. Set this property to false to disable this automatic blinking. This can be useful if you - * wish to fully control the blink facial blendshapes via the {@link MyAvatar.setBlendshape} method. - * @property {boolean} hasProceduralEyeFaceMovement=true - By default the avatar eye facial blendshapes will be adjusted - * automatically as the eyes move. This will prevent the iris is never obscured by the upper or lower lids. Set this - * property to false to disable this automatic movement. This can be useful if you wish to fully control - * the eye blendshapes via the {@link MyAvatar.setBlendshape} method. - * @property {boolean} hasAudioEnabledFaceMovement=true - By default the avatar mouth blendshapes will animate based on - * the microphone audio. Set this property to false to disable that animaiton. This can be useful if you - * wish to fully control the blink facial blendshapes via the {@link MyAvatar.setBlendshape} method. + * @property {boolean} hasScriptedBlendshapes=false - true if blend shapes are controlled by scripted actions, + * otherwise false. Set this to true before using the {@link Avatar.setBlendshape} method, + * and set back to false after you no longer want scripted control over the blend shapes. + *

Note: This property will automatically be set to true if the controller system has + * valid facial blend shape actions.

+ * @property {boolean} hasProceduralBlinkFaceMovement=true - true if avatars blink automatically by animating + * facial blend shapes, false if automatic blinking is disabled. Set to false to fully control + * the blink facial blend shapes via the {@link Avatar.setBlendshape} method. + * @property {boolean} hasProceduralEyeFaceMovement=true - true if the facial blend shapes for an avatar's eyes + * adjust automatically as the eyes move, false if this automatic movement is disabled. Set this property + * to true to prevent the iris from being obscured by the upper or lower lids. Set to false to + * fully control the eye blend shapes via the {@link Avatar.setBlendshape} method. + * @property {boolean} hasAudioEnabledFaceMovement=true - true if the avatar's mouth blend shapes animate + * automatically based on detected microphone input, false if this automatic movement is disabled. Set + * this property to false to fully control the mouth facial blend shapes via the + * {@link Avatar.setBlendshape} method. */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) @@ -793,7 +796,7 @@ public: * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example Set your avatar to it's default T-pose for a while.
- * Avatar in T-pose + * Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -857,7 +860,7 @@ public: /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -868,7 +871,7 @@ public: * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. @@ -901,7 +904,7 @@ public: * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @example Set your avatar to its default T-pose then rotate its right arm.
- * Avatar in T-pose with arm rotated + * Avatar in T-pose with arm rotated * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -936,7 +939,7 @@ public: * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between * the head and body or you will see the neck stretched.
- * Avatar with neck stretched + * Avatar with neck stretched * // Stretch your avatar's neck. * MyAvatar.setJointTranslation("Neck", Vec3.multiply(2, MyAvatar.getJointTranslation("Neck"))); * @@ -978,7 +981,7 @@ public: /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {string} name - The name of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -993,7 +996,7 @@ public: * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} name - The name of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. @@ -1038,7 +1041,7 @@ public: * @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the * array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @example Set your avatar to its default T-pose then rotate its right arm.
- * Avatar in T-pose + * Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -1130,12 +1133,12 @@ public: /**jsdoc - * Sets the value of a blendshape to animate your avatar's face. To enable other users to see the resulting animation of - * your avatar's face, set {@link Avatar.hasScriptedBlendshapes} to true while using this API and back to false when your - * animation is complete. + * Sets the value of a blend shape to animate your avatar's face. In order for other users to see the resulting animations + * on your avatar's face, set hasScriptedBlendshapes to true. When you are done using this API, + * set hasScriptedBlendshapes back to false when the animation is complete. * @function Avatar.setBlendshape * @param {string} name - The name of the blendshape, per the - * {@link https://docs.projectathena.dev/create/avatars/avatar-standards.html#blendshapes Avatar Standards}. + * {@link https://docs.vircadia.dev/create/avatars/avatar-standards.html#blendshapes Avatar Standards}. * @param {number} value - A value between 0.0 and 1.0. * @example Open your avatar's mouth wide. * MyAvatar.hasScriptedBlendshapes = true; @@ -1171,7 +1174,7 @@ public: /**jsdoc * @function Avatar.updateAvatarEntity * @param {Uuid} entityID - The entity ID. - * @param {Array.} entityData - Entity data. + * @param {ArrayBuffer} entityData - Entity data. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE virtual void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); @@ -1179,18 +1182,18 @@ public: /**jsdoc * @function Avatar.clearAvatarEntity * @param {Uuid} entityID - The entity ID. - * @param {boolean} [requiresRemovalFromTree=true] - Requires removal from tree. + * @param {boolean} [requiresRemovalFromTree=true] - unused * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE virtual void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); /**jsdoc - *

Deprecated: This method is deprecated and will be removed.

- * Use Avatar.hasScriptedBlendshapes property instead. - * Enables blendshapes set using {@link Avatar.setBlendshape} or {@link MyAvatar.setBlendshape} to be transmitted to other + * Enables blend shapes set using {@link Avatar.setBlendshape} or {@link MyAvatar.setBlendshape} to be transmitted to other * users so that they can see the animation of your avatar's face. + *

Deprecated: This method is deprecated and will be removed. Use the + * Avatar.hasScriptedBlendshapes or MyAvatar.hasScriptedBlendshapes property instead.

* @function Avatar.setForceFaceTrackerConnected - * @param {boolean} connected - true to enable blendshape changes to be transmitted to other users, + * @param {boolean} connected - true to enable blend shape changes to be transmitted to other users, * false to disable. */ Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { setHasScriptedBlendshapes(connected); } @@ -1271,7 +1274,7 @@ public: * null to remove all attachments. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. * @example Remove a hat attachment if your avatar is wearing it. - * var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx"; + * var hatURL = "https://apidocs.vircadia.dev/models/cowboy-hat.fbx"; * var attachments = MyAvatar.getAttachmentData(); * * for (var i = 0; i < attachments.length; i++) { @@ -1308,7 +1311,7 @@ public: * @deprecated This function is deprecated and will be removed. Use avatar entities instead. * @example Attach a cowboy hat to your avatar's head. * var attachment = { - * modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx", + * modelURL: "https://apidocs.vircadia.dev/models/cowboy-hat.fbx", * jointName: "Head", * translation: {"x": 0, "y": 0.25, "z": 0}, * rotation: {"x": 0, "y": 0, "z": 0, "w": 1}, @@ -1387,7 +1390,11 @@ public: /**jsdoc * @comment Documented in derived classes' JSDoc because implementations are different. */ - Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const; + // Get avatar entity data with all property values. Used in API. + Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const; + + // Get avatar entity data with non-default property values. Used internally. + virtual AvatarEntityMap getAvatarEntityDataNonDefault() const; /**jsdoc * @comment Documented in derived classes' JSDoc because implementations are different. @@ -1535,7 +1542,7 @@ signals: void sessionDisplayNameChanged(); /**jsdoc - * Triggered when the avatar's model (i.e., skeletonModelURL property value) is changed. + * Triggered when the avatar's model (i.e., skeletonModelURL property value) changes. * @function Avatar.skeletonModelURLChanged * @returns {Signal} * @example Report when your avatar's skeleton model changes. @@ -1957,6 +1964,7 @@ Q_DECLARE_METATYPE(RayToAvatarIntersectionResult) QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results); void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results); +// No JSDoc because it's not provided as a type to the script engine. class ParabolaToAvatarIntersectionResult { public: bool intersects { false }; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index c474353451..e12e5b4649 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -111,7 +111,7 @@ public: * Gets information about an avatar. * @function AvatarList.getAvatar * @param {Uuid} avatarID - The ID of the avatar. - * @returns {AvatarData} Information about the avatar. + * @returns {ScriptAvatar} Information about the avatar. */ // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 51f843b83c..e133f178df 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -171,8 +171,6 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m return; } - // AJT: DON'T CHECK THIS IN, disable model URL overrides. - /* // only accept an override if this is for a trait type we override // and the version matches what we last sent for skeleton if (traitType == AvatarTraits::SkeletonModelURL @@ -194,7 +192,6 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m } else { message->seek(message->getPosition() + traitBinarySize); } - */ } } } diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 5e8d5c457f..392b08d48c 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -34,7 +34,6 @@ HeadData::HeadData(AvatarData* owningAvatar) : { _userProceduralAnimationFlags.assign((size_t)ProceduralAnimaitonTypeCount, true); _suppressProceduralAnimationFlags.assign((size_t)ProceduralAnimaitonTypeCount, false); - computeBlendshapesLookupMap(); } glm::quat HeadData::getRawOrientation() const { @@ -72,12 +71,6 @@ void HeadData::setOrientation(const glm::quat& orientation) { setHeadOrientation(orientation); } -void HeadData::computeBlendshapesLookupMap(){ - for (int i = 0; i < (int)Blendshapes::BlendshapeCount; i++) { - _blendshapeLookupMap[FACESHIFT_BLENDSHAPES[i]] = i; - } -} - int HeadData::getNumSummedBlendshapeCoefficients() const { int maxSize = std::max(_blendshapeCoefficients.size(), _transientBlendshapeCoefficients.size()); return maxSize; @@ -109,8 +102,8 @@ const QVector& HeadData::getSummedBlendshapeCoefficients() { void HeadData::setBlendshape(QString name, float val) { // Check to see if the named blendshape exists, and then set its value if it does - auto it = _blendshapeLookupMap.find(name); - if (it != _blendshapeLookupMap.end()) { + auto it = BLENDSHAPE_LOOKUP_MAP.find(name); + if (it != BLENDSHAPE_LOOKUP_MAP.end()) { if (_blendshapeCoefficients.size() <= it.value()) { _blendshapeCoefficients.resize(it.value() + 1); } @@ -135,8 +128,8 @@ void HeadData::setBlendshape(QString name, float val) { } int HeadData::getBlendshapeIndex(const QString& name) { - auto it = _blendshapeLookupMap.find(name); - int index = it != _blendshapeLookupMap.end() ? it.value() : -1; + auto it = BLENDSHAPE_LOOKUP_MAP.find(name); + int index = it != BLENDSHAPE_LOOKUP_MAP.end() ? it.value() : -1; return index; } @@ -155,8 +148,8 @@ static const QString JSON_AVATAR_HEAD_LOOKAT = QStringLiteral("lookAt"); QJsonObject HeadData::toJson() const { QJsonObject headJson; QJsonObject blendshapesJson; - for (auto name : _blendshapeLookupMap.keys()) { - auto index = _blendshapeLookupMap[name]; + for (auto name : BLENDSHAPE_LOOKUP_MAP.keys()) { + auto index = BLENDSHAPE_LOOKUP_MAP[name]; float value = 0.0f; if (index < _blendshapeCoefficients.size()) { value += _blendshapeCoefficients[index]; diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 2fa91c0bed..af71fd883d 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -125,7 +125,6 @@ protected: QVector _blendshapeCoefficients; QVector _transientBlendshapeCoefficients; QVector _summedBlendshapeCoefficients; - QMap _blendshapeLookupMap; AvatarData* _owningAvatar; private: @@ -134,7 +133,6 @@ private: HeadData& operator= (const HeadData&); void setHeadOrientation(const glm::quat& orientation); - void computeBlendshapesLookupMap(); }; #endif // hifi_HeadData_h diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 18717c8ca3..a67af18c40 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -278,7 +278,7 @@ AvatarEntityMap ScriptAvatarData::getAvatarEntities() const { AvatarEntityMap scriptEntityData; if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { - return sharedAvatarData->getAvatarEntityData(); + return sharedAvatarData->getAvatarEntityDataNonDefault(); } return scriptEntityData; diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 9c5c2c6918..290e8c178a 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -16,53 +16,6 @@ #include "AvatarData.h" -/**jsdoc - * Information about an avatar. - * @typedef {object} AvatarData - * @property {Vec3} position - The avatar's position. - * @property {number} scale - The target scale of the avatar without any restrictions on permissible values imposed by the - * domain. - * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar but - * is otherwise not used or changed by Interface. - * @property {number} bodyPitch - The pitch of the avatar's body, in degrees. - * @property {number} bodyYaw - The yaw of the avatar's body, in degrees. - * @property {number} bodyRoll - The roll of the avatar's body, in degrees. - * @property {Quat} orientation - The orientation of the avatar's body. - * @property {Quat} headOrientation - The orientation of the avatar's head. - * @property {number} headPitch - The pitch of the avatar's head relative to the body, in degrees. - * @property {number} headYaw - The yaw of the avatar's head relative to the body, in degrees. - * @property {number} headRoll - The roll of the avatar's head relative to the body, in degrees. - * - * @property {Vec3} velocity - The linear velocity of the avatar. - * @property {Vec3} angularVelocity - The angular velocity of the avatar. - * - * @property {Uuid} sessionUUID - The avatar's session ID. - * @property {string} displayName - The avatar's display name. - * @property {string} sessionDisplayName - The avatar's display name, sanitized and versioned, as defined by the avatar mixer. - * It is unique among all avatars present in the domain at the time. - * @property {boolean} isReplicated - Deprecated: This property is deprecated and will be - * removed. - * @property {boolean} lookAtSnappingEnabled - true if the avatar's eyes snap to look at another avatar's eyes - * when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. - * - * @property {string} skeletonModelURL - The avatar's FST file. - * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. - *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

- * @property {string[]} jointNames - The list of joints in the current avatar model. - * - * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the - * domain. - * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting into - * the domain. - * - * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the - * avatar's size, orientation, and position in the virtual world. - * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the avatar. - * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the - * avatar. - * - * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. - */ class ScriptAvatarData : public QObject { Q_OBJECT @@ -153,16 +106,110 @@ public: // ATTACHMENT AND JOINT PROPERTIES // QString getSkeletonModelURLFromScript() const; + + /**jsdoc + * Gets the pointing state of the hands to control where the laser emanates from. If the right index finger is pointing, the + * laser emanates from the tip of that finger, otherwise it emanates from the palm. + * @function ScriptAvatar.getHandState + * @returns {HandState|number} The pointing state of the hand, or -1 if the avatar data aren't available. + */ Q_INVOKABLE char getHandState() const; + + /**jsdoc + * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see + * Avatar Standards. + * @function ScriptAvatar.getJointRotation + * @param {number} index - The index of the joint. + * @returns {Quat} The rotation of the joint relative to its parent, or {@link Quat(0)|Quat.IDENTITY} if the avatar data + * aren't available. + */ Q_INVOKABLE glm::quat getJointRotation(int index) const; + + /**jsdoc + * Gets the translation of a joint relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

For information on the joint hierarchy used, see + * Avatar Standards.

+ * @function ScriptAvatar.getJointTranslation + * @param {number} index - The index of the joint. + * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates, or {@link Vec3(0)|Vec3.ZERO} + * if the avatar data aren't available. + */ Q_INVOKABLE glm::vec3 getJointTranslation(int index) const; + + /**jsdoc + * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see + * Avatar Standards. + * @function ScriptAvatar.getJointRotation + * @param {string} name - The name of the joint. + * @returns {Quat} The rotation of the joint relative to its parent, or {@link Quat(0)|Quat.IDENTITY} if the avatar data + * aren't available. + */ Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; + + /**jsdoc + * Gets the translation of a joint relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

For information on the joint hierarchy used, see + * Avatar Standards.

+ * @function ScriptAvatar.getJointTranslation + * @param {number} name - The name of the joint. + * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates, or {@link Vec3(0)|Vec3.ZERO} + * if the avatar data aren't available. + */ Q_INVOKABLE glm::vec3 getJointTranslation(const QString& name) const; + + /**jsdoc + * Gets the rotations of all joints in the avatar. Each joint's rotation is relative to its parent joint. + * @function ScriptAvatar.getJointRotations + * @returns {Quat[]} The rotations of all joints relative to each's parent, or [] if the avatar data aren't + * available. The values are in the same order as the array returned by {@link ScriptAvatar.getJointNames}. + */ Q_INVOKABLE QVector getJointRotations() const; + + /**jsdoc + * Gets the translations of all joints in the avatar. Each joint's translation is relative to its parent joint, in + * model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ * @function ScriptAvatar.getJointTranslations + * @returns {Vec3[]} The translations of all joints relative to each's parent, in model coordinates, or [] if + * the avatar data aren't available. The values are in the same order as the array returned by + * {@link ScriptAvatar.getJointNames}. + */ Q_INVOKABLE QVector getJointTranslations() const; + + /**jsdoc + * Checks that the data for a joint are valid. + * @function ScriptAvatar.isJointDataValid + * @param {number} index - The index of the joint. + * @returns {boolean} true if the joint data are valid, false if not or the avatar data aren't + * available. + */ Q_INVOKABLE bool isJointDataValid(const QString& name) const; + + /**jsdoc + * Gets the joint index for a named joint. The joint index value is the position of the joint in the array returned by + * {@linkScriptAvatar.getJointNames}. + * @function ScriptAvatar.getJointIndex + * @param {string} name - The name of the joint. + * @returns {number} The index of the joint if valid and avatar data are available, otherwise -1. + */ Q_INVOKABLE int getJointIndex(const QString& name) const; + + /**jsdoc + * Gets the names of all the joints in the avatar. + * @function ScriptAvatar.getJointNames + * @returns {string[]} The joint names, or [] if the avatar data aren't available. + */ Q_INVOKABLE QStringList getJointNames() const; + + /**jsdoc + * Gets information about the models currently attached to the avatar. + * @function ScriptAvatar.getAttachmentData + * @returns {AttachmentData[]} Information about all models attached to the avatar, or [] if the avatar data + * aren't available. + * @deprecated This function is deprecated and will be removed. Use avatar entities instead. + */ Q_INVOKABLE QVector getAttachmentData() const; #if DEV_BUILD || PR_BUILD @@ -185,13 +232,54 @@ public: bool getHasPriority() const; signals: + + /**jsdoc + * Triggered when the avatar's displayName property value changes. + * @function ScriptAvatar.displayNameChanged + * @returns {Signal} + */ void displayNameChanged(); + + /**jsdoc + * Triggered when the avatar's sessionDisplayName property value changes. + * @function ScriptAvatar.sessionDisplayNameChanged + * @returns {Signal} + */ void sessionDisplayNameChanged(); + + /**jsdoc + * Triggered when the avatar's model (i.e., skeletonModelURL property value) changes. + * @function ScriptAvatar.skeletonModelURLChanged + * @returns {Signal} + */ void skeletonModelURLChanged(); + + /**jsdoc + * Triggered when the avatar's lookAtSnappingEnabled property value changes. + * @function ScriptAvatar.lookAtSnappingChanged + * @param {boolean} enabled - true if look-at snapping is enabled, false if not. + * @returns {Signal} + */ void lookAtSnappingChanged(bool enabled); public slots: + + /**jsdoc + * Gets the rotation of a joint relative to the avatar. + * @function ScriptAvatar.getAbsoluteJointRotationInObjectFrame + * @param {number} index - The index of the joint. + * @returns {Quat} The rotation of the joint relative to the avatar, or {@link Quat(0)|Quat.IDENTITY} if the avatar data + * aren't available. + */ glm::quat getAbsoluteJointRotationInObjectFrame(int index) const; + + /**jsdoc + * Gets the translation of a joint relative to the avatar. + * @function ScriptAvatar.getAbsoluteJointTranslationInObjectFrame + * @param {number} index - The index of the joint. + * @returns {Vec3} The translation of the joint relative to the avatar, or {@link Vec3(0)|Vec3.ZERO} if the avatar data + * aren't available. + */ glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const; protected: diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp index 96d7247a82..def217f8fd 100644 --- a/libraries/baking/src/JSBaker.cpp +++ b/libraries/baking/src/JSBaker.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -62,7 +63,7 @@ void JSBaker::loadScript() { // setup the request to follow re-directs and always hit the network networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); networkRequest.setUrl(_jsURL); diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 70290fe283..1b81ee9740 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -159,7 +160,7 @@ void ModelBaker::saveSourceModel() { // setup the request to follow re-directs and always hit the network networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); networkRequest.setUrl(_modelURL); diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 182caf7fc6..d70c6586b7 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -99,7 +100,7 @@ void TextureBaker::loadTexture() { // setup the request to follow re-directs and always hit the network networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); networkRequest.setUrl(_textureURL); diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 30f4181b43..2edd1adfeb 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -176,8 +176,8 @@ namespace controller { * person view. * BoomOutnumbernumberZoom camera out from first person to third * person view. - * CycleCameranumbernumberCycle the camera view from first person, to - * third person, to full screen mirror, then back to first person and repeat. + * CycleCameranumbernumberCycle the camera view from first person look + * at, to (third person) look at, to selfie if in desktop mode, then back to first person and repeat. * ContextMenunumbernumberShow/hide the tablet. * ToggleMutenumbernumberToggle the microphone mute. * TogglePushToTalknumbernumberToggle push to talk. diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 05e246deaa..a1875c7fe8 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -282,7 +282,7 @@ namespace controller { * Enables or disables a controller mapping. When enabled, the routes in the mapping have effect. * @function Controller.enableMapping * @param {string} mappingName - The name of the mapping. - * @param {boolean} [[enable=true] - If true then the mapping is enabled, otherwise it is disabled. + * @param {boolean} [enable=true] - If true then the mapping is enabled, otherwise it is disabled. */ Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 1eb1a9fa1a..604a4f9c73 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -312,7 +312,10 @@ void UserInputMapper::update(float deltaTime) { Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { Locker locker(_lock); auto iterator = _registeredDevices.find(deviceID); - return iterator->second->getAvailableInputs(); + if (iterator != _registeredDevices.end()) { + return iterator->second->getAvailableInputs(); + } + return Input::NamedVector(); } QVector UserInputMapper::getAllActions() const { @@ -366,7 +369,7 @@ bool UserInputMapper::triggerHapticPulse(float strength, float duration, control Locker locker(_lock); bool toReturn = false; for (const auto& device : _registeredDevices) { - toReturn = toReturn || device.second->triggerHapticPulse(strength, duration, hand); + toReturn = device.second->triggerHapticPulse(strength, duration, hand) || toReturn; } return toReturn; } @@ -1237,16 +1240,42 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { } void UserInputMapper::setActionState(Action action, float value, bool valid) { + Locker locker(_lock); _actionStates[toInt(action)] = value; _actionStatesValid[toInt(action)] = valid; } void UserInputMapper::deltaActionState(Action action, float delta, bool valid) { + Locker locker(_lock); _actionStates[toInt(action)] += delta; bool wasValid = _actionStatesValid[toInt(action)]; _actionStatesValid[toInt(action)] = wasValid & valid; } +float UserInputMapper::getActionState(Action action) const { + Locker locker(_lock); + + int index = toInt(action); + if (index >= 0 && index < _actionStates.size()) { + return _actionStates[index]; + } + + qCDebug(controllers) << "UserInputMapper::getActionState invalid action:" << index; + return 0.0f; +} + +bool UserInputMapper::getActionStateValid(Action action) const { + Locker locker(_lock); + + int index = toInt(action); + if (index >= 0 && index < _actionStatesValid.size()) { + return _actionStatesValid[index]; + } + + qCDebug(controllers) << "UserInputMapper::getActionStateValid invalid action:" << index; + return false; +} + } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index cd44f3226c..79fcf6e64c 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -81,8 +81,8 @@ namespace controller { QVector getAllActions() const; QString getActionName(Action action) const; QString getStandardPoseName(uint16_t pose); - float getActionState(Action action) const { return _actionStates[toInt(action)]; } - bool getActionStateValid(Action action) const { return _actionStatesValid[toInt(action)]; } + float getActionState(Action action) const; + bool getActionStateValid(Action action) const; Pose getPoseState(Action action) const; int findAction(const QString& actionName) const; QVector getActionNames() const; diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 5a8fd3083d..9dafc03f1f 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -54,6 +54,7 @@ class UserInputMapper; * * * @class MappingObject + * @hideconstructor * * @hifi-interface * @hifi-client-entity diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index f1b36cfec5..38b18346a8 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -36,6 +36,7 @@ class ScriptingInterface; * types.

* * @class RouteObject + * @hideconstructor * * @hifi-interface * @hifi-client-entity diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 597f6921cc..d25cde413e 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -34,7 +34,7 @@ class HTTPManager : public QTcpServer, public HTTPRequestHandler { public: /// Initializes the manager. HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = nullptr); - + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; private slots: diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ab3f4c5243..f070c9c2f7 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -226,7 +226,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); if (entityItem && !entityItem->getScript().isEmpty()) { - if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { + if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { if (_currentEntitiesInside.contains(entityID)) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } @@ -240,7 +240,6 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { stopDomainAndNonOwnedEntities(); - auto sessionUUID = getTree()->getMyAvatarSessionUUID(); std::unordered_map savedEntities; std::unordered_set savedRenderables; // remove all entities from the scene @@ -249,7 +248,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { for (const auto& entry : _entitiesInScene) { const auto& renderer = entry.second; const EntityItemPointer& entityItem = renderer->getEntity(); - if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == sessionUUID))) { + if (entityItem && !(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { fadeOutRenderable(renderer); } else { savedEntities[entry.first] = entry.second; @@ -261,7 +260,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { _renderablesToUpdate = savedRenderables; _entitiesInScene = savedEntities; - if (_layeredZones.clearDomainAndNonOwnedZones(sessionUUID)) { + if (_layeredZones.clearDomainAndNonOwnedZones()) { applyLayeredZones(); } @@ -683,7 +682,7 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() { QSet currentEntitiesInsideToSave; foreach (const EntityItemID& entityID, _currentEntitiesInside) { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); - if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { + if (entityItem && !(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { emit leaveEntity(entityID); if (_entitiesScriptEngine) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); @@ -1215,13 +1214,13 @@ void EntityTreeRenderer::updateZone(const EntityItemID& id) { } } -bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& sessionUUID) { +bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones() { bool zonesChanged = false; auto it = begin(); while (it != end()) { auto zone = it->zone.lock(); - if (!zone || !(zone->isLocalEntity() || (zone->isAvatarEntity() && zone->getOwningAvatarID() == sessionUUID))) { + if (!zone || !(zone->isLocalEntity() || zone->isMyAvatarEntity())) { zonesChanged = true; it = erase(it); } else { @@ -1362,6 +1361,10 @@ EntityItemPointer EntityTreeRenderer::getEntity(const EntityItemID& id) { return result; } +void EntityTreeRenderer::deleteEntity(const EntityItemID& id) const { + DependencyManager::get()->deleteEntity(id); +} + void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) { _changedEntitiesGuard.withWriteLock([&] { _changedEntities.insert(id); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 6dbaedc123..1deadc254e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -118,6 +118,7 @@ public: void setProxyWindow(const EntityItemID& id, QWindow* proxyWindow); void setCollisionSound(const EntityItemID& id, const SharedSoundPointer& sound); EntityItemPointer getEntity(const EntityItemID& id); + void deleteEntity(const EntityItemID& id) const; void onEntityChanged(const EntityItemID& id); // Access the workload Space @@ -229,7 +230,7 @@ private: class LayeredZones : public std::vector { public: - bool clearDomainAndNonOwnedZones(const QUuid& sessionUUID); + bool clearDomainAndNonOwnedZones(); void sort() { std::sort(begin(), end(), std::less()); } bool equals(const LayeredZones& other) const; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index ae56a8fc47..9ca958d128 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -43,6 +43,7 @@ const Transform& EntityRenderer::getModelTransform() const { void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::Status::Getters& statusGetters) { auto nodeList = DependencyManager::get(); + // DANGER: nodeList->getSessionUUID() will return null id when not connected to domain. const QUuid& myNodeID = nodeList->getSessionUUID(); statusGetters.push_back([entity]() -> render::Item::Status::Value { @@ -103,9 +104,9 @@ void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::St (unsigned char)render::Item::Status::Icon::HAS_ACTIONS); }); - statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value { + statusGetters.push_back([entity] () -> render::Item::Status::Value { if (entity->isAvatarEntity()) { - if (entity->getOwningAvatarID() == myNodeID) { + if (entity->isMyAvatarEntity()) { return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN, (unsigned char)render::Item::Status::Icon::ENTITY_HOST_TYPE); } else { @@ -128,7 +129,7 @@ std::shared_ptr make_renderer(const EntityItemPointer& entity) { return std::shared_ptr(new T(entity), [](T* ptr) { ptr->deleteLater(); }); } -EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity) {} +EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity), _entityID(entity->getID()) {} EntityRenderer::~EntityRenderer() {} @@ -196,6 +197,23 @@ uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const { return 0; } +bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& containingZones) const { + auto renderWithZones = resultWithReadLock>([&] { + return _renderWithZones; + }); + if (!renderWithZones.isEmpty()) { + if (!containingZones.empty()) { + for (auto renderWithZone : renderWithZones) { + if (containingZones.find(renderWithZone) != containingZones.end()) { + return true; + } + } + } + return false; + } + return true; +} + void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { return; @@ -385,6 +403,10 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity return true; } + if (entity->needsZoneOcclusionUpdate()) { + return true; + } + return false; } @@ -426,6 +448,10 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa _canCastShadow = entity->getCanCastShadow(); setCullWithParent(entity->getCullWithParent()); _cauterized = entity->getCauterized(); + if (entity->needsZoneOcclusionUpdate()) { + entity->resetNeedsZoneOcclusionUpdate(); + setRenderWithZones(entity->getRenderWithZones()); + } entity->setNeedsRenderUpdate(false); }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 227eb6a018..9c787d82c2 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -65,6 +65,7 @@ public: virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; virtual Item::Bound getBound() override; + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override; protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -109,6 +110,7 @@ protected: virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; } virtual void setPrimitiveMode(PrimitiveMode value) { _primitiveMode = value; } virtual void setCullWithParent(bool value) { _cullWithParent = value; } + virtual void setRenderWithZones(const QVector& renderWithZones) { _renderWithZones = renderWithZones; } template T withReadLockResult(const std::function& f) { @@ -143,6 +145,7 @@ protected: bool _cullWithParent { false }; RenderLayer _renderLayer { RenderLayer::WORLD }; PrimitiveMode _primitiveMode { PrimitiveMode::SOLID }; + QVector _renderWithZones; bool _cauterized { false }; bool _moving { false }; // Only touched on the rendering thread @@ -153,6 +156,7 @@ protected: std::mutex _materialsLock; quint64 _created; + QUuid _entityID; // The base class relies on comparing the model transform to the entity transform in order // to trigger an update, so the member must not be visible to derived classes as a modifiable diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 5fbbdfa0b8..eb7856b7ed 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1083,6 +1083,11 @@ uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const { return 0; } +void ModelEntityRenderer::handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) { + setBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs); +} + void ModelEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& transaction) { if (_model) { _model->removeFromScene(scene, transaction); @@ -1251,7 +1256,11 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin if (model && model->isLoaded()) { if (!entity->_dimensionsInitialized || entity->_needsInitialSimulation || !entity->_originalTexturesRead) { return true; - } + } + + if (entity->blendshapesChanged()) { + return true; + } // Check to see if we need to update the model bounds if (entity->needsUpdateModelBounds()) { @@ -1324,8 +1333,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce setKey(didVisualGeometryRequestSucceed); _model->setTagMask(getTagMask()); _model->setHifiRenderLayer(getHifiRenderLayer()); + _model->setPrimitiveMode(_primitiveMode); + _model->setCullWithParent(_cullWithParent); + _model->setRenderWithZones(_renderWithZones); emit requestRenderUpdate(); - if(didVisualGeometryRequestSucceed) { + if (didVisualGeometryRequestSucceed) { emit DependencyManager::get()-> modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model); } @@ -1407,6 +1419,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->setTagMask(tagMask, scene); } + if (entity->blendshapesChanged()) { + model->setBlendshapeCoefficients(entity->getBlendshapeCoefficientVector()); + model->updateBlendshapes(); + } + // TODO? early exit here when not visible? if (model->canCastShadow() != _canCastShadow) { @@ -1427,7 +1444,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->removeFromScene(scene, transaction); render::Item::Status::Getters statusGetters; makeStatusGetters(entity, statusGetters); - model->addToScene(scene, transaction, statusGetters); + using namespace std::placeholders; + model->addToScene(scene, transaction, statusGetters, std::bind(&ModelEntityRenderer::metaBlendshapeOperator, _renderItemID, _1, _2, _3, _4)); entity->bumpAncestorChainRenderableVersion(); processMaterials(); } @@ -1507,6 +1525,13 @@ void ModelEntityRenderer::setCullWithParent(bool value) { } } +void ModelEntityRenderer::setRenderWithZones(const QVector& renderWithZones) { + Parent::setRenderWithZones(renderWithZones); + if (_model) { + _model->setRenderWithZones(renderWithZones); + } +} + // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items void ModelEntityRenderer::doRender(RenderArgs* args) { DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); @@ -1579,3 +1604,12 @@ void ModelEntityRenderer::processMaterials() { } } } + +void ModelEntityRenderer::metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) { + render::Transaction transaction; + transaction.updateItem(renderItemID, [blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs](PayloadProxyInterface& self) { + self.handleBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs); + }); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 0119c7bc26..f42f0aec94 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "RenderableEntityItem.h" @@ -131,7 +132,7 @@ private: namespace render { namespace entities { -class ModelEntityRenderer : public TypedEntityRenderer { +class ModelEntityRenderer : public TypedEntityRenderer, public MetaModelPayload { using Parent = TypedEntityRenderer; friend class EntityRenderer; Q_OBJECT @@ -155,6 +156,8 @@ protected: void setKey(bool didVisualGeometryRequestSucceed); virtual ItemKey getKey() override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; + virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual bool needsRenderUpdate() const override; @@ -165,6 +168,7 @@ protected: void setRenderLayer(RenderLayer value) override; void setPrimitiveMode(PrimitiveMode value) override; void setCullWithParent(bool value) override; + void setRenderWithZones(const QVector& renderWithZones) override; private: void animate(const TypedEntityPointer& entity); @@ -198,6 +202,10 @@ private: bool _prevModelLoaded { false }; void processMaterials(); + + static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs); + }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index f34eb85230..bc56781924 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -199,7 +199,7 @@ float importanceSample3DDimension(float startDim) { return dimension; } -ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, +ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(const Transform& baseTransform, const particle::Properties& particleProperties, const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource, const TriangleInfo& triangleInfo) { CpuParticle particle; @@ -217,7 +217,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa const auto& polarFinish = particleProperties.polar.finish; particle.seed = randFloatInRange(-1.0f, 1.0f); - particle.expiration = now + (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND); + particle.expiration = (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND); particle.relativePosition = glm::vec3(0.0f); particle.basePosition = baseTransform.getTranslation(); @@ -403,7 +403,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { computeTriangles(geometryResource->getHFMModel()); } // emit particle - _cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo)); + _cpuParticles.push_back(createParticle(modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo)); _timeUntilNextEmit = emitInterval; if (emitInterval < timeRemaining) { timeRemaining -= emitInterval; @@ -415,7 +415,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { } // Kill any particles that have expired or are over the max size - while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration <= now)) { + while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration == 0)) { _cpuParticles.pop_front(); } @@ -428,6 +428,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { } particle.basePosition = modelTransform.getTranslation(); } + particle.expiration = particle.expiration >= interval ? particle.expiration - interval : 0; particle.integrate(deltaTime); } _prevEmitterShouldTrail = particleProperties.emission.shouldTrail; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index cc907f2b1d..8a3d8120f5 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -88,7 +88,7 @@ private: glm::mat4 transform; } _triangleInfo; - static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, + static CpuParticle createParticle(const Transform& baseTransform, const particle::Properties& particleProperties, const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource, const TriangleInfo& triangleInfo); void stepSimulation(); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 5bc61606ad..e4d6d099a6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -45,7 +45,7 @@ private: PulsePropertyGroup _pulseProperties; std::shared_ptr _material { std::make_shared() }; glm::vec3 _color { NAN }; - float _alpha; + float _alpha { NAN }; glm::vec3 _position; glm::vec3 _dimensions; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index fbd10e2f5b..2ba1acea11 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -289,6 +289,17 @@ ShapeKey entities::TextPayload::getShapeKey() const { return ShapeKey::Builder::invalid(); } +bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set& containingZones) const { + auto entityTreeRenderer = DependencyManager::get(); + if (entityTreeRenderer) { + auto renderable = entityTreeRenderer->renderableForEntityId(_entityID); + if (renderable) { + return std::static_pointer_cast(renderable)->passesZoneOcclusionTest(containingZones); + } + } + return false; +} + void entities::TextPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("TextPayload::render"); Q_ASSERT(args->_batch); @@ -388,4 +399,12 @@ template <> const ShapeKey shapeGetShapeKey(const TextPayload::Pointer& payload) template <> void payloadRender(const TextPayload::Pointer& payload, RenderArgs* args) { return payload->render(args); } + +template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones) { + if (payload) { + return payload->passesZoneOcclusionTest(containingZones); + } + return false; +} + } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 63cf3e6e9e..87102daa32 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -94,6 +94,7 @@ public: Item::Bound getBound() const; ShapeKey getShapeKey() const; void render(RenderArgs* args); + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; protected: QUuid _entityID; @@ -109,6 +110,7 @@ namespace render { template <> const Item::Bound payloadGetBound(const entities::TextPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); + template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones); } #endif // hifi_RenderableTextEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index bbb8c67ad1..948c6ca762 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -71,6 +71,11 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity } void ZoneEntityRenderer::doRender(RenderArgs* args) { + // This is necessary so that zones can themselves be zone culled + if (!passesZoneOcclusionTest(CullTest::_prevContainingZones)) { + return; + } + if (!_stage) { _stage = args->_scene->getStage(); assert(_stage); @@ -180,6 +185,8 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { _bloomStage->_currentFrame.pushBloom(_bloomIndex); } } + + CullTest::_containingZones.insert(_entityID); } void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 3d5cc190d0..eb46be1e20 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -19,6 +19,8 @@ <@include paintStroke.slh@> <$declarePolyLineBuffers()$> +<@include CullFace.slh@> + LAYOUT(binding=0) uniform sampler2D _texture; <@if not HIFI_USE_FORWARD@> @@ -35,9 +37,9 @@ void main(void) { <@if not HIFI_USE_FORWARD@> <@if HIFI_USE_TRANSLUCENT@> - packDeferredFragmentTranslucent((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb, DEFAULT_ROUGHNESS); + packDeferredFragmentTranslucent(evalFrontOrBackFaceNormal(_normalWS), texel.a, texel.rgb, DEFAULT_ROUGHNESS); <@else@> - packDeferredFragmentUnlit((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb); + packDeferredFragmentUnlit(evalFrontOrBackFaceNormal(_normalWS), texel.a, texel.rgb); <@endif@> <@else@> _fragColor0 = texel; diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp index 1dca171ae3..a4ecb532e5 100644 --- a/libraries/entities/src/DeleteEntityOperator.cpp +++ b/libraries/entities/src/DeleteEntityOperator.cpp @@ -53,6 +53,15 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt } } +void DeleteEntityOperator::addEntityToDeleteList(const EntityItemPointer& entity) { + assert(entity && entity->getElement()); + EntityToDeleteDetails details; + details.entity = entity; + details.containingElement = entity->getElement(); + details.cube = details.containingElement->getAACube(); + _entitiesToDelete << details; + _lookingCount++; +} // does this entity tree element contain the old entity bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element) { diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h index 3b3ee2a868..1449e2caad 100644 --- a/libraries/entities/src/DeleteEntityOperator.h +++ b/libraries/entities/src/DeleteEntityOperator.h @@ -42,6 +42,7 @@ public: ~DeleteEntityOperator(); void addEntityIDToDeleteList(const EntityItemID& searchEntityID); + void addEntityToDeleteList(const EntityItemPointer& entity); virtual bool preRecursion(const OctreeElementPointer& element) override; virtual bool postRecursion(const OctreeElementPointer& element) override; diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index a222ca8216..16dace0fc8 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -43,7 +43,7 @@ QList EntityEditFilters::getZonesByPosition(glm::vec3& position) { } bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, - bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, EntityItemPointer& existingEntity) { + bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, const EntityItemPointer& existingEntity) { // get the ids of all the zones (plus the global entity edit filter) that the position // lies within diff --git a/libraries/entities/src/EntityEditFilters.h b/libraries/entities/src/EntityEditFilters.h index cb99c97762..69fd920998 100644 --- a/libraries/entities/src/EntityEditFilters.h +++ b/libraries/entities/src/EntityEditFilters.h @@ -55,7 +55,7 @@ public: void removeFilter(EntityItemID entityID); bool filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, - EntityTree::FilterType filterType, EntityItemID& entityID, EntityItemPointer& existingEntity); + EntityTree::FilterType filterType, EntityItemID& entityID, const EntityItemPointer& existingEntity); signals: void filterAdded(EntityItemID id, bool success); diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index dbb3ab076e..aaaf7d645a 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -73,8 +73,12 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, if (properties.getEntityHostType() == entity::HostType::AVATAR) { if (!_myAvatar) { qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit with no myAvatar"; - } else if (properties.getOwningAvatarID() == _myAvatar->getID()) { - // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server + } else if (properties.getOwningAvatarID() == _myAvatar->getID() || properties.getOwningAvatarID() == AVATAR_SELF_ID) { + // this is a local avatar-entity --> update our avatar-data rather than sending to the entity-server + // Note: we store AVATAR_SELF_ID in EntityItem::_owningAvatarID and we usually + // store the actual sessionUUID in EntityItemProperties::_owningAvatarID. + // However at this context we check for both cases just in case. Really we just want to know + // where to route the data: entity-server or avatar-mixer. queueEditAvatarEntityMessage(entityTree, entityItemID); } else { qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit for another avatar"; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 856468f2ec..3623e5f96a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -106,6 +106,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_RENDER_LAYER; requestedProperties += PROP_PRIMITIVE_MODE; requestedProperties += PROP_IGNORE_PICK_INTERSECTION; + requestedProperties += PROP_RENDER_WITH_ZONES; requestedProperties += _grabProperties.getEntityProperties(params); // Physics @@ -301,6 +302,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)getRenderLayer()); APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)getPrimitiveMode()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection()); + APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, getRenderWithZones()); withReadLock([&] { _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -876,6 +878,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_RENDER_LAYER, RenderLayer, setRenderLayer); READ_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode); READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); + READ_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); withWriteLock([&] { int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, @@ -1348,13 +1351,14 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityHostType, getEntityHostType); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarIDForProperties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube); COPY_ENTITY_PROPERTY_TO_PROPERTIES(canCastShadow, getCanCastShadow); COPY_ENTITY_PROPERTY_TO_PROPERTIES(isVisibleInSecondaryCamera, isVisibleInSecondaryCamera); COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderLayer, getRenderLayer); COPY_ENTITY_PROPERTY_TO_PROPERTIES(primitiveMode, getPrimitiveMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderWithZones, getRenderWithZones); withReadLock([&] { _grabProperties.getProperties(properties); }); @@ -1504,6 +1508,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderLayer, setRenderLayer); SET_ENTITY_PROPERTY_FROM_PROPERTIES(primitiveMode, setPrimitiveMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderWithZones, setRenderWithZones); withWriteLock([&] { bool grabPropertiesChanged = _grabProperties.setProperties(properties); somethingChanged |= grabPropertiesChanged; @@ -2708,10 +2713,12 @@ quint64 EntityItem::getLastEdited() const { } void EntityItem::setLastEdited(quint64 lastEdited) { - withWriteLock([&] { - _lastEdited = _lastUpdated = lastEdited; - _changedOnServer = glm::max(lastEdited, _changedOnServer); - }); + if (lastEdited == 0 || lastEdited > _lastEdited) { + withWriteLock([&] { + _lastEdited = _lastUpdated = lastEdited; + _changedOnServer = glm::max(lastEdited, _changedOnServer); + }); + } } void EntityItem::markAsChangedOnServer() { @@ -3027,7 +3034,11 @@ void EntityItem::setCanCastShadow(bool value) { } bool EntityItem::getCullWithParent() const { - return _cullWithParent; + bool result; + withReadLock([&] { + result = _cullWithParent; + }); + return result; } void EntityItem::setCullWithParent(bool value) { @@ -3215,6 +3226,7 @@ void EntityItem::somethingChangedNotification() { }); } +// static void EntityItem::retrieveMarketplacePublicKey() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; @@ -3246,6 +3258,23 @@ void EntityItem::retrieveMarketplacePublicKey() { }); } +void EntityItem::collectChildrenForDelete(std::vector& entitiesToDelete, const QUuid& sessionID) const { + // Deleting an entity has consequences for its children, however there are rules dictating what can be deleted. + // This method helps enforce those rules: not for this entity, but for its children. + for (SpatiallyNestablePointer child : getChildren()) { + if (child && child->getNestableType() == NestableType::Entity) { + EntityItemPointer childEntity = std::static_pointer_cast(child); + // NOTE: null sessionID means "collect ALL known children", else we only collect: local-entities and myAvatar-entities + if (sessionID.isNull() || childEntity->isLocalEntity() || childEntity->isMyAvatarEntity()) { + if (std::find(entitiesToDelete.begin(), entitiesToDelete.end(), childEntity) == entitiesToDelete.end()) { + entitiesToDelete.push_back(childEntity); + childEntity->collectChildrenForDelete(entitiesToDelete, sessionID); + } + } + } + } +} + void EntityItem::setSpaceIndex(int32_t index) { assert(_spaceIndex == -1); _spaceIndex = index; @@ -3410,6 +3439,7 @@ void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properti properties.setSimulationOwner(Physics::getSessionUUID(), priority); setPendingOwnershipPriority(priority); + // TODO: figure out if it would be OK to NOT bother set these properties here properties.setEntityHostType(getEntityHostType()); properties.setOwningAvatarID(getOwningAvatarID()); setLastBroadcast(now); // for debug/physics status icons @@ -3421,9 +3451,27 @@ bool EntityItem::isWearable() const { } bool EntityItem::isMyAvatarEntity() const { - return _hostType == entity::HostType::AVATAR && Physics::getSessionUUID() == _owningAvatarID; + return _hostType == entity::HostType::AVATAR && AVATAR_SELF_ID == _owningAvatarID; }; +QUuid EntityItem::getOwningAvatarIDForProperties() const { + if (isMyAvatarEntity()) { + // NOTE: we always store AVATAR_SELF_ID for MyAvatar's avatar entities, + // however for EntityItemProperties to be consumed by outside contexts (e.g. JS) + // we use the actual "sessionUUID" which is conveniently cached in the Physics namespace + return Physics::getSessionUUID(); + } + return _owningAvatarID; +} + +void EntityItem::setOwningAvatarID(const QUuid& owningAvatarID) { + if (!owningAvatarID.isNull() && owningAvatarID == Physics::getSessionUUID()) { + _owningAvatarID = AVATAR_SELF_ID; + } else { + _owningAvatarID = owningAvatarID; + } +} + void EntityItem::addGrab(GrabPointer grab) { enableNoBootstrap(); SpatiallyNestable::addGrab(grab); @@ -3521,3 +3569,18 @@ void EntityItem::disableGrab(GrabPointer grab) { } } } + +void EntityItem::setRenderWithZones(const QVector& renderWithZones) { + withWriteLock([&] { + if (_renderWithZones != renderWithZones) { + _needsZoneOcclusionUpdate = true; + _renderWithZones = renderWithZones; + } + }); +} + +QVector EntityItem::getRenderWithZones() const { + return resultWithReadLock>([&] { + return _renderWithZones; + }); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 14b8b259cc..2bb5dab16b 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -18,6 +18,7 @@ #include #include +#include #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState @@ -49,6 +50,7 @@ typedef std::shared_ptr EntityTreePointer; typedef std::shared_ptr EntityDynamicPointer; typedef std::shared_ptr EntityTreeElementPointer; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; +using SetOfEntities = QSet; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { }; @@ -517,7 +519,8 @@ public: // if this entity is an avatar entity, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } - virtual void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + QUuid getOwningAvatarIDForProperties() const; + void setOwningAvatarID(const QUuid& owningAvatarID); virtual bool wantsHandControllerPointerEvents() const { return false; } virtual bool wantsKeyboardFocus() const { return false; } @@ -543,6 +546,8 @@ public: static QString _marketplacePublicKey; static void retrieveMarketplacePublicKey(); + void collectChildrenForDelete(std::vector& entitiesToDelete, const QUuid& sessionID) const; + float getBoundingRadius() const { return _boundingRadius; } void setSpaceIndex(int32_t index); int32_t getSpaceIndex() const { return _spaceIndex; } @@ -577,6 +582,11 @@ public: bool needsRenderUpdate() const { return resultWithReadLock([&] { return _needsRenderUpdate; }); } void setNeedsRenderUpdate(bool needsRenderUpdate) { withWriteLock([&] { _needsRenderUpdate = needsRenderUpdate; }); } + void setRenderWithZones(const QVector& renderWithZones); + QVector getRenderWithZones() const; + bool needsZoneOcclusionUpdate() const { return _needsZoneOcclusionUpdate; } + void resetNeedsZoneOcclusionUpdate() { withWriteLock([&] { _needsZoneOcclusionUpdate = false; }); } + signals: void spaceUpdate(std::pair data); @@ -765,8 +775,11 @@ protected: QHash _grabActions; + QVector _renderWithZones; + mutable bool _needsZoneOcclusionUpdate { false }; + bool _cullWithParent { false }; - + mutable bool _needsRenderUpdate { false }; private: diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 9afaa5e85b..d671d46c22 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -299,6 +299,16 @@ void EntityItemProperties::setAvatarPriorityFromString(const QString& mode) { } } +QString EntityItemProperties::getScreenshareAsString() const { return getComponentModeAsString(_screenshare); } +void EntityItemProperties::setScreenshareFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _screenshare = modeItr.value(); + _screenshareChanged = true; + } +} + + inline void addTextEffect(QHash& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; } const QHash stringToTextEffectLookup = [] { QHash toReturn; @@ -421,6 +431,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RENDER_LAYER, renderLayer); CHECK_PROPERTY_CHANGE(PROP_PRIMITIVE_MODE, primitiveMode); CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); + CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); changedProperties += _grab.getChangedProperties(); // Physics @@ -525,6 +536,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_JOINT_TRANSLATIONS, jointTranslations); CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled); + CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); changedProperties += _animation.getChangedProperties(); // Light @@ -566,6 +578,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode); CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority); + CHECK_PROPERTY_CHANGE(PROP_SCREENSHARE, screenshare); // Polyvox CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -631,33 +644,33 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { } /**jsdoc - * Different entity types have different properties: some common to all entities (listed in the table) and some specific to + * Different entity types have different properties: some common to all entities (listed in the table) and some specific to * each {@link Entities.EntityType|EntityType} (linked to below). * * @typedef {object} Entities.EntityProperties * @property {Uuid} id - The ID of the entity. Read-only. * @property {string} name="" - A name for the entity. Need not be unique. - * @property {Entities.EntityType} type - The entity's type. You cannot change the type of an entity after it's created. - * However, its value may switch among "Box", "Shape", and "Sphere" depending on + * @property {Entities.EntityType} type - The entity's type. You cannot change the type of an entity after it's created. + * However, its value may switch among "Box", "Shape", and "Sphere" depending on * changes to the shape property set for entities of these types. Read-only. * * @property {Entities.EntityHostType} entityHostType="domain" - How the entity is hosted and sent to others for display. * The value can only be set at entity creation by one of the {@link Entities.addEntity} methods. Read-only. - * @property {boolean} avatarEntity=false - true if the entity is an {@link Entities.EntityHostType|avatar entity}, - * false if it isn't. The value is per the entityHostType property value, set at entity creation + * @property {boolean} avatarEntity=false - true if the entity is an {@link Entities.EntityHostType|avatar entity}, + * false if it isn't. The value is per the entityHostType property value, set at entity creation * by one of the {@link Entities.addEntity} methods. Read-only. * @property {boolean} clientOnly=false - A synonym for avatarEntity. Read-only. - * @property {boolean} localEntity=false - true if the entity is a {@link Entities.EntityHostType|local entity}, - * false if it isn't. The value is per the entityHostType property value, set at entity creation + * @property {boolean} localEntity=false - true if the entity is a {@link Entities.EntityHostType|local entity}, + * false if it isn't. The value is per the entityHostType property value, set at entity creation * by one of the {@link Entities.addEntity} methods. Read-only. * - * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is + * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is * true, otherwise {@link Uuid(0)|Uuid.NULL}. Read-only. * * @property {string} created - The UTC date and time that the entity was created, in ISO 8601 format as * yyyy-MM-ddTHH:mm:ssZ. Read-only. * @property {number} age - The age of the entity in seconds since it was created. Read-only. - * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s + * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s * seconds. * @property {number} lifetime=-1 - How long an entity lives for, in seconds, before being automatically deleted. A value of * -1 means that the entity lives for ever. @@ -666,133 +679,137 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Uuid} lastEditedBy - The session ID of the avatar or agent that most recently created or edited the entity. * Read-only. * - * @property {boolean} locked=false - true if properties other than locked cannot be changed and the + * @property {boolean} locked=false - true if properties other than locked cannot be changed and the * entity cannot be deleted, false if all properties can be changed and the entity can be deleted. * @property {boolean} visible=true - true if the entity is rendered, false if it isn't. - * @property {boolean} canCastShadow=true - true if the entity can cast a shadow, false if it can't. - * Currently applicable only to {@link Entities.EntityProperties-Model|Model} and - * {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a - * {@link Entities.EntityProperties-Zone|Zone} entity with castShadows enabled in its keyLight + * @property {boolean} canCastShadow=true - true if the entity can cast a shadow, false if it can't. + * Currently applicable only to {@link Entities.EntityProperties-Model|Model} and + * {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a + * {@link Entities.EntityProperties-Zone|Zone} entity with castShadows enabled in its keyLight * property. - * @property {boolean} isVisibleInSecondaryCamera=true - true if the entity is rendered in the secondary camera, + * @property {boolean} isVisibleInSecondaryCamera=true - true if the entity is rendered in the secondary camera, * false if it isn't. * @property {Entities.RenderLayer} renderLayer="world" - The layer that the entity renders in. * @property {Entities.PrimitiveMode} primitiveMode="solid" - How the entity's geometry is rendered. - * @property {boolean} ignorePickIntersection=false - true if {@link Picks} and {@link RayPick} ignore the entity, + * @property {boolean} ignorePickIntersection=false - true if {@link Picks} and {@link RayPick} ignore the entity, * false if they don't. * * @property {Vec3} position=0,0,0 - The position of the entity in world coordinates. * @property {Quat} rotation=0,0,0,1 - The orientation of the entity in world coordinates. - * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated - * about, range {@link Vec3(0)|Vec3.ZERO} – {@link Vec3(0)|Vec3.ONE}. A value of {@link Vec3(0)|Vec3.ZERO} is the + * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated + * about, range {@link Vec3(0)|Vec3.ZERO} – {@link Vec3(0)|Vec3.ONE}. A value of {@link Vec3(0)|Vec3.ZERO} is the * entity's minimum x, y, z corner; a value of {@link Vec3(0)|Vec3.ONE} is the entity's maximum x, y, z corner. * * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise * {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise + * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise * {@link Vec3(0)|Vec3.ONE}. Read-only. * * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. - * @property {number} damping=0.39347 - How much the linear velocity of an entity slows down over time, range - * 0.01.0. A higher damping value slows down the entity more quickly. The default value - * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * @property {number} damping=0.39347 - How much the linear velocity of an entity slows down over time, range + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 * of its initial value. * @property {Vec3} angularVelocity=0,0,0 - The angular velocity of the entity in rad/s with respect to its axes, about its * registration point. - * @property {number} angularDamping=0.39347 - How much the angular velocity of an entity slows down over time, range - * 0.01.0. A higher damping value slows down the entity more quickly. The default value - * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * @property {number} angularDamping=0.39347 - How much the angular velocity of an entity slows down over time, range + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 * of its initial value. * - * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in - * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied + * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in + * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied * to an entity's motion only if its dynamic property is true. - *

If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small + *

If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small * velocity in order to kick off physics simulation.

* @property {Vec3} acceleration - The current, measured acceleration of the entity, in m/s2. *

Deprecated: This property is deprecated and will be removed.

- * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – + * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – * 0.99. The higher the value, the more bouncy. - * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 - * – 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, + * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 + * – 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, * 0.9 for sandpaper. - * @property {number} density=1000 - The density of the entity in kg/m3, range 100 – - * 10000. Examples: 100 for balsa wood, 10000 for silver. The density is used in + * @property {number} density=1000 - The density of the entity in kg/m3, range 100 – + * 10000. Examples: 100 for balsa wood, 10000 for silver. The density is used in * conjunction with the entity's bounding box volume to work out its mass in the application of physics. * - * @property {boolean} collisionless=false - true if the entity shouldn't collide, false if it + * @property {boolean} collisionless=false - true if the entity shouldn't collide, false if it * collides with items per its collisionMask property. * @property {boolean} ignoreForCollisions - Synonym for collisionless. * @property {CollisionMask} collisionMask=31 - What types of items the entity should collide with. * @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for collisionMask, * in text format. - * @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file + * @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file * formats are per {@link SoundObject}. - * @property {boolean} dynamic=false - true if the entity's movement is affected by collisions, false - * if it isn't. + * @property {boolean} dynamic=false - true if the entity's movement is affected by collisions, false + * if it isn't. * @property {boolean} collisionsWillMove - A synonym for dynamic. * * @property {string} href="" - A "hifi://" metaverse address that a user is teleported to when they click on the entity. * @property {string} description="" - A description of the href property value. * - * @property {string} userData="" - Used to store extra data about the entity in JSON format. - *

Warning: Other apps may also use this property, so make sure you handle data stored by other apps: - * edit only your bit and leave the rest of the data intact. You can use JSON.parse() to parse the string into - * a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert the + * @property {string} userData="" - Used to store extra data about the entity in JSON format. + *

Warning: Other apps may also use this property, so make sure you handle data stored by other apps: + * edit only your bit and leave the rest of the data intact. You can use JSON.parse() to parse the string into + * a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert the * object into a string to put back in the property.

* - * @property {string} privateUserData="" - Like userData, but only accessible by server entity scripts, assignment + * @property {string} privateUserData="" - Like userData, but only accessible by server entity scripts, assignment * client scripts, and users who have "Can Get and Set Private User Data" permissions in the domain. * * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. - * @property {number} scriptTimestamp=0 - Used to indicate when the client entity script was loaded. Should be - * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). - * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" + * @property {number} scriptTimestamp=0 - Used to indicate when the client entity script was loaded. Should be + * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). + * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" * button beside the "script URL" field in properties tab of the Create app works. * @property {string} serverScripts="" - The URL of the server entity script, if any, that is attached to the entity. * - * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of + * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of * {@link Uuid(0)|Uuid.NULL} is used if the entity is not parented. - * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use + * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a joint. - * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, - * otherwise the same value as position. If the entity is parented to an avatar and is an avatar entity + * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, + * otherwise the same value as position. If the entity is parented to an avatar and is an avatar entity * so that it scales with the avatar, this value remains the original local position value while the avatar scale changes. - * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, + * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, * otherwise the same value as rotation. - * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, + * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, * otherwise the same value as velocity. - * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is + * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is * parented, otherwise the same value as angularVelocity. * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is an - * avatar entity so that it scales with the avatar, this value remains the original dimensions value while the + * avatar entity so that it scales with the avatar, this value remains the original dimensions value while the * avatar scale changes. * - * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. + * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. * Read-only. - * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. - * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: - * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce + * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. + * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: + * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce * unnecessary entity server updates. Scripts should not change this property's value. * * @property {string} actionData="" - Base-64 encoded compressed dump of the actions associated with the entity. This property * is typically not used in scripts directly; rather, functions that manipulate an entity's actions update it, e.g., * {@link Entities.addAction}. The size of this property increases with the number of actions. Because this property value - * has to fit within a High Fidelity datagram packet, there is a limit to the number of actions that an entity can have; + * has to fit within a Vircadia datagram packet, there is a limit to the number of actions that an entity can have; * edits which would result in overflow are rejected. Read-only. - * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only + * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only * provided for Model entities. Read-only. * - * @property {boolean} cloneable=false - true if the domain or avatar entity can be cloned via + * @property {boolean} cloneable=false - true if the domain or avatar entity can be cloned via * {@link Entities.cloneEntity}, false if it can't be. * @property {number} cloneLifetime=300 - The entity lifetime for clones created from this entity. * @property {number} cloneLimit=0 - The total number of clones of this entity that can exist in the domain at any given time. - * @property {boolean} cloneDynamic=false - true if clones created from this entity will have their + * @property {boolean} cloneDynamic=false - true if clones created from this entity will have their * dynamic property set to true, false if they won't. - * @property {boolean} cloneAvatarEntity=false - true if clones created from this entity will be created as + * @property {boolean} cloneAvatarEntity=false - true if clones created from this entity will be created as * avatar entities, false if they won't be. * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. * + * @property {Uuid[]} renderWithZones=[]] - A list of entity IDs representing with which zones this entity should render. + * If it is empty, this entity will render normally. Otherwise, this entity will only render if your avatar is within + * one of the zones in this list. + * * @property {Entities.Grab} grab - The entity's grab-related properties. * * @property {string} itemName="" - Certifiable name of the Marketplace item. @@ -800,12 +817,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {string} itemCategories="" - Certifiable category of the Marketplace item. * @property {string} itemArtist="" - Certifiable artist that created the Marketplace item. * @property {string} itemLicense="" - Certifiable license URL for the Marketplace item. - * @property {number} limitedRun=4294967295 - Certifiable maximum integer number of editions (copies) of the Marketplace item + * @property {number} limitedRun=4294967295 - Certifiable maximum integer number of editions (copies) of the Marketplace item * allowed to be sold. - * @property {number} editionNumber=0 - Certifiable integer edition (copy) number or the Marketplace item. Each copy sold in + * @property {number} editionNumber=0 - Certifiable integer edition (copy) number or the Marketplace item. Each copy sold in * the Marketplace is numbered sequentially, starting at 1. - * @property {number} entityInstanceNumber=0 - Certifiable integer instance number for identical entities in a Marketplace - * item. A Marketplace item may have multiple, identical parts. If so, then each is numbered sequentially with an instance + * @property {number} entityInstanceNumber=0 - Certifiable integer instance number for identical entities in a Marketplace + * item. A Marketplace item may have multiple, identical parts. If so, then each is numbered sequentially with an instance * number. * @property {string} marketplaceID="" - Certifiable UUID for the Marketplace item, as used in the URL of the item's download * and its Marketplace Web page. @@ -834,8 +851,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Box" {@link Entities.EntityType|EntityType} is the same as the "Shape" * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Cube" - * when the entity is created. If its shape property value is subsequently changed then the entity's - * type will be reported as "Sphere" if the shape is set to "Sphere", + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Sphere" if the shape is set to "Sphere", * otherwise it will be reported as "Shape". * * @typedef {object} Entities.EntityProperties-Box @@ -843,11 +860,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. It has properties in addition + * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. It has properties in addition * to the common {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Light - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Surfaces outside these dimensions are not lit + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Surfaces outside these dimensions are not lit * by the light. * @property {Color} color=255,255,255 - The color of the light emitted. * @property {number} intensity=1 - The brightness of the light. @@ -900,46 +917,46 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Material" {@link Entities.EntityType|EntityType} modifies existing materials on entities and avatars. It + * The "Material" {@link Entities.EntityType|EntityType} modifies existing materials on entities and avatars. It * has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. *

To apply a material to an entity, set the material entity's parentID property to the entity ID. * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. - * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity - * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar" and set the - * entity's parentID property to MyAvatar.SELF_ID. + * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity + * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar" and set the + * entity's parentID property to MyAvatar.SELF_ID. * Material entities render as non-scalable spheres if they don't have their parent set.

* * @typedef {object} Entities.EntityProperties-Material * @property {Vec3} dimensions=0.1,0.1,0.1 - Used when materialMappingMode == "projected". - * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. If you append - * "#name" to the URL, the material with that name in the {@link Entities.MaterialResource|MaterialResource} - * will be applied to the entity. Alternatively, set the property value to "materialData" to use the + * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. If you append + * "#name" to the URL, the material with that name in the {@link Entities.MaterialResource|MaterialResource} + * will be applied to the entity. Alternatively, set the property value to "materialData" to use the * materialData property for the {@link Entities.MaterialResource|MaterialResource} values. - * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. - * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the + * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. + * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the * properties of, and use JSON.stringify() to convert the object into a string to put in the property. * @property {number} priority=0 - The priority for applying the material to its parent. Only the highest priority material is - * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of + * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of * 0. * @property {string} parentMaterialName="0" - Selects the mesh part or parts within the parent to which to apply the material. * If in the format "mat::string", all mesh parts with material name "string" are replaced. - * If "all", then all mesh parts are replaced. - * Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify. - *

If the string represents an array (starts with "[" and ends with "]"), the string is split - * at each "," and each element parsed as either a number or a string if it starts with "mat::". - * For example, "[0,1,mat::string,mat::string2]" will replace mesh parts 0 and 1, and any mesh parts with - * material "string" or "string2". Do not put spaces around the commas. Invalid values are parsed + * If "all", then all mesh parts are replaced. + * Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify. + *

If the string represents an array (starts with "[" and ends with "]"), the string is split + * at each "," and each element parsed as either a number or a string if it starts with "mat::". + * For example, "[0,1,mat::string,mat::string2]" will replace mesh parts 0 and 1, and any mesh parts with + * material "string" or "string2". Do not put spaces around the commas. Invalid values are parsed * to 0.

- * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or - * "projected". In "uv" mode, the material is evaluated within the UV space of the mesh it is - * applied to. In "projected" mode, the 3D transform (position, rotation, and dimensions) of the Material + * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or + * "projected". In "uv" mode, the material is evaluated within the UV space of the mesh it is + * applied to. In "projected" mode, the 3D transform (position, rotation, and dimensions) of the Material * entity is used to evaluate the texture coordinates for the material. - * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range + * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range * { x: 0, y: 0 }{ x: 1, y: 1 }. * @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space. * @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees. - * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If - * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and + * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If + * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and * "projected" modes. * @example Color a sphere using a Material entity. * var entityID = Entities.addEntity({ @@ -966,27 +983,30 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Model" {@link Entities.EntityType|EntityType} displays a glTF, FBX, or OBJ model. When adding an entity, - * if no dimensions value is specified then the model is automatically sized to its - * {@link Entities.EntityProperties|naturalDimensions}. It has properties in addition to the common + * The "Model" {@link Entities.EntityType|EntityType} displays a glTF, FBX, or OBJ model. When adding an entity, + * if no dimensions value is specified then the model is automatically sized to its + * {@link Entities.EntityProperties|naturalDimensions}. It has properties in addition to the common * {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Model - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions - * value is specified then the model is automatically sized to its + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions + * value is specified then the model is automatically sized to its * {@link Entities.EntityProperties|naturalDimensions}. - * @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be + * @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format + * (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be * compressed in GZ format, in which case the URL ends in ".gz". * @property {Vec3} modelScale - The scale factor applied to the model's dimensions. *

Deprecated: This property is deprecated and will be removed.

+ * @property {string} blendshapeCoefficients - A JSON string of a map of blendshape names to values. Only stores set values. + * When editing this property, only coefficients that you are editing will change; it will not explicitly reset other + * coefficients. * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the - * model's original textures. Use a texture name from the originalTextures property to override that texture. - * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no - * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON + * model's original textures. Use a texture name from the originalTextures property to override that texture. + * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no + * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON * string. - * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is - * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to + * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is + * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to * parse the JSON string into a JavaScript object of name, URL pairs. Read-only. * @property {Color} color=255,255,255 - Currently not used. * @@ -996,28 +1016,28 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Entities.AnimationProperties} animation - An animation to play on the model. * - * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the - * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to + * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the + * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to * each joint's parent. - *

Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or - * by setting the value of this property. If you set a joint rotation using this property, you also need to set the + *

Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or + * by setting the value of this property. If you set a joint rotation using this property, you also need to set the * corresponding jointRotationsSet value to true.

- * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, - * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per * {@link Entities.getJointIndex|getJointIndex}. - * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or - * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Translations are + * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or + * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Translations are * relative to each joint's parent. - *

Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar - * functions, or by setting the value of this property. If you set a joint translation using this property you also need to + *

Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar + * functions, or by setting the value of this property. If you set a joint translation using this property you also need to * set the corresponding jointTranslationsSet value to true.

- * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, - * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per * {@link Entities.getJointIndex|getJointIndex}. - * @property {boolean} relayParentJoints=false - true if when the entity is parented to an avatar, the avatar's - * joint rotations are applied to the entity's joints; false if a parent avatar's joint rotations are not + * @property {boolean} relayParentJoints=false - true if when the entity is parented to an avatar, the avatar's + * joint rotations are applied to the entity's joints; false if a parent avatar's joint rotations are not * applied to the entity's joints. - * @property {boolean} groupCulled=false - true if the mesh parts of the model are LOD culled as a group, + * @property {boolean} groupCulled=false - true if the mesh parts of the model are LOD culled as a group, * false if separate mesh parts are LOD culled individually. * * @example Rez a Vive tracker puck. @@ -1025,8 +1045,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * type: "Model", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -2 })), * rotation: MyAvatar.orientation, - * modelURL: "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj", - * dimensions: { x: 0.0945, y: 0.0921, z: 0.0423 }, + * modelURL: "https://apidocs.vircadia.dev/models/cowboy-hat.fbx", + * dimensions: { x: 0.8569, y: 0.3960, z: 1.0744 }, * lifetime: 300 // Delete after 5 minutes. * }); */ @@ -1038,15 +1058,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-ParticleEffect * @property {boolean} isEmitting=true - true if particles are being emitted, false if they aren't. - * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if + * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if * necessary when new ones are created. * @property {number} lifespan=3s - How long, in seconds, each particle lives. * @property {number} emitRate=15 - The number of particles per second to emit. * @property {number} emitSpeed=5 - The speed, in m/s, that each particle is emitted at. - * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. For example, if - * emitSpeed == 5 and speedSpread == 1, particles will be emitted with speeds in the range + * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. For example, if + * emitSpeed == 5 and speedSpread == 1, particles will be emitted with speeds in the range * 46m/s. - * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The + * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The * default is Earth's gravity value. * @property {Vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. For example, if * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == @@ -1058,33 +1078,33 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * false if they stay within the entity's dimensions. * * @property {Quat} emitOrientation=-0.707,0,0,0.707 - The orientation of particle emission relative to the entity's axes. By - * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish - * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., + * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish + * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., * the particles emit vertically. * * @property {ShapeType} shapeType="ellipsoid" - The shape from which particles are emitted. - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType == + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType == * "compound". * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. * @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted; * range 0.01.0 for the center to the surface, respectively. - * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the + * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the * shape's surface. - * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted - * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted + * within the shape; range 0Math.PI. Particles are emitted from the portion of the * shape that lies between polarStart and polarFinish. Only used if shapeType is * "ellipsoid" or "sphere". - * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted - * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted + * within the shape; range 0Math.PI. Particles are emitted from the portion of the * shape that lies between polarStart and polarFinish. Only used if shapeType is * "ellipsoid" or "sphere". - * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local - * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local + * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. * Only used if shapeType is "ellipsoid", "sphere", or "circle". * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. * Only used if shapeType is "ellipsoid", "sphere", or "circle". * * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, @@ -1094,40 +1114,40 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * particleRadius value is used. * @property {number} radiusFinish=null - The radius of each particle at the end of its life. If null, the * particleRadius value is used. - * @property {number} radiusSpread=0 - The spread in radius that each particle is given. For example, if - * particleRadius == 0.5 and radiusSpread == 0.25, each particle will have a radius in the range + * @property {number} radiusSpread=0 - The spread in radius that each particle is given. For example, if + * particleRadius == 0.5 and radiusSpread == 0.25, each particle will have a radius in the range * 0.250.75. * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. - * @property {ColorFloat} colorStart=null,null,null - The color of each particle at the start of its life. If any of the + * @property {ColorFloat} colorStart=null,null,null - The color of each particle at the start of its life. If any of the * component values are undefined, the color value is used. - * @property {ColorFloat} colorFinish=null,null,null - The color of each particle at the end of its life. If any of the + * @property {ColorFloat} colorFinish=null,null,null - The color of each particle at the end of its life. If any of the * component values are undefined, the color value is used. * @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. For example, if * color == {red: 100, green: 100, blue: 100} and colorSpread == - * {red: 10, green: 25, blue: 50}, each particle will have a color in the range + * {red: 10, green: 25, blue: 50}, each particle will have a color in the range * {red: 90, green: 75, blue: 50}{red: 110, green: 125, blue: 150}. * @property {number} alpha=1 - The opacity of each particle at the middle of its life. * @property {number} alphaStart=null - The opacity of each particle at the start of its life. If null, the * alpha value is used. * @property {number} alphaFinish=null - The opacity of each particle at the end of its life. If null, the * alpha value is used. - * @property {number} alphaSpread=0 - The spread in alpha that each particle is given. For example, if - * alpha == 0.5 and alphaSpread == 0.25, each particle will have an alpha in the range + * @property {number} alphaSpread=0 - The spread in alpha that each particle is given. For example, if + * alpha == 0.5 and alphaSpread == 0.25, each particle will have an alpha in the range * 0.250.75. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

- * @property {number} particleSpin=0 - The rotation of each particle at the middle of its life, range -2 * Math.PI + * @property {number} particleSpin=0 - The rotation of each particle at the middle of its life, range -2 * Math.PI * – 2 * Math.PI radians. - * @property {number} spinStart=null - The rotation of each particle at the start of its life, range -2 * Math.PI + * @property {number} spinStart=null - The rotation of each particle at the start of its life, range -2 * Math.PI * – 2 * Math.PI radians. If null, the particleSpin value is used. - * @property {number} spinFinish=null - The rotation of each particle at the end of its life, range -2 * Math.PI + * @property {number} spinFinish=null - The rotation of each particle at the end of its life, range -2 * Math.PI * – 2 * Math.PI radians. If null, the particleSpin value is used. - * @property {number} spinSpread=0 - The spread in spin that each particle is given, range 0 – - * 2 * Math.PI radians. For example, if particleSpin == Math.PI and - * spinSpread == Math.PI / 2, each particle will have a rotation in the range Math.PI / 2 – + * @property {number} spinSpread=0 - The spread in spin that each particle is given, range 0 – + * 2 * Math.PI radians. For example, if particleSpin == Math.PI and + * spinSpread == Math.PI / 2, each particle will have a rotation in the range Math.PI / 2 – * 3 * Math.PI / 2. - * @property {boolean} rotateWithEntity=false - true if the particles' rotations are relative to the entity's - * instantaneous rotation, false if they're relative to world coordinates. If true with + * @property {boolean} rotateWithEntity=false - true if the particles' rotations are relative to the entity's + * instantaneous rotation, false if they're relative to world coordinates. If true with * particleSpin == 0, the particles keep oriented per the entity's orientation. * * @example Create a ball of green smoke. @@ -1153,27 +1173,27 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-PolyLine - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity, i.e., the size of the bounding box that contains the + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity, i.e., the size of the bounding box that contains the * lines drawn. Read-only. * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's * position. A maximum of 70 points can be specified. - * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are + * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are * relative to the entity's orientation. Must be specified in order for the entity to render. - * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in + * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in * order for the entity to render. - * @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range 0.0,0.0,0.0 - * – 1.0,1.0,1.0. These colors are multiplied with the color of the texture. If there are more line + * @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range 0.0,0.0,0.0 + * – 1.0,1.0,1.0. These colors are multiplied with the color of the texture. If there are more line * points than stroke colors, the color property value is used for the remaining points. *

Warning: The ordinate values are in the range 0.01.0.

- * @property {Color} color=255,255,255 - Used as the color for each point if strokeColors doesn't have a value for + * @property {Color} color=255,255,255 - Used as the color for each point if strokeColors doesn't have a value for * the point. * @property {string} textures="" - The URL of a JPG or PNG texture to use for the lines. If you want transparency, use PNG * format. - * @property {boolean} isUVModeStretch=true - true if the texture is stretched to fill the whole line, + * @property {boolean} isUVModeStretch=true - true if the texture is stretched to fill the whole line, * false if the texture repeats along the line. - * @property {boolean} glow=false - true if the opacity of the strokes drops off away from the line center, + * @property {boolean} glow=false - true if the opacity of the strokes drops off away from the line center, * false if it doesn't. - * @property {boolean} faceCamera=false - true if each line segment rotates to face the camera, false + * @property {boolean} faceCamera=false - true if each line segment rotates to face the camera, false * if they don't. * @example Draw a textured "V". * var entity = Entities.addEntity({ @@ -1208,34 +1228,34 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-PolyVox * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. - * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range - * 1,1,1 to 128,128,128. The dimensions of each voxel is + * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range + * 1,1,1 to 128,128,128. The dimensions of each voxel is * dimensions / voxelVolumesize. - * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of - * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox + * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of + * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox * entity update it. *

The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how - * the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet, + * the particular entity's voxels compress. Because this property value has to fit within a Vircadia datagram packet, * there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.

- * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how + * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how * neighboring PolyVox entities are joined. - * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. + * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {string} yTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local y-axis. + * @property {string} yTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local y-axis. * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {string} zTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. + * @property {string} zTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {Uuid} xNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local x-axis + * @property {Uuid} xNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local x-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} yNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local y-axis + * @property {Uuid} yNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local y-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} zNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local z-axis + * @property {Uuid} zNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local z-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} xPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local x-axis + * @property {Uuid} xPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local x-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} yPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local y-axis + * @property {Uuid} yPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local y-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} zPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local z-axis + * @property {Uuid} zPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local z-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. * @example Create a textured PolyVox sphere. * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); @@ -1276,8 +1296,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" - * when the entity is created. If its shape property value is subsequently changed then the entity's - * type will be reported as "Box" if the shape is set to "Cube", + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Box" if the shape is set to "Cube", * otherwise it will be reported as "Shape". * * @typedef {object} Entities.EntityProperties-Sphere @@ -1303,7 +1323,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} rightMargin=0.0 - The right margin, in meters. * @property {number} topMargin=0.0 - The top margin, in meters. * @property {number} bottomMargin=0.0 - The bottom margin, in meters. - * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit + * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit * by the key light and local lights. * @property {string} font="" - The font to render the text with. It can be one of the following: "Courier", * "Inconsolata", "Roboto", "Timeless", or a path to a .sdff file. @@ -1311,10 +1331,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Color} textEffectColor=255,255,255 - The color of the effect. * @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range 0.00.5. * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * @property {boolean} isFacingAvatar - true if billboardMode is "full", * false if it isn't. Setting this property to false sets the billboardMode to * "none". *

Deprecated: This property is deprecated and will be removed.

@@ -1332,41 +1352,41 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Web" {@link Entities.EntityType|EntityType} displays a browsable web page. Each user views their own copy - * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being - * played, users don't see it in sync. It has properties in addition to the common + * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being + * played, users don't see it in sync. It has properties in addition to the common * {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Web * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {string} sourceUrl="" - The URL of the web page to display. This value does not change as you or others navigate * on the Web entity. - * @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel - * colors on the web page are multiplied by the property color. For example, a value of + * @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel + * colors on the web page are multiplied by the property color. For example, a value of * { red: 255, green: 0, blue: 0 } lets only the red channel of pixels' colors through. * @property {number} alpha=1 - The opacity of the web surface. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * @property {boolean} isFacingAvatar - true if billboardMode is "full", * false if it isn't. Setting this property to false sets the billboardMode to * "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter - * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value + * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter + * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value * you get the resolution in pixels. * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the web page. * @property {number} maxFPS=10 - The maximum update rate for the web content, in frames/second. * @property {WebInputMode} inputMode="touch" - The user input mode to use. - * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard + * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard * focus, false if it isn't. * @example Create a Web entity displaying at 1920 x 1080 resolution. * var METERS_TO_INCHES = 39.3701; * var entity = Entities.addEntity({ * type: "Web", - * sourceUrl: "https://projectathena.io/", + * sourceUrl: "https://vircadia.com/", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), * rotation: MyAvatar.orientation, * dimensions: { @@ -1381,17 +1401,17 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Zone" {@link Entities.EntityType|EntityType} is a volume of lighting effects and avatar permissions. - * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. It has properties in + * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. It has properties in * addition to the common {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Zone - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the volume in which the zone's lighting effects and avatar + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the volume in which the zone's lighting effects and avatar * permissions have effect. * * @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar - * permissions have effect. Reverts to the default value if set to "none", or set to "compound" + * permissions have effect. Reverts to the default value if set to "none", or set to "compound" * and compoundShapeURL is "". - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is * "compound". * * @property {Entities.ComponentMode} keyLightMode="inherit" - Configures the key light in the zone. @@ -1409,14 +1429,14 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone. * @property {Entities.Bloom} bloom - The bloom properties of the zone. * - * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they + * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they * cannot. Only works for domain entities. - * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not - * collide with content in the zone; false if visitors will always collide with content in the zone. Only + * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not + * collide with content in the zone; false if visitors will always collide with content in the zone. Only * works for domain entities. * - * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the - * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to + * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the + * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to * certain properties: *
  * function filter(properties) {
@@ -1426,9 +1446,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
  * }
  * 
* - * @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the + * @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the * zone to other clients. * + * @property {Entities.ScreenshareMode} screenshare="inherit" - Configures a zone for screen-sharing. + * * @example Create a zone that casts a red key light along the x-axis. * var zone = Entities.addEntity({ * type: "Zone", @@ -1450,21 +1472,21 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @typedef {object} Entities.EntityProperties-Image * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {string} imageURL="" - The URL of the image to use. - * @property {boolean} emissive=false - true if the image should be emissive (unlit), false if it + * @property {boolean} emissive=false - true if the image should be emissive (unlit), false if it * shouldn't. - * @property {boolean} keepAspectRatio=true - true if the image should maintain its aspect ratio, + * @property {boolean} keepAspectRatio=true - true if the image should maintain its aspect ratio, * false if it shouldn't. - * @property {Rect} subImage=0,0,0,0 - The portion of the image to display. If width or height are 0, it defaults + * @property {Rect} subImage=0,0,0,0 - The portion of the image to display. If width or height are 0, it defaults * to the full image in that dimension. * @property {Color} color=255,255,255 - The color of the image. * @property {number} alpha=1 - The opacity of the image. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * @property {boolean} isFacingAvatar - true if billboardMode is "full", * false if it isn't. Setting this property to false sets the billboardMode to * "none". *

Deprecated: This property is deprecated and will be removed.

@@ -1583,6 +1605,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_RENDER_LAYER, renderLayer, getRenderLayerAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PRIMITIVE_MODE, primitiveMode, getPrimitiveModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RENDER_WITH_ZONES, renderWithZones); _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); // Physics @@ -1704,6 +1727,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); if (!psuedoPropertyFlagsButDesiredEmpty) { _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); } @@ -1779,6 +1803,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AVATAR_PRIORITY, avatarPriority, getAvatarPriorityAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SCREENSHARE, screenshare, getScreenshareAsString()); } // Web only @@ -1922,13 +1947,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool QScriptValue renderInfo = engine->newObject(); /**jsdoc - * Information on how an entity is rendered. Properties are only filled in for Model entities; other + * Information on how an entity is rendered. Properties are only filled in for Model entities; other * entity types have an empty object, {}. * @typedef {object} Entities.RenderInfo * @property {number} verticesCount - The number of vertices in the entity. * @property {number} texturesCount - The number of textures in the entity. * @property {number} texturesSize - The total size of the textures in the entity, in bytes. - * @property {boolean} hasTransparent - true if any of the textures has transparency, false + * @property {boolean} hasTransparent - true if any of the textures has transparency, false * if none of them do. * @property {number} drawCalls - The number of draw calls required to render the entity. */ @@ -2000,6 +2025,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(renderLayer, RenderLayer); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(primitiveMode, PrimitiveMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection); + COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones); _grab.copyFromScriptValue(object, _defaultSettings); // Physics @@ -2109,6 +2135,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations); COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints); COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled); + COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients); _animation.copyFromScriptValue(object, _defaultSettings); // Light @@ -2150,6 +2177,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(avatarPriority, AvatarPriority); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(screenshare, Screenshare); // Polyvox COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -2293,6 +2321,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(renderLayer); COPY_PROPERTY_IF_CHANGED(primitiveMode); COPY_PROPERTY_IF_CHANGED(ignorePickIntersection); + COPY_PROPERTY_IF_CHANGED(renderWithZones); _grab.merge(other._grab); // Physics @@ -2397,6 +2426,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(jointTranslations); COPY_PROPERTY_IF_CHANGED(relayParentJoints); COPY_PROPERTY_IF_CHANGED(groupCulled); + COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients); _animation.merge(other._animation); // Light @@ -2438,6 +2468,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(hazeMode); COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(avatarPriority); + COPY_PROPERTY_IF_CHANGED(screenshare); // Polyvox COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -2573,7 +2604,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DIMENSIONS, Dimensions, dimensions, vec3, ENTITY_ITEM_MIN_DIMENSION, FLT_MAX); ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, quat); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3, ENTITY_ITEM_MIN_REGISTRATION_POINT, ENTITY_ITEM_MAX_REGISTRATION_POINT); ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); ADD_PROPERTY_TO_MAP(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid); @@ -2585,6 +2616,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer); ADD_PROPERTY_TO_MAP(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode); ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool); + ADD_PROPERTY_TO_MAP(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector); { // Grab ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); @@ -2609,19 +2641,19 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr } // Physics - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float, ENTITY_ITEM_MIN_DENSITY, ENTITY_ITEM_MAX_DENSITY); ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, vec3); ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, vec3); ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, vec3); ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DAMPING, Damping, damping, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DAMPING, Damping, damping, float, ENTITY_ITEM_MIN_DAMPING, ENTITY_ITEM_MAX_DAMPING); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_MIN_DAMPING, ENTITY_ITEM_MAX_DAMPING); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RESTITUTION, Restitution, restitution, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RESTITUTION, Restitution, restitution, float, ENTITY_ITEM_MIN_RESTITUTION, ENTITY_ITEM_MAX_RESTITUTION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_FRICTION, Friction, friction, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_FRICTION, Friction, friction, float, ENTITY_ITEM_MIN_FRICTION, ENTITY_ITEM_MAX_FRICTION); ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); @@ -2665,7 +2697,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat); ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, vec3); ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, ENTITY_ITEM_MIN_DIMENSION, FLT_MAX); // Common @@ -2684,59 +2716,59 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); // Particles - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::MINIMUM_MAX_PARTICLES, particle::MAXIMUM_MAX_PARTICLES); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LIFESPAN, Lifespan, lifespan, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LIFESPAN, Lifespan, lifespan, float, particle::MINIMUM_LIFESPAN, particle::MAXIMUM_LIFESPAN); ADD_PROPERTY_TO_MAP(PROP_EMITTING_PARTICLES, IsEmitting, isEmitting, bool); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RATE, EmitRate, emitRate, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RATE, EmitRate, emitRate, float, particle::MINIMUM_EMIT_RATE, particle::MAXIMUM_EMIT_RATE); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3, particle::MINIMUM_EMIT_SPEED, particle::MAXIMUM_EMIT_SPEED); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3, particle::MINIMUM_EMIT_SPEED, particle::MAXIMUM_EMIT_SPEED); ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, quat); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3, particle::MINIMUM_EMIT_DIMENSION, particle::MAXIMUM_EMIT_DIMENSION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, particle::MINIMUM_EMIT_RADIUS_START, particle::MAXIMUM_EMIT_RADIUS_START); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_START, EmitPolarStart, polarStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_START, EmitPolarStart, polarStart, float, particle::MINIMUM_POLAR, particle::MAXIMUM_POLAR); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float, particle::MINIMUM_POLAR, particle::MAXIMUM_POLAR); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float, particle::MINIMUM_AZIMUTH, particle::MAXIMUM_AZIMUTH); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float, particle::MINIMUM_AZIMUTH, particle::MAXIMUM_AZIMUTH); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3, particle::MINIMUM_EMIT_ACCELERATION, particle::MAXIMUM_EMIT_ACCELERATION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3, particle::MINIMUM_ACCELERATION_SPREAD, particle::MAXIMUM_ACCELERATION_SPREAD); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_START, RadiusStart, radiusStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_START, RadiusStart, radiusStart, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, vec3Color); ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3Color); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_START, AlphaStart, alphaStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_START, SpinStart, spinStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_START, SpinStart, spinStart, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); ADD_PROPERTY_TO_MAP(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, float); @@ -2749,6 +2781,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool); ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool); + ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString); { // Animation ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); @@ -2765,7 +2798,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool); ADD_PROPERTY_TO_MAP(PROP_INTENSITY, Intensity, intensity, float); ADD_PROPERTY_TO_MAP(PROP_EXPONENT, Exponent, exponent, float); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_CUTOFF, Cutoff, cutoff, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::MIN_CUTOFF, LightEntityItem::MAX_CUTOFF); ADD_PROPERTY_TO_MAP(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float); @@ -2834,6 +2867,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t); // Polyvox ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); @@ -3073,6 +3107,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)properties.getRenderLayer()); APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)properties.getPrimitiveMode()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection()); + APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, properties.getRenderWithZones()); _staticGrab.setProperties(properties); _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3186,6 +3221,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations()); APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints()); APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled()); + APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients()); _staticAnimation.setProperties(properties); _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3252,6 +3288,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode()); APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, (uint32_t)properties.getAvatarPriority()); + APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, (uint32_t)properties.getScreenshare()); } if (properties.getType() == EntityTypes::PolyVox) { @@ -3562,6 +3599,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_LAYER, RenderLayer, setRenderLayer); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); // Physics @@ -3671,6 +3709,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); } @@ -3726,6 +3765,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCREENSHARE, uint32_t, setScreenshare); } if (properties.getType() == EntityTypes::PolyVox) { @@ -3936,7 +3976,7 @@ bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, in processedBytes = 0; if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) { - qCDebug(entities) << "EntityItemProperties::processEraseMessageDetails().... bailing because not enough bytes in buffer"; + qCDebug(entities) << "EntityItemProperties::decodeCloneEntityMessage().... bailing because not enough bytes in buffer"; return false; // bail to prevent buffer overflow } @@ -3979,6 +4019,7 @@ void EntityItemProperties::markAllChanged() { _renderLayerChanged = true; _primitiveModeChanged = true; _ignorePickIntersectionChanged = true; + _renderWithZonesChanged = true; _grab.markAllChanged(); // Physics @@ -4076,6 +4117,7 @@ void EntityItemProperties::markAllChanged() { _jointTranslationsChanged = true; _relayParentJointsChanged = true; _groupCulledChanged = true; + _blendshapeCoefficientsChanged = true; _animation.markAllChanged(); // Light @@ -4117,6 +4159,7 @@ void EntityItemProperties::markAllChanged() { _hazeModeChanged = true; _bloomModeChanged = true; _avatarPriorityChanged = true; + _screenshareChanged = true; // Polyvox _voxelVolumeSizeChanged = true; @@ -4381,6 +4424,9 @@ QList EntityItemProperties::listChangedProperties() { if (ignorePickIntersectionChanged()) { out += "ignorePickIntersection"; } + if (renderWithZonesChanged()) { + out += "renderWithZones"; + } getGrab().listChangedProperties(out); // Physics @@ -4640,6 +4686,9 @@ QList EntityItemProperties::listChangedProperties() { if (groupCulledChanged()) { out += "groupCulled"; } + if (blendshapeCoefficientsChanged()) { + out += "blendshapeCoefficients"; + } getAnimation().listChangedProperties(out); // Light @@ -4739,6 +4788,9 @@ QList EntityItemProperties::listChangedProperties() { if (avatarPriorityChanged()) { out += "avatarPriority"; } + if (screenshareChanged()) { + out += "screenshare"; + } // Polyvox if (voxelVolumeSizeChanged()) { @@ -5053,7 +5105,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte bool EntityItemProperties::verifyStaticCertificateProperties() { // True IFF a non-empty certificateID matches the static certificate json. - // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. + // I.e., if we can verify that the certificateID was produced by Vircadia signing the static certificate hash. return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8())); } @@ -5071,8 +5123,9 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setEntityHostType(entity::HostType::LOCAL); setCollisionless(true); } - setCreated(usecTimestampNow()); - setLastEdited(usecTimestampNow()); + uint64_t now = usecTimestampNow(); + setCreated(now); + setLastEdited(now); setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); @@ -5096,10 +5149,13 @@ bool EntityItemProperties::blobToProperties(QScriptEngine& scriptEngine, const Q return true; } -void EntityItemProperties::propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, QByteArray& blob) { +void EntityItemProperties::propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, + const EntityItemProperties& properties, QByteArray& blob, bool allProperties) { // DANGER: this method is NOT efficient. // begin recipe for extracting unfortunately-formatted-binary-blob from EntityItem - QScriptValue scriptValue = EntityItemNonDefaultPropertiesToScriptValue(&scriptEngine, properties); + QScriptValue scriptValue = allProperties + ? EntityItemPropertiesToScriptValue(&scriptEngine, properties) + : EntityItemNonDefaultPropertiesToScriptValue(&scriptEngine, properties); QVariant variantProperties = scriptValue.toVariant(); QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 7ee6742e7f..efc8b5dc33 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -118,7 +118,8 @@ class EntityItemProperties { friend class MaterialEntityItem; public: static bool blobToProperties(QScriptEngine& scriptEngine, const QByteArray& blob, EntityItemProperties& properties); - static void propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, QByteArray& blob); + static void propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, + QByteArray& blob, bool allProperties = false); EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); EntityItemProperties(const EntityItemProperties&) = default; @@ -194,6 +195,7 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer, RenderLayer::WORLD); DEFINE_PROPERTY_REF_ENUM(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode, PrimitiveMode::SOLID); DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false); + DEFINE_PROPERTY_REF(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector, QVector()); DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); // Physics @@ -298,6 +300,7 @@ public: DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false); + DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, ""); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); // Light @@ -339,6 +342,7 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); // Polyvox DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); @@ -701,6 +705,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, AvatarPriority, avatarPriority, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Screenshare, screenshare, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, ""); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 4c989ef74e..c25eb21e6c 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -119,6 +119,7 @@ inline QScriptValue qVectorVec3Color_convertScriptValue(QScriptEngine* e, const inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorQuatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorQUuidToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } @@ -216,6 +217,7 @@ typedef QVector qVectorVec3; typedef QVector qVectorQuat; typedef QVector qVectorBool; typedef QVector qVectorFloat; +typedef QVector qVectorQUuid; inline float float_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline quint64 quint64_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline quint32 quint32_convertFromScriptValue(const QScriptValue& v, bool& isValid) { @@ -293,6 +295,11 @@ inline qVectorBool qVectorBool_convertFromScriptValue(const QScriptValue& v, boo return qVectorBoolFromScriptValue(v); } +inline qVectorQUuid qVectorQUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + return qVectorQUuidFromScriptValue(v); +} + inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted QScriptValue x = v.property("x"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index d5af337a7d..a7359c0bca 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -43,6 +43,7 @@ enum EntityPropertyList { PROP_RENDER_LAYER, PROP_PRIMITIVE_MODE, PROP_IGNORE_PICK_INTERSECTION, + PROP_RENDER_WITH_ZONES, // Grab PROP_GRAB_GRABBABLE, PROP_GRAB_KINEMATIC, @@ -161,6 +162,7 @@ enum EntityPropertyList { PROP_DERIVED_31, PROP_DERIVED_32, PROP_DERIVED_33, + PROP_DERIVED_34, PROP_AFTER_LAST_ITEM, @@ -214,16 +216,17 @@ enum EntityPropertyList { PROP_JOINT_TRANSLATIONS = PROP_DERIVED_5, PROP_RELAY_PARENT_JOINTS = PROP_DERIVED_6, PROP_GROUP_CULLED = PROP_DERIVED_7, + PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8, // Animation - PROP_ANIMATION_URL = PROP_DERIVED_8, - PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_9, - PROP_ANIMATION_FPS = PROP_DERIVED_10, - PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_11, - PROP_ANIMATION_PLAYING = PROP_DERIVED_12, - PROP_ANIMATION_LOOP = PROP_DERIVED_13, - PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_14, - PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_15, - PROP_ANIMATION_HOLD = PROP_DERIVED_16, + PROP_ANIMATION_URL = PROP_DERIVED_9, + PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_10, + PROP_ANIMATION_FPS = PROP_DERIVED_11, + PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_12, + PROP_ANIMATION_PLAYING = PROP_DERIVED_13, + PROP_ANIMATION_LOOP = PROP_DERIVED_14, + PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_15, + PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_16, + PROP_ANIMATION_HOLD = PROP_DERIVED_17, // Light PROP_IS_SPOTLIGHT = PROP_DERIVED_0, @@ -290,6 +293,8 @@ enum EntityPropertyList { PROP_BLOOM_MODE = PROP_DERIVED_32, // Avatar priority PROP_AVATAR_PRIORITY = PROP_DERIVED_33, + // Screen-sharing + PROP_SCREENSHARE = PROP_DERIVED_34, // Polyvox PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0, diff --git a/libraries/entities/src/EntityScriptServerLogClient.h b/libraries/entities/src/EntityScriptServerLogClient.h index 22245e2a4e..6d3dec16e2 100644 --- a/libraries/entities/src/EntityScriptServerLogClient.h +++ b/libraries/entities/src/EntityScriptServerLogClient.h @@ -16,6 +16,16 @@ #include +/**jsdoc + * The EntityScriptServerLog API makes server log file output written by server entity scripts available to client + * scripts. + * + * @namespace EntityScriptServerLog + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + */ class EntityScriptServerLogClient : public QObject, public Dependency { Q_OBJECT @@ -23,6 +33,21 @@ public: EntityScriptServerLogClient(); signals: + + /**jsdoc + * Triggered when one or more lines are written to the server log by server entity scripts. + * @function EntityScriptServerLog.receivedNewLogLines + * @param {string} logLines - The server log lines written by server entity scripts. If there are multiple lines they are + * separated by "\n"s. + * @example Echo server entity script program log output to Interface's program log. + * EntityScriptServerLog.receivedNewLogLines.connect(function (logLines) { + * print("Log lines from server entity scripts:", logLines); + * }); + * @example A server entity script to test with. Copy the code into an entity's "Server Script" property. + * (function () { + * print("Hello from a server entity script!"); + * }) + */ void receivedNewLogLines(QString logLines); protected: diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3305d9ba00..fd83c99ca5 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -480,17 +480,11 @@ QUuid EntityScriptingInterface::addEntityInternal(const EntityItemProperties& pr _activityTracking.addedEntityCount++; - auto nodeList = DependencyManager::get(); - auto sessionID = nodeList->getSessionUUID(); - EntityItemProperties propertiesWithSimID = properties; propertiesWithSimID.setEntityHostType(entityHostType); if (entityHostType == entity::HostType::AVATAR) { - if (sessionID.isNull()) { - // null sessionID is unacceptable in this case - sessionID = AVATAR_SELF_ID; - } - propertiesWithSimID.setOwningAvatarID(sessionID); + // only allow adding our own avatar entities from script + propertiesWithSimID.setOwningAvatarID(AVATAR_SELF_ID); } else if (entityHostType == entity::HostType::LOCAL) { // For now, local entities are always collisionless // TODO: create a separate, local physics simulation that just handles local entities (and MyAvatar?) @@ -498,6 +492,8 @@ QUuid EntityScriptingInterface::addEntityInternal(const EntityItemProperties& pr } // the created time will be set in EntityTree::addEntity by recordCreationTime() + auto nodeList = DependencyManager::get(); + auto sessionID = nodeList->getSessionUUID(); propertiesWithSimID.setLastEditedBy(sessionID); bool scalesWithParent = propertiesWithSimID.getScalesWithParent(); @@ -805,7 +801,7 @@ QUuid EntityScriptingInterface::editEntity(const QUuid& id, const EntityItemProp return; } - if (entity->isAvatarEntity() && entity->getOwningAvatarID() != sessionID && entity->getOwningAvatarID() != AVATAR_SELF_ID) { + if (entity->isAvatarEntity() && !entity->isMyAvatarEntity()) { // don't edit other avatar's avatarEntities properties = EntityItemProperties(); return; @@ -825,7 +821,7 @@ QUuid EntityScriptingInterface::editEntity(const QUuid& id, const EntityItemProp // flag for simulation ownership, or upgrade existing ownership priority // (actual bids for simulation ownership are sent by the PhysicalEntitySimulation) entity->upgradeScriptSimulationPriority(properties.computeSimulationBidPriority()); - if (simulationOwner.getID() == sessionID) { + if (entity->isLocalEntity() || entity->isMyAvatarEntity() || simulationOwner.getID() == sessionID) { // we own the simulation --> copy ALL restricted properties properties.copySimulationRestrictedProperties(entity); } else { @@ -970,43 +966,43 @@ void EntityScriptingInterface::deleteEntity(const QUuid& id) { _activityTracking.deletedEntityCount++; - EntityItemID entityID(id); - bool shouldSendDeleteToServer = true; - - // If we have a local entity tree set, then also update it. - if (_entityTree) { - _entityTree->withWriteLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); - if (entity) { - - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); - if (entity->isAvatarEntity() && entity->getOwningAvatarID() != myNodeID) { - // don't delete other avatar's avatarEntities - shouldSendDeleteToServer = false; - return; - } - - if (entity->getLocked()) { - shouldSendDeleteToServer = false; - } else { - // only delete local entities, server entities will round trip through the server filters - if (!entity->isDomainEntity() || _entityTree->isServerlessMode()) { - shouldSendDeleteToServer = false; - _entityTree->deleteEntity(entityID); - - if (entity->isAvatarEntity() && getEntityPacketSender()->getMyAvatar()) { - getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entityID, false); - } - } - } - } - }); + if (!_entityTree) { + return; } - // if at this point, we know the id, and we should still delete the entity, send the update to the entity server - if (shouldSendDeleteToServer) { - getEntityPacketSender()->queueEraseEntityMessage(entityID); + EntityItemID entityID(id); + + // If we have a local entity tree set, then also update it. + std::vector entitiesToDeleteImmediately; + _entityTree->withWriteLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + if (entity) { + if (entity->isAvatarEntity() && !entity->isMyAvatarEntity()) { + // don't delete other avatar's avatarEntities + return; + } + if (entity->getLocked()) { + return; + } + + // Deleting an entity has consequences for linked children: some can be deleted but others can't. + // Local- and my-avatar-entities can be deleted immediately, but other-avatar-entities can't be deleted + // by this context, and a domain-entity must round trip through the entity-server for authorization. + if (entity->isDomainEntity() && !_entityTree->isServerlessMode()) { + getEntityPacketSender()->queueEraseEntityMessage(id); + } else { + entitiesToDeleteImmediately.push_back(entity); + const auto sessionID = DependencyManager::get()->getSessionUUID(); + entity->collectChildrenForDelete(entitiesToDeleteImmediately, sessionID); + _entityTree->deleteEntitiesByPointer(entitiesToDeleteImmediately); + } + } + }); + + for (auto entity : entitiesToDeleteImmediately) { + if (entity->isMyAvatarEntity()) { + getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entity->getID(), false); + } } } @@ -1653,12 +1649,9 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return false; } - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); - EntityItemPointer entity; bool doTransmit = false; - _entityTree->withWriteLock([this, &entity, entityID, myNodeID, &doTransmit, actor] { + _entityTree->withWriteLock([this, &entity, entityID, &doTransmit, actor] { EntitySimulationPointer simulation = _entityTree->getSimulation(); entity = _entityTree->findEntityByEntityItemID(entityID); if (!entity) { @@ -1671,7 +1664,7 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return; } - if (entity->isAvatarEntity() && entity->getOwningAvatarID() != myNodeID) { + if (entity->isAvatarEntity() && !entity->isMyAvatarEntity()) { return; } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index ae2c9ed746..dae0922f4a 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1871,7 +1871,8 @@ public slots: * @function Entities.getMeshes * @param {Uuid} entityID - The ID of the Model or PolyVox entity to get the meshes of. * @param {Entities~getMeshesCallback} callback - The function to call upon completion. - * @deprecated This function is deprecated and will be removed. Use the {@link Graphics} API instead. + * @deprecated This function is deprecated and will be removed. It no longer works for Model entities. Use the + * {@link Graphics} API instead. */ /**jsdoc * Called when a {@link Entities.getMeshes} call is complete. @@ -1880,7 +1881,8 @@ public slots: * Model or PolyVox entity; otherwise undefined. * @param {boolean} success - true if the {@link Entities.getMeshes} call was successful, false * otherwise. The call may be unsuccessful if the requested entity could not be found. - * @deprecated This function is deprecated and will be removed. Use the {@link Graphics} API instead. + * @deprecated This function is deprecated and will be removed. It no longer works for Model entities. Use the + * {@link Graphics} API instead. */ // FIXME move to a renderable entity interface Q_INVOKABLE void getMeshes(const QUuid& entityID, QScriptValue callback); @@ -2397,7 +2399,9 @@ signals: /**jsdoc - * Triggered when an avatar enters an entity, but only if the entity has an entity method exposed for this event. + * Triggered when an avatar enters an entity. + * Note: At the initial loading of the script, if the avatar is already present inside the entity, it might be too late + * to catch this event when the script runs, so it won't trigger. The {@link Entities.preload|preload} signal can be used to handle those cases. *

See also, {@link Entities|Entity Methods} and {@link Script.addEventHandler}.

* @function Entities.enterEntity * @param {Uuid} entityID - The ID of the entity that the avatar entered. @@ -2406,7 +2410,7 @@ signals: void enterEntity(const EntityItemID& entityItemID); /**jsdoc - * Triggered when an avatar leaves an entity, but only if the entity has an entity method exposed for this event. + * Triggered when an avatar leaves an entity. *

See also, {@link Entities|Entity Methods} and {@link Script.addEventHandler}.

* @function Entities.leaveEntity * @param {Uuid} entityID - The ID of the entity that the avatar left. diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 9f81572a4a..c7eb906efb 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -19,41 +19,37 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) { if (_entityTree && _entityTree != tree) { - _mortalEntities.clear(); - _nextExpiry = std::numeric_limits::max(); - _entitiesToUpdate.clear(); _entitiesToSort.clear(); _simpleKinematicEntities.clear(); + _changedEntities.clear(); + _entitiesToUpdate.clear(); + _mortalEntities.clear(); + _nextExpiry = std::numeric_limits::max(); } _entityTree = tree; } void EntitySimulation::updateEntities() { + PerformanceTimer perfTimer("EntitySimulation::updateEntities"); QMutexLocker lock(&_mutex); uint64_t now = usecTimestampNow(); - PerformanceTimer perfTimer("EntitySimulation::updateEntities"); // these methods may accumulate entries in _entitiesToBeDeleted expireMortalEntities(now); callUpdateOnEntitiesThatNeedIt(now); moveSimpleKinematics(now); - updateEntitiesInternal(now); sortEntitiesThatMoved(); + processDeadEntities(); } -void EntitySimulation::takeDeadEntities(SetOfEntities& entitiesToDelete) { - QMutexLocker lock(&_mutex); - entitiesToDelete.swap(_deadEntities); - _deadEntities.clear(); -} - -void EntitySimulation::removeEntityInternal(EntityItemPointer entity) { - // remove from all internal lists except _deadEntities - _mortalEntities.remove(entity); - _entitiesToUpdate.remove(entity); +void EntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) { + // protected: _mutex lock is guaranteed + // remove from all internal lists except _deadEntitiesToRemoveFromTree _entitiesToSort.remove(entity); _simpleKinematicEntities.remove(entity); _allEntities.remove(entity); + _entitiesToUpdate.remove(entity); + _mortalEntities.remove(entity); entity->setSimulated(false); } @@ -62,10 +58,9 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) { assert(entity->isDead()); if (entity->isSimulated()) { QMutexLocker lock(&_mutex); - entity->clearActions(getThisPointer()); - removeEntityInternal(entity); + removeEntityFromInternalLists(entity); if (entity->getElement()) { - _deadEntities.insert(entity); + _deadEntitiesToRemoveFromTree.insert(entity); _entityTree->cleanupCloneIDs(entity->getEntityItemID()); } } @@ -149,10 +144,8 @@ void EntitySimulation::sortEntitiesThatMoved() { _entitiesToSort.clear(); } -void EntitySimulation::addEntity(EntityItemPointer entity) { - QMutexLocker lock(&_mutex); - assert(entity); - entity->deserializeActions(); +void EntitySimulation::addEntityToInternalLists(EntityItemPointer entity) { + // protected: _mutex lock is guaranteed if (entity->isMortal()) { _mortalEntities.insert(entity); uint64_t expiry = entity->getExpiry(); @@ -163,10 +156,14 @@ void EntitySimulation::addEntity(EntityItemPointer entity) { if (entity->needsToCallUpdate()) { _entitiesToUpdate.insert(entity); } - addEntityInternal(entity); - _allEntities.insert(entity); entity->setSimulated(true); +} + +void EntitySimulation::addEntity(EntityItemPointer entity) { + QMutexLocker lock(&_mutex); + assert(entity); + addEntityToInternalLists(entity); // DirtyFlags are used to signal changes to entities that have already been added, // so we can clear them for this entity which has just been added. @@ -218,16 +215,14 @@ void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) { void EntitySimulation::clearEntities() { QMutexLocker lock(&_mutex); - _mortalEntities.clear(); - _nextExpiry = std::numeric_limits::max(); - _entitiesToUpdate.clear(); _entitiesToSort.clear(); _simpleKinematicEntities.clear(); - - clearEntitiesInternal(); - + _changedEntities.clear(); _allEntities.clear(); - _deadEntities.clear(); + _deadEntitiesToRemoveFromTree.clear(); + _entitiesToUpdate.clear(); + _mortalEntities.clear(); + _nextExpiry = std::numeric_limits::max(); } void EntitySimulation::moveSimpleKinematics(uint64_t now) { @@ -263,25 +258,19 @@ void EntitySimulation::moveSimpleKinematics(uint64_t now) { } } -void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) { - QMutexLocker lock(&_dynamicsMutex); - _dynamicsToAdd += dynamic; -} - -void EntitySimulation::removeDynamic(const QUuid dynamicID) { - QMutexLocker lock(&_dynamicsMutex); - _dynamicsToRemove += dynamicID; -} - -void EntitySimulation::removeDynamics(QList dynamicIDsToRemove) { - QMutexLocker lock(&_dynamicsMutex); - foreach(QUuid uuid, dynamicIDsToRemove) { - _dynamicsToRemove.insert(uuid); +void EntitySimulation::processDeadEntities() { + if (_deadEntitiesToRemoveFromTree.empty()) { + return; } -} - -void EntitySimulation::applyDynamicChanges() { - QMutexLocker lock(&_dynamicsMutex); - _dynamicsToAdd.clear(); - _dynamicsToRemove.clear(); + std::vector entitiesToDeleteImmediately; + entitiesToDeleteImmediately.reserve(_deadEntitiesToRemoveFromTree.size()); + QUuid nullSessionID; + foreach (auto entity, _deadEntitiesToRemoveFromTree) { + entitiesToDeleteImmediately.push_back(entity); + entity->collectChildrenForDelete(entitiesToDeleteImmediately, nullSessionID); + } + if (_entityTree) { + _entityTree->deleteEntitiesByPointer(entitiesToDeleteImmediately); + } + _deadEntitiesToRemoveFromTree.clear(); } diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index 1dd0369561..5b7b38e447 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -16,17 +16,14 @@ #include #include -#include #include #include -#include "EntityDynamicInterface.h" #include "EntityItem.h" #include "EntityTree.h" using EntitySimulationPointer = std::shared_ptr; -using SetOfEntities = QSet; using VectorOfEntities = QVector; // the EntitySimulation needs to know when these things change on an entity, @@ -47,8 +44,8 @@ const int DIRTY_SIMULATION_FLAGS = class EntitySimulation : public QObject, public std::enable_shared_from_this { public: - EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(std::numeric_limits::max()) { } - virtual ~EntitySimulation() { setEntityTree(NULL); } + EntitySimulation() : _mutex(QMutex::Recursive), _nextExpiry(std::numeric_limits::max()), _entityTree(nullptr) { } + virtual ~EntitySimulation() { setEntityTree(nullptr); } inline EntitySimulationPointer getThisPointer() const { return std::const_pointer_cast(shared_from_this()); @@ -57,12 +54,12 @@ public: /// \param tree pointer to EntityTree which is stored internally void setEntityTree(EntityTreePointer tree); - void updateEntities(); + virtual void updateEntities(); - virtual void addDynamic(EntityDynamicPointer dynamic); - virtual void removeDynamic(const QUuid dynamicID); - virtual void removeDynamics(QList dynamicIDsToRemove); - virtual void applyDynamicChanges(); + // FIXME: remove these + virtual void addDynamic(EntityDynamicPointer dynamic) {} + virtual void removeDynamic(const QUuid dynamicID) {} + virtual void applyDynamicChanges() {}; /// \param entity pointer to EntityItem to be added /// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked @@ -72,27 +69,22 @@ public: /// call this whenever an entity was changed from some EXTERNAL event (NOT by the EntitySimulation itself) void changeEntity(EntityItemPointer entity); - void clearEntities(); + virtual void clearEntities(); void moveSimpleKinematics(uint64_t now); EntityTreePointer getEntityTree() { return _entityTree; } - virtual void takeDeadEntities(SetOfEntities& entitiesToDelete); - - /// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others. virtual void prepareEntityForDelete(EntityItemPointer entity); void processChangedEntities(); + virtual void queueEraseDomainEntity(const QUuid& id) const { } protected: - // These pure virtual methods are protected because they are not to be called will-nilly. The base class - // calls them in the right places. - virtual void updateEntitiesInternal(uint64_t now) = 0; - virtual void addEntityInternal(EntityItemPointer entity) = 0; - virtual void removeEntityInternal(EntityItemPointer entity); + virtual void addEntityToInternalLists(EntityItemPointer entity); + virtual void removeEntityFromInternalLists(EntityItemPointer entity); virtual void processChangedEntity(const EntityItemPointer& entity); - virtual void clearEntitiesInternal() = 0; + virtual void processDeadEntities(); void expireMortalEntities(uint64_t now); void callUpdateOnEntitiesThatNeedIt(uint64_t now); @@ -102,27 +94,21 @@ protected: SetOfEntities _entitiesToSort; // entities moved by simulation (and might need resort in EntityTree) SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion - QList _dynamicsToAdd; - QSet _dynamicsToRemove; - QMutex _dynamicsMutex { QMutex::Recursive }; - -protected: - SetOfEntities _deadEntities; // dead entities that might still be in the _entityTree + SetOfEntities _deadEntitiesToRemoveFromTree; private: void moveSimpleKinematics(); - // back pointer to EntityTree structure - EntityTreePointer _entityTree; - // We maintain multiple lists, each for its distinct purpose. // An entity may be in more than one list. std::unordered_set _changedEntities; // all changes this frame SetOfEntities _allEntities; // tracks all entities added the simulation + SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update() SetOfEntities _mortalEntities; // entities that have an expiry uint64_t _nextExpiry; - SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update() + // back pointer to EntityTree structure + EntityTreePointer _entityTree; }; #endif // hifi_EntitySimulation_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index dbcfcb3d07..e6f5e36202 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -85,7 +85,7 @@ void EntityTree::eraseDomainAndNonOwnedEntities() { emit clearingEntities(); if (_simulation) { - // local entities are not in the simulation, so we clear ALL + // local-entities are not in the simulation, so we clear ALL _simulation->clearEntities(); } @@ -99,14 +99,15 @@ void EntityTree::eraseDomainAndNonOwnedEntities() { if (element) { element->cleanupDomainAndNonOwnedEntities(); } - - if (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID())) { - savedEntities[entity->getEntityItemID()] = entity; - } else { - int32_t spaceIndex = entity->getSpaceIndex(); - if (spaceIndex != -1) { - // stale spaceIndices will be freed later - _staleProxies.push_back(spaceIndex); + if (!getIsServer()) { + if (entity->isLocalEntity() || entity->isMyAvatarEntity()) { + savedEntities[entity->getEntityItemID()] = entity; + } else { + int32_t spaceIndex = entity->getSpaceIndex(); + if (spaceIndex != -1) { + // stale spaceIndices will be freed later + _staleProxies.push_back(spaceIndex); + } } } } @@ -122,7 +123,7 @@ void EntityTree::eraseDomainAndNonOwnedEntities() { foreach (EntityItemWeakPointer entityItem, _needsParentFixup) { auto entity = entityItem.lock(); - if (entity && (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID()))) { + if (entity && (entity->isLocalEntity() || entity->isMyAvatarEntity())) { needParentFixup.push_back(entityItem); } } @@ -145,10 +146,12 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) { if (element) { element->cleanupEntities(); } - int32_t spaceIndex = entity->getSpaceIndex(); - if (spaceIndex != -1) { - // assume stale spaceIndices will be freed later - _staleProxies.push_back(spaceIndex); + if (!getIsServer()) { + int32_t spaceIndex = entity->getSpaceIndex(); + if (spaceIndex != -1) { + // assume stale spaceIndices will be freed later + _staleProxies.push_back(spaceIndex); + } } } }); @@ -534,7 +537,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti return true; } -EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone) { +EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone, const bool isImport) { EntityItemProperties props = properties; auto nodeList = DependencyManager::get(); @@ -545,7 +548,8 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti if (properties.getEntityHostType() == entity::HostType::DOMAIN && getIsClient() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && - !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) { + !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && + !_serverlessDomain && !isClone && !isImport) { return nullptr; } @@ -606,61 +610,21 @@ void EntityTree::setSimulation(EntitySimulationPointer simulation) { } void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ignoreWarnings) { - EntityTreeElementPointer containingElement = getContainingElement(entityID); - if (!containingElement) { - if (!ignoreWarnings) { - qCWarning(entities) << "EntityTree::deleteEntity() on non-existent entityID=" << entityID; - } - return; - } - - EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID); - if (!existingEntity) { - if (!ignoreWarnings) { - qCWarning(entities) << "EntityTree::deleteEntity() on non-existant entity item with entityID=" << entityID; - } - return; - } - - if (existingEntity->getLocked() && !force) { - if (!ignoreWarnings) { - qCDebug(entities) << "ERROR! EntityTree::deleteEntity() trying to delete locked entity. entityID=" << entityID; - } - return; - } - - cleanupCloneIDs(entityID); - unhookChildAvatar(entityID); - emit deletingEntity(entityID); - emit deletingEntityPointer(existingEntity.get()); - - // NOTE: callers must lock the tree before using this method - DeleteEntityOperator theOperator(getThisPointer(), entityID); - - existingEntity->forEachDescendant([&](SpatiallyNestablePointer descendant) { - auto descendantID = descendant->getID(); - theOperator.addEntityIDToDeleteList(descendantID); - emit deletingEntity(descendantID); - EntityItemPointer descendantEntity = std::dynamic_pointer_cast(descendant); - if (descendantEntity) { - emit deletingEntityPointer(descendantEntity.get()); - } - }); - - recurseTreeWithOperator(&theOperator); - processRemovedEntities(theOperator); - _isDirty = true; + // NOTE: can be called without lock because deleteEntitiesByID() will lock + std::vector ids; + ids.push_back(entityID); + deleteEntitiesByID(ids, force, ignoreWarnings); } void EntityTree::unhookChildAvatar(const EntityItemID entityID) { - - EntityItemPointer entity = findEntityByEntityItemID(entityID); - - entity->forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Avatar) { - child->setParentID(nullptr); - } - }); + if (!getIsServer()) { + EntityItemPointer entity = findEntityByEntityItemID(entityID); + entity->forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Avatar) { + child->setParentID(nullptr); + } + }); + } } void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { @@ -685,39 +649,104 @@ void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { } } -void EntityTree::deleteEntities(QSet entityIDs, bool force, bool ignoreWarnings) { - // NOTE: callers must lock the tree before using this method +void EntityTree::recursivelyFilterAndCollectForDelete(const EntityItemPointer& entity, std::vector& entitiesToDelete, bool force) const { + // tree must be read-locked before calling this method + //TODO: assert(treeIsLocked); + assert(entity); + if (entity->getElement() && (std::find(entitiesToDelete.begin(), entitiesToDelete.end(), entity) == entitiesToDelete.end())) { + // filter + bool allowed = force; + if (!allowed) { + bool wasChanged = false; + auto startFilter = usecTimestampNow(); + EntityItemProperties dummyProperties; + allowed = filterProperties(entity, dummyProperties, dummyProperties, wasChanged, FilterType::Delete); + auto endFilter = usecTimestampNow(); + _totalFilterTime += endFilter - startFilter; + } + if (allowed) { + entitiesToDelete.push_back(entity); + for (SpatiallyNestablePointer child : entity->getChildren()) { + if (child && child->getNestableType() == NestableType::Entity) { + EntityItemPointer childEntity = std::static_pointer_cast(child); + recursivelyFilterAndCollectForDelete(childEntity, entitiesToDelete, force); + } + } + } + } +} + +void EntityTree::deleteEntitiesByID(const std::vector& ids, bool force, bool ignoreWarnings) { + // this method has two paths: + // (a) entity-server: applies delete filter + // (b) interface-client: deletes local- and my-avatar-entities immediately, submits domainEntity deletes to the entity-server + if (getIsServer()) { + withWriteLock([&] { + std::vector entitiesToDelete; + entitiesToDelete.reserve(ids.size()); + for (auto id : ids) { + EntityItemPointer entity; + { + QReadLocker locker(&_entityMapLock); + entity = _entityMap.value(id); + } + if (entity) { + recursivelyFilterAndCollectForDelete(entity, entitiesToDelete, force); + } + } + if (!entitiesToDelete.empty()) { + deleteEntitiesByPointer(entitiesToDelete); + } + }); + } else { + std::vector domainEntitiesIDs; + std::vector entitiesToDelete; + entitiesToDelete.reserve(ids.size()); + QUuid sessionID = DependencyManager::get()->getSessionUUID(); + withWriteLock([&] { + for (auto id : ids) { + EntityItemPointer entity; + { + QReadLocker locker(&_entityMapLock); + entity = _entityMap.value(id); + } + if (entity) { + if (entity->isDomainEntity()) { + // domain-entity deletes must round-trip through entity-server + domainEntitiesIDs.push_back(id); + } else if (force || entity->isLocalEntity() || entity->isMyAvatarEntity()) { + entitiesToDelete.push_back(entity); + entity->collectChildrenForDelete(entitiesToDelete, sessionID); + } + } + } + if (!entitiesToDelete.empty()) { + deleteEntitiesByPointer(entitiesToDelete); + } + }); + if (!domainEntitiesIDs.empty() && _simulation) { + for (auto id : domainEntitiesIDs) { + _simulation->queueEraseDomainEntity(id); + } + } + } +} + +void EntityTree::deleteEntitiesByPointer(const std::vector& entities) { + // tree must be write-locked before calling this method + //TODO: assert(treeIsLocked); + // NOTE: there is no entity validation (i.e. is entity in tree?) nor snarfing of children beyond this point. + // Get those done BEFORE calling this method. + for (auto entity : entities) { + cleanupCloneIDs(entity->getID()); + } DeleteEntityOperator theOperator(getThisPointer()); - foreach(const EntityItemID& entityID, entityIDs) { - EntityTreeElementPointer containingElement = getContainingElement(entityID); - if (!containingElement) { - if (!ignoreWarnings) { - qCWarning(entities) << "EntityTree::deleteEntities() on non-existent entityID=" << entityID; - } - continue; + for (auto entity : entities) { + if (entity->getElement()) { + theOperator.addEntityToDeleteList(entity); + emit deletingEntity(entity->getID()); + emit deletingEntityPointer(entity.get()); } - - EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID); - if (!existingEntity) { - if (!ignoreWarnings) { - qCWarning(entities) << "EntityTree::deleteEntities() on non-existent entity item with entityID=" << entityID; - } - continue; - } - - if (existingEntity->getLocked() && !force) { - if (!ignoreWarnings) { - qCDebug(entities) << "ERROR! EntityTree::deleteEntities() trying to delete locked entity. entityID=" << entityID; - } - continue; - } - - // tell our delete operator about this entityID - cleanupCloneIDs(entityID); - unhookChildAvatar(entityID); - theOperator.addEntityIDToDeleteList(entityID); - emit deletingEntity(entityID); - emit deletingEntityPointer(existingEntity.get()); } if (!theOperator.getEntities().empty()) { @@ -728,23 +757,11 @@ void EntityTree::deleteEntities(QSet entityIDs, bool force, bool i } void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) { + // NOTE: assume tree already write-locked because this method only called in deleteEntitiesByPointer() quint64 deletedAt = usecTimestampNow(); const RemovedEntities& entities = theOperator.getEntities(); foreach(const EntityToDeleteDetails& details, entities) { EntityItemPointer theEntity = details.entity; - - if (getIsServer()) { - QSet childrenIDs; - theEntity->forEachChild([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - childrenIDs += child->getID(); - } - }); - deleteEntities(childrenIDs, true, true); - } - - theEntity->die(); - if (getIsServer()) { removeCertifiedEntityOnServer(theEntity); @@ -752,19 +769,25 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); } else { + theEntity->forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Avatar) { + child->setParentID(nullptr); + } + }); + // on the client side, we also remember that we deleted this entity, we don't care about the time trackDeletedEntity(theEntity->getEntityItemID()); - } + int32_t spaceIndex = theEntity->getSpaceIndex(); + if (spaceIndex != -1) { + // stale spaceIndices will be freed later + _staleProxies.push_back(spaceIndex); + } + } if (theEntity->isSimulated()) { + theEntity->die(); _simulation->prepareEntityForDelete(theEntity); } - - int32_t spaceIndex = theEntity->getSpaceIndex(); - if (spaceIndex != -1) { - // stale spaceIndices will be freed later - _staleProxies.push_back(spaceIndex); - } } } @@ -1370,7 +1393,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList(); if (entityEditFilters) { @@ -1424,14 +1447,17 @@ void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { entityList << entityItemID; // adds to list within hash because entityList is a reference. qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID << "total" << entityList.size() << "entities."; } - // Delete an already-existing entity from the tree if it has the same + // Handle an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. if (!existingEntityItemID.isNull()) { qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" - << existingEntityItemID << ". Deleting existing entity."; - withWriteLock([&] { - deleteEntity(existingEntityItemID, true); - }); + << existingEntityItemID << ". No action will be taken to remove it."; + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(existingEntityItemID, true); + // }); } } @@ -1505,10 +1531,13 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove continue; } qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - withWriteLock([&] { - deleteEntity(entityID, true); - }); + << "doesn't match the current Domain ID" << thisDomainID << ". No action will be taken to remove it: " << entityID; + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(entityID, true); + // }); } { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); @@ -1533,10 +1562,13 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) } }); connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { - qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; - withWriteLock([&] { - deleteEntity(entityItemID, true); - }); + qCDebug(entities) << "Ownership challenge timed out for entity " << entityItemID << ". No action will be taken to remove it."; + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(entityItemID, true); + // }); if (_challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1628,10 +1660,13 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri QByteArray text = computeNonce(entityItemID, ownerKey); if (text == "") { - qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity..."; - withWriteLock([&] { - deleteEntity(entityItemID, true); - }); + qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. No action will be taken to remove this entity."; + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(entityItemID, true); + // }); } else { qCDebug(entities) << "Challenging ownership of Cert ID" << certID; // 2. Send the nonce to the rezzing avatar's node @@ -1702,15 +1737,21 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (networkReply->error() == QNetworkReply::NoError) { if (!jsonObject["invalid_reason"].toString().isEmpty()) { - qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; - withWriteLock([&] { - deleteEntity(entityItemID, true); - }); + qCDebug(entities) << "invalid_reason not empty, no action will be taken to delete entity" << entityItemID; + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(entityItemID, true); + // }); } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { - qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; - withWriteLock([&] { - deleteEntity(entityItemID, true); - }); + qCDebug(entities) << "'transfer_status' is 'failed', no action will be taken to delete entity" << entityItemID; + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(entityItemID, true); + // }); } else { // Second, challenge ownership of the PoP cert // (ignore pending status; a failure will be cleaned up during DDV) @@ -1720,11 +1761,14 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt senderNode); } } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; no action will be taken to delete entity" << entityItemID << "More info:" << jsonObject; - withWriteLock([&] { - deleteEntity(entityItemID, true); - }); + // FIXME: All certificate checking needs to be moved to its own files, + // then the deletion settings need to have a toggle for domain owners + // and a setting to change the verification service provider. + // withWriteLock([&] { + // deleteEntity(entityItemID, true); + // }); } networkReply->deleteLater(); @@ -1750,9 +1794,9 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const } } +// NOTE: Caller must lock the tree before calling this. int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { - if (!getIsServer()) { qCWarning(entities) << "EntityTree::processEditPacketData() should only be called on a server tree."; return 0; @@ -1922,6 +1966,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c FilterType filterType = isPhysics ? FilterType::Physics : (isAdd ? FilterType::Add : FilterType::Edit); bool allowed = (!isPhysics && senderNode->isAllowedEditor()) || filterProperties(existingEntity, properties, properties, wasChanged, filterType); if (!allowed) { + // the update failed and we need to convey that fact to the sender + // our method is to re-assert the current properties and bump the lastEdited timestamp auto timestamp = properties.getLastEdited(); properties = EntityItemProperties(); properties.setLastEdited(timestamp); @@ -2188,15 +2234,24 @@ void EntityTree::fixupNeedsParentFixups() { } entity->postParentFixup(); - } else if (getIsServer() || _avatarIDs.contains(entity->getParentID())) { - // this is a child of an avatar, which the entity server will never have - // a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves. - if (!_childrenOfAvatars.contains(entity->getParentID())) { - _childrenOfAvatars[entity->getParentID()] = QSet(); + } else { + bool needsUpdate = getIsServer(); + if (!needsUpdate) { + std::lock_guard lock(_avatarIDsLock); + needsUpdate = _avatarIDs.contains(entity->getParentID()); + } + + if (needsUpdate) { + std::lock_guard lock(_childrenOfAvatarsLock); + // this is a child of an avatar, which the entity server will never have + // a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves. + if (!_childrenOfAvatars.contains(entity->getParentID())) { + _childrenOfAvatars[entity->getParentID()] = QSet(); + } + _childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID(); + doMove = true; + iter.remove(); // and pull it out of the list } - _childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID(); - doMove = true; - iter.remove(); // and pull it out of the list } if (queryAACubeSuccess && doMove) { @@ -2216,17 +2271,40 @@ void EntityTree::fixupNeedsParentFixups() { } } -void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) { - if (_childrenOfAvatars.contains(avatarID)) { - deleteEntities(_childrenOfAvatars[avatarID]); - _childrenOfAvatars.remove(avatarID); +void EntityTree::knowAvatarID(const QUuid& avatarID) { + std::lock_guard lock(_avatarIDsLock); + _avatarIDs += avatarID; +} + +void EntityTree::forgetAvatarID(const QUuid& avatarID) { + std::lock_guard lock(_avatarIDsLock); + _avatarIDs -= avatarID; +} + +void EntityTree::deleteDescendantsOfAvatar(const QUuid& avatarID) { + std::lock_guard lock(_childrenOfAvatarsLock); + auto itr = _childrenOfAvatars.find(avatarID); + if (itr != _childrenOfAvatars.end()) { + if (!itr.value().empty()) { + std::vector ids; + ids.reserve(itr.value().size()); + for (const auto id : itr.value()) { + ids.push_back(id); + } + bool force = true; + bool ignoreWarnings = true; + deleteEntitiesByID(ids, force, ignoreWarnings); + } + _childrenOfAvatars.erase(itr); } } void EntityTree::removeFromChildrenOfAvatars(EntityItemPointer entity) { QUuid avatarID = entity->getParentID(); - if (_childrenOfAvatars.contains(avatarID)) { - _childrenOfAvatars[avatarID].remove(entity->getID()); + std::lock_guard lock(_childrenOfAvatarsLock); + auto itr = _childrenOfAvatars.find(avatarID); + if (itr != _childrenOfAvatars.end()) { + itr.value().remove(entity->getID()); } } @@ -2250,22 +2328,6 @@ void EntityTree::update(bool simulate) { if (simulate && _simulation) { withWriteLock([&] { _simulation->updateEntities(); - { - PROFILE_RANGE(simulation_physics, "Deletes"); - SetOfEntities deadEntities; - _simulation->takeDeadEntities(deadEntities); - if (!deadEntities.empty()) { - // translate into list of ID's - QSet idsToDelete; - - for (auto entity : deadEntities) { - idsToDelete.insert(entity->getEntityItemID()); - } - - // delete these things the roundabout way - deleteEntities(idsToDelete, true); - } - } }); } } @@ -2354,9 +2416,13 @@ bool EntityTree::shouldEraseEntity(EntityItemID entityID, const SharedNodePointe return allowed; } - -// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) { + // NOTE: this is only called by the interface-client on receipt of deleteEntity message from entity-server. + // Which means this is a state synchronization message from the the entity-server. It is saying + // "The following domain-entities have already been deleted". While need to perform sanity checking + // (e.g. verify these are domain entities) permissions need NOT checked for the domain-entities. + assert(!getIsServer()); + // TODO: remove this stuff out of EntityTree:: and into interface-client code. #ifdef EXTRA_ERASE_DEBUGGING qCDebug(entities) << "EntityTree::processEraseMessage()"; #endif @@ -2367,10 +2433,9 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo message.readPrimitive(&numberOfIDs); if (numberOfIDs > 0) { - QSet entityItemIDsToDelete; + QSet idsToDelete; for (size_t i = 0; i < numberOfIDs; i++) { - if (NUM_BYTES_RFC4122_UUID > message.getBytesLeftToRead()) { qCDebug(entities) << "EntityTree::processEraseMessage().... bailing because not enough bytes in buffer"; break; // bail to prevent buffer overflow @@ -2382,64 +2447,87 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo #endif EntityItemID entityItemID(entityID); + idsToDelete << entityItemID; + } - if (shouldEraseEntity(entityID, sourceNode)) { - entityItemIDsToDelete << entityItemID; - cleanupCloneIDs(entityItemID); + // domain-entity deletion can trigger deletion of other entities the entity-server doesn't know about + // so we must recurse down the children and collect consequential deletes however + // we must first identify all domain-entities in idsToDelete so as to not overstep entity-server's authority + std::vector domainEntities; + domainEntities.reserve(idsToDelete.size()); + for (auto id : idsToDelete) { + EntityItemPointer entity = _entityMap.value(id); + if (entity && entity->isDomainEntity()) { + domainEntities.push_back(entity); } } - deleteEntities(entityItemIDsToDelete, true, true); + // now we recurse domain-entities children and snarf consequential entities + // which are nomally just local-entities and myAvatar-entities + auto nodeList = DependencyManager::get(); + QUuid sessionID = nodeList->getSessionUUID(); + // NOTE: normally a null sessionID would be bad, as that would cause the collectDhildrenForDelete() method below + // to snarf domain-entities for which the interface-client is not authorized to delete without explicit instructions + // from the entity-server, however it is ok here because that would mean: + // (a) interface-client is not connected to a domain which means... + // (b) we should never get here (since this would correspond to a message from the entity-server) but... + // (c) who cares? When not connected to a domain the interface-client can do whatever it wants. + std::vector entitiesToDelete; + entitiesToDelete.reserve(domainEntities.size()); + for (auto entity : domainEntities) { + entitiesToDelete.push_back(entity); + entity->collectChildrenForDelete(entitiesToDelete, sessionID); + } + + if (!entitiesToDelete.empty()) { + deleteEntitiesByPointer(entitiesToDelete); + } } }); return message.getPosition(); } // This version skips over the header -// NOTE: Caller must lock the tree before calling this. -// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() +// NOTE: Caller must write-lock the tree before calling this. int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { + // NOTE: this is called on entity-server when receiving a delete request from an interface-client or agent + //TODO: assert(treeIsLocked); + assert(getIsServer()); #ifdef EXTRA_ERASE_DEBUGGING qCDebug(entities) << "EntityTree::processEraseMessageDetails()"; #endif - const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); - const unsigned char* dataAt = packetData; size_t packetLength = dataByteArray.size(); size_t processedBytes = 0; uint16_t numberOfIds = 0; // placeholder for now - memcpy(&numberOfIds, dataAt, sizeof(numberOfIds)); - dataAt += sizeof(numberOfIds); + memcpy(&numberOfIds, dataByteArray.constData(), sizeof(numberOfIds)); processedBytes += sizeof(numberOfIds); if (numberOfIds > 0) { - QSet entityItemIDsToDelete; + std::vector ids; + ids.reserve(numberOfIds); + // extract ids from packet for (size_t i = 0; i < numberOfIds; i++) { - - if (processedBytes + NUM_BYTES_RFC4122_UUID > packetLength) { qCDebug(entities) << "EntityTree::processEraseMessageDetails().... bailing because not enough bytes in buffer"; break; // bail to prevent buffer overflow } QByteArray encodedID = dataByteArray.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); - QUuid entityID = QUuid::fromRfc4122(encodedID); - dataAt += encodedID.size(); + QUuid id = QUuid::fromRfc4122(encodedID); processedBytes += encodedID.size(); #ifdef EXTRA_ERASE_DEBUGGING - qCDebug(entities) << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID; + qCDebug(entities) << " ---- EntityTree::processEraseMessageDetails() contains id:" << id; #endif - EntityItemID entityItemID(entityID); - - if (shouldEraseEntity(entityID, sourceNode)) { - entityItemIDsToDelete << entityItemID; - cleanupCloneIDs(entityItemID); - } - + EntityItemID entityID(id); + ids.push_back(entityID); } - deleteEntities(entityItemIDsToDelete, true, true); + + bool force = sourceNode->isAllowedEditor(); + bool ignoreWarnings = true; + deleteEntitiesByID(ids, force, ignoreWarnings); } return (int)processedBytes; } @@ -2592,11 +2680,12 @@ QByteArray EntityTree::remapActionDataIDs(QByteArray actionData, QHash EntityTree::sendEntities(EntityEditPacketSender* packetSender, EntityTreePointer localTree, - float x, float y, float z) { + const QString& entityHostType, float x, float y, float z) { SendEntitiesOperationArgs args; args.ourTree = this; args.otherTree = localTree; args.root = glm::vec3(x, y, z); + args.entityHostType = entityHostType; // If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we // use new identifiers. We need to keep a map so that we can map parent identifiers correctly. QHash map; @@ -2686,6 +2775,11 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void EntityItemID newID = getMapped(oldID); EntityItemProperties properties = item->getProperties(); + properties.setEntityHostTypeFromString(args->entityHostType); + if (properties.getEntityHostType() == entity::HostType::AVATAR) { + properties.setOwningAvatarID(AVATAR_SELF_ID); + } + EntityItemID oldParentID = properties.getParentID(); if (oldParentID.isInvalidID()) { // no parent properties.setPosition(properties.getPosition() + args->root); @@ -2699,6 +2793,17 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void } } + QVector oldRenderWithZones = properties.getRenderWithZones(); + if (!oldRenderWithZones.isEmpty()) { + QVector newRenderWithZones; + for (QUuid oldRenderWithZoneID : oldRenderWithZones) { + if (args->ourTree->findEntityByEntityItemID(oldRenderWithZoneID)) { + newRenderWithZones.append(getMapped(oldRenderWithZoneID)); + } + } + properties.setRenderWithZones(newRenderWithZones); + } + properties.setXNNeighborID(getMapped(properties.getXNNeighborID())); properties.setXPNeighborID(getMapped(properties.getXPNeighborID())); properties.setYNNeighborID(getMapped(properties.getYNNeighborID())); @@ -2861,7 +2966,7 @@ void convertGrabUserDataToProperties(EntityItemProperties& properties) { } -bool EntityTree::readFromMap(QVariantMap& map) { +bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { // These are needed to deal with older content (before adding inheritance modes) int contentVersion = map["Version"].toInt(); @@ -3031,7 +3136,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { } } - EntityItemPointer entity = addEntity(entityItemID, properties); + EntityItemPointer entity = addEntity(entityItemID, properties, isImport); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); success = false; @@ -3150,21 +3255,30 @@ glm::vec3 EntityTree::getUnscaledDimensionsForID(const QUuid& id) { return glm::vec3(1.0f); } -void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, +AACube EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { + glm::vec3 min(FLT_MAX); + glm::vec3 max(-FLT_MAX); + // if the queryBox has changed, tell the entity-server EntityItemPointer entity = std::dynamic_pointer_cast(object); if (entity) { bool queryAACubeChanged = false; if (!entity->hasChildren()) { - // updateQueryAACube will also update all ancestors' AACubes, so we only need to call this for leaf nodes - queryAACubeChanged = entity->updateQueryAACube(); + queryAACubeChanged = entity->updateQueryAACube(false); + AACube entityAACube = entity->getQueryAACube(); + min = glm::min(min, entityAACube.getMinimumPoint()); + max = glm::max(max, entityAACube.getMaximumPoint()); } else { - AACube oldCube = entity->getQueryAACube(); object->forEachChild([&](SpatiallyNestablePointer descendant) { - updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + AACube entityAACube = updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + min = glm::min(min, entityAACube.getMinimumPoint()); + max = glm::max(max, entityAACube.getMaximumPoint()); }); - queryAACubeChanged = oldCube != entity->getQueryAACube(); + queryAACubeChanged = entity->updateQueryAACubeWithDescendantAACube(AACube(Extents(min, max)), false); + AACube newCube = entity->getQueryAACube(); + min = glm::min(min, newCube.getMinimumPoint()); + max = glm::max(max, newCube.getMaximumPoint()); } if (queryAACubeChanged || force) { @@ -3173,9 +3287,10 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, if (success) { moveOperator.addEntityToMoveList(entity, newCube); } - // send an edit packet to update the entity-server about the queryAABox. We do this for domain-hosted - // entities as well as for avatar-entities; the packet-sender will route the update accordingly - if (tellServer && packetSender && (entity->isDomainEntity() || entity->isAvatarEntity())) { + // send an edit packet to update the entity-server about the queryAABox. We only do this for domain-hosted + // entities, as we don't want to flood the update pipeline with AvatarEntity updates, so we assume + // others have all info required to properly update queryAACube of AvatarEntities on their end + if (tellServer && packetSender && entity->isDomainEntity()) { quint64 now = usecTimestampNow(); EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); @@ -3190,7 +3305,16 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, entity->markDirtyFlags(Simulation::DIRTY_POSITION); entityChanged(entity); } + } else { + // if we're called on a non-entity, we might still have entity descendants + object->forEachChild([&](SpatiallyNestablePointer descendant) { + AACube entityAACube = updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + min = glm::min(min, entityAACube.getMinimumPoint()); + max = glm::max(max, entityAACube.getMaximumPoint()); + }); } + + return AACube(Extents(min, max)); } void EntityTree::updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index a0fcbd3244..66e761f7a0 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -40,6 +40,7 @@ public: class SendEntitiesOperationArgs { public: glm::vec3 root; + QString entityHostType; EntityTree* ourTree; EntityTreePointer otherTree; QHash* map; @@ -117,7 +118,7 @@ public: // The newer API... void postAddEntity(EntityItemPointer entityItem); - EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false); + EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false, const bool isImport = false); // use this method if you only know the entityID bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); @@ -126,7 +127,9 @@ public: void unhookChildAvatar(const EntityItemID entityID); void cleanupCloneIDs(const EntityItemID& entityID); void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true); - void deleteEntities(QSet entityIDs, bool force = false, bool ignoreWarnings = true); + + void deleteEntitiesByID(const std::vector& entityIDs, bool force = false, bool ignoreWarnings = true); + void deleteEntitiesByPointer(const std::vector& entities); EntityItemPointer findEntityByID(const QUuid& id) const; EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const; @@ -175,7 +178,7 @@ public: static QByteArray remapActionDataIDs(QByteArray actionData, QHash& map); QVector sendEntities(EntityEditPacketSender* packetSender, EntityTreePointer localTree, - float x, float y, float z); + const QString& entityHostType, float x, float y, float z); void entityChanged(EntityItemPointer entity); @@ -193,7 +196,7 @@ public: virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) override; - virtual bool readFromMap(QVariantMap& entityDescription) override; + virtual bool readFromMap(QVariantMap& entityDescription, const bool isImport = false) override; virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) override; @@ -237,9 +240,9 @@ public: Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name) const; Q_INVOKABLE QStringList getJointNames(const QUuid& entityID) const; - void knowAvatarID(QUuid avatarID) { _avatarIDs += avatarID; } - void forgetAvatarID(QUuid avatarID) { _avatarIDs -= avatarID; } - void deleteDescendantsOfAvatar(QUuid avatarID); + void knowAvatarID(const QUuid& avatarID); + void forgetAvatarID(const QUuid& avatarID); + void deleteDescendantsOfAvatar(const QUuid& avatarID); void removeFromChildrenOfAvatars(EntityItemPointer entity); void addToNeedsParentFixupList(EntityItemPointer entity); @@ -294,6 +297,7 @@ signals: protected: + void recursivelyFilterAndCollectForDelete(const EntityItemPointer& entity, std::vector& entitiesToDelete, bool force) const; void processRemovedEntities(const DeleteEntityOperator& theOperator); bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); @@ -342,12 +346,12 @@ protected: int _totalEditMessages = 0; int _totalUpdates = 0; int _totalCreates = 0; - quint64 _totalDecodeTime = 0; - quint64 _totalLookupTime = 0; - quint64 _totalUpdateTime = 0; - quint64 _totalCreateTime = 0; - quint64 _totalLoggingTime = 0; - quint64 _totalFilterTime = 0; + mutable quint64 _totalDecodeTime = 0; + mutable quint64 _totalLookupTime = 0; + mutable quint64 _totalUpdateTime = 0; + mutable quint64 _totalCreateTime = 0; + mutable quint64 _totalLoggingTime = 0; + mutable quint64 _totalFilterTime = 0; // these performance statistics are only used in the client void resetClientEditStats(); @@ -361,13 +365,15 @@ protected: QVector _needsParentFixup; // entites with a parentID but no (yet) known parent instance mutable QReadWriteLock _needsParentFixupLock; + std::mutex _avatarIDsLock; // we maintain a list of avatarIDs to notice when an entity is a child of one. QSet _avatarIDs; // IDs of avatars connected to entity server + std::mutex _childrenOfAvatarsLock; QHash> _childrenOfAvatars; // which entities are children of which avatars float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME }; - bool filterProperties(EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType); + bool filterProperties(const EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) const; bool _hasEntityEditFilter{ false }; QStringList _entityScriptSourceWhitelist; @@ -397,8 +403,9 @@ private: std::map _namedPaths; - void updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, - MovingEntitiesOperator& moveOperator, bool force, bool tellServer); + // Return an AACube containing object and all its entity descendants + AACube updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, + MovingEntitiesOperator& moveOperator, bool force, bool tellServer); }; void convertGrabUserDataToProperties(EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 60eaafc0dd..9af0bbfdb6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -197,7 +197,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori // only called if we do intersect our bounding cube, but find if we actually intersect with entities... EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { - if (entity->getIgnorePickIntersection()) { + if (entity->getIgnorePickIntersection() && !searchFilter.bypassIgnore()) { return; } @@ -341,7 +341,7 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 // only called if we do intersect our bounding cube, but find if we actually intersect with entities... EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { - if (entity->getIgnorePickIntersection()) { + if (entity->getIgnorePickIntersection() && !searchFilter.bypassIgnore()) { return; } @@ -705,7 +705,7 @@ void EntityTreeElement::cleanupDomainAndNonOwnedEntities() { withWriteLock([&] { EntityItems savedEntities; foreach(EntityItemPointer entity, _entityItems) { - if (!(entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { + if (!(entity->isLocalEntity() || entity->isMyAvatarEntity())) { entity->preDelete(); entity->_element = NULL; } else { diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 0b6e62e89e..3808844a6f 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -33,7 +33,8 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E return entity; } -ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) +ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID), + _blendshapeCoefficientsVector((int)Blendshapes::BlendshapeCount, 0.0f) { _lastAnimated = usecTimestampNow(); // set the last animated when interface (re)starts @@ -71,6 +72,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations); COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints); COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients); withReadLock([&] { _animationProperties.getProperties(properties); }); @@ -94,6 +96,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations); SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints); SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients); withWriteLock([&] { AnimationPropertyGroup animationProperties = _animationProperties; @@ -138,6 +141,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled); + READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); // grab a local copy of _animationProperties to avoid multiple locks int bytesFromAnimation; @@ -176,6 +180,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_JOINT_TRANSLATIONS; requestedProperties += PROP_RELAY_PARENT_JOINTS; requestedProperties += PROP_GROUP_CULLED; + requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS; requestedProperties += _animationProperties.getEntityProperties(params); return requestedProperties; @@ -204,6 +209,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations()); APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints()); APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled()); + APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients()); withReadLock([&] { _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -256,6 +262,7 @@ void ModelEntityItem::debugDump() const { qCDebug(entities) << " dimensions:" << getScaledDimensions(); qCDebug(entities) << " model URL:" << getModelURL(); qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); + qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients(); } void ModelEntityItem::setShapeType(ShapeType type) { @@ -743,3 +750,39 @@ void ModelEntityItem::setModelScale(const glm::vec3& modelScale) { _modelScale = modelScale; }); } + +QString ModelEntityItem::getBlendshapeCoefficients() const { + return resultWithReadLock([&] { + return QJsonDocument::fromVariant(_blendshapeCoefficientsMap).toJson(); + }); +} + +void ModelEntityItem::setBlendshapeCoefficients(const QString& blendshapeCoefficients) { + QJsonParseError error; + QJsonDocument newCoefficientsJSON = QJsonDocument::fromJson(blendshapeCoefficients.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "Could not evaluate blendshapeCoefficients property value:" << newCoefficientsJSON; + return; + } + + QVariantMap newCoefficientsMap = newCoefficientsJSON.toVariant().toMap(); + withWriteLock([&] { + for (auto& blendshape : newCoefficientsMap.keys()) { + auto newCoefficient = newCoefficientsMap[blendshape]; + auto blendshapeIter = BLENDSHAPE_LOOKUP_MAP.find(blendshape); + if (newCoefficient.canConvert() && blendshapeIter != BLENDSHAPE_LOOKUP_MAP.end()) { + float newCoefficientValue = newCoefficient.toFloat(); + _blendshapeCoefficientsVector[blendshapeIter.value()] = newCoefficientValue; + _blendshapeCoefficientsMap[blendshape] = newCoefficientValue; + _blendshapesChanged = true; + } + } + }); +} + +QVector ModelEntityItem::getBlendshapeCoefficientVector() { + return resultWithReadLock>([&] { + _blendshapesChanged = false; // ok to change this within read lock here + return _blendshapeCoefficientsVector; + }); +} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 75a695f1c0..c331a94adf 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -17,6 +17,7 @@ #include #include "AnimationPropertyGroup.h" +#include "BlendshapeConstants.h" class ModelEntityItem : public EntityItem { public: @@ -133,6 +134,11 @@ public: glm::vec3 getModelScale() const; void setModelScale(const glm::vec3& modelScale); + QString getBlendshapeCoefficients() const; + void setBlendshapeCoefficients(const QString& blendshapeCoefficients); + bool blendshapesChanged() const { return _blendshapesChanged; } + QVector getBlendshapeCoefficientVector(); + private: void setAnimationSettings(const QString& value); // only called for old bitstream format bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); @@ -166,6 +172,7 @@ protected: QString _modelURL; bool _relayParentJoints; bool _groupCulled { false }; + QVariantMap _blendshapeCoefficientsMap; ThreadSafeValueCache _compoundShapeURL; @@ -178,6 +185,9 @@ protected: private: uint64_t _lastAnimated{ 0 }; float _currentFrame{ -1.0f }; + + QVector _blendshapeCoefficientsVector; + bool _blendshapesChanged { false }; }; #endif // hifi_ModelEntityItem_h diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index b8e3df2d03..d64efdf87f 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -47,14 +47,17 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) { } } -void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) { +void SimpleEntitySimulation::updateEntities() { + EntitySimulation::updateEntities(); + QMutexLocker lock(&_mutex); + uint64_t now = usecTimestampNow(); expireStaleOwnerships(now); stopOwnerlessEntities(now); } -void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { +void SimpleEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) { + EntitySimulation::addEntityToInternalLists(entity); if (entity->getSimulatorID().isNull()) { - QMutexLocker lock(&_mutex); if (entity->getDynamic()) { // we don't allow dynamic objects to move without an owner so nothing to do here } else if (entity->isMovingRelativeToParent()) { @@ -65,7 +68,6 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { } } } else { - QMutexLocker lock(&_mutex); _entitiesWithSimulationOwner.insert(entity); _nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry()); @@ -79,10 +81,10 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { } } -void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { - EntitySimulation::removeEntityInternal(entity); +void SimpleEntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) { _entitiesWithSimulationOwner.remove(entity); _entitiesThatNeedSimulationOwner.remove(entity); + EntitySimulation::removeEntityFromInternalLists(entity); } void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entity) { @@ -135,10 +137,11 @@ void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entit entity->clearDirtyFlags(); } -void SimpleEntitySimulation::clearEntitiesInternal() { +void SimpleEntitySimulation::clearEntities() { QMutexLocker lock(&_mutex); _entitiesWithSimulationOwner.clear(); _entitiesThatNeedSimulationOwner.clear(); + EntitySimulation::clearEntities(); } void SimpleEntitySimulation::sortEntitiesThatMoved() { diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 1b240a8bf0..e984b72ed4 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -23,16 +23,16 @@ using SimpleEntitySimulationPointer = std::shared_ptr; class SimpleEntitySimulation : public EntitySimulation { public: SimpleEntitySimulation() : EntitySimulation() { } - ~SimpleEntitySimulation() { clearEntitiesInternal(); } + ~SimpleEntitySimulation() { clearEntities(); } void clearOwnership(const QUuid& ownerID); + void clearEntities() override; + void updateEntities() override; protected: - void updateEntitiesInternal(uint64_t now) override; - void addEntityInternal(EntityItemPointer entity) override; - void removeEntityInternal(EntityItemPointer entity) override; + void addEntityToInternalLists(EntityItemPointer entity) override; + void removeEntityFromInternalLists(EntityItemPointer entity) override; void processChangedEntity(const EntityItemPointer& entity) override; - void clearEntitiesInternal() override; void sortEntitiesThatMoved() override; diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 0771d9ad54..898a6d4b98 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -71,6 +71,7 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(screenshare, getScreenshare); return properties; } @@ -118,6 +119,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare); somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged; @@ -194,6 +196,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); + READ_ENTITY_PROPERTY(PROP_SCREENSHARE, uint32_t, setScreenshare); return bytesRead; } @@ -214,6 +217,7 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_GHOSTING_ALLOWED; requestedProperties += PROP_FILTER_URL; requestedProperties += PROP_AVATAR_PRIORITY; + requestedProperties += PROP_SCREENSHARE; requestedProperties += PROP_KEY_LIGHT_MODE; requestedProperties += PROP_AMBIENT_LIGHT_MODE; @@ -260,6 +264,7 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority()); + APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, getScreenshare()); } void ZoneEntityItem::debugDump() const { @@ -471,9 +476,9 @@ bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const { static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority"; - // If set ignore only priority-inherit zones: + // If set match zones of interest to avatar mixer: if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY) && jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool() - && _avatarPriority != COMPONENT_MODE_INHERIT) { + && (_avatarPriority != COMPONENT_MODE_INHERIT || _screenshare != COMPONENT_MODE_INHERIT)) { return true; } diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 34ad47f095..8ec3ea12b4 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -102,6 +102,9 @@ public: uint32_t getAvatarPriority() const { return _avatarPriority; } void setAvatarPriority(uint32_t value) { _avatarPriority = value; } + uint32_t getScreenshare() const { return _screenshare; } + void setScreenshare(uint32_t value) { _screenshare = value; } + bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } @@ -156,6 +159,9 @@ protected: // Avatar-updates priority uint32_t _avatarPriority { COMPONENT_MODE_INHERIT }; + // Screen-sharing zone + uint32_t _screenshare { COMPONENT_MODE_INHERIT }; + // Dirty flags turn true when either keylight properties is changing values. bool _keyLightPropertiesChanged { false }; bool _ambientLightPropertiesChanged { false }; diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index 802db4b428..f74a960dc8 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -127,7 +127,7 @@ void appendIndex(MeshData& data, QVector& indices, int index, bool deduplic glm::vec4 color; - bool hasColors = (data.colors.size() > 1); + bool hasColors = (data.colors.size() > 0); if (hasColors) { int colorIndex = data.colorsByVertex ? vertexIndex : index; if (data.colorIndices.isEmpty()) { diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index b647fe2e7f..cd3b8d268c 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -17,6 +17,7 @@ #include #include +#include #include QVariantHash FSTReader::parseMapping(QIODevice* device) { @@ -253,7 +254,7 @@ QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(networkRequest); QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index e117e1f211..8234f2b17d 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -399,7 +399,7 @@ bool GLTFSerializer::addBuffer(const QJsonObject& object) { getIntVal(object, "byteLength", buffer.byteLength, buffer.defined); - if (_url.toString().endsWith("glb")) { + if (_url.path().endsWith("glb")) { if (!_glbBinary.isEmpty()) { buffer.blob = _glbBinary; } else { @@ -646,7 +646,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { hifi::ByteArray jsonChunk = data; - if (_url.toString().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) { + if (_url.path().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) { jsonChunk = setGLBChunks(data); } @@ -1055,6 +1055,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& int indicesAccessorIdx = primitive.indices; + if (indicesAccessorIdx > _file.accessors.size()) { + qWarning(modelformat) << "Indices accessor index is out of bounds for model " << _url; + continue; + } + GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx]; // Buffers @@ -1093,6 +1098,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& foreach(auto &key, keys) { int accessorIdx = primitive.attributes.values[key]; + if (accessorIdx > _file.accessors.size()) { + qWarning(modelformat) << "Accessor index is out of bounds for model " << _url; + continue; + } + GLTFAccessor& accessor = _file.accessors[accessorIdx]; if (key == "POSITION") { @@ -1239,6 +1249,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& int v2_index = (indices[n + 1] * 3); int v3_index = (indices[n + 2] * 3); + if (v1_index + 2 >= vertices.size() || v2_index + 2 >= vertices.size() || v3_index + 2 >= vertices.size()) { + qWarning(modelformat) << "Indices out of range for model " << _url; + break; + } + glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]); glm::vec3 v2 = glm::vec3(vertices[v2_index], vertices[v2_index + 1], vertices[v2_index + 2]); glm::vec3 v3 = glm::vec3(vertices[v3_index], vertices[v3_index + 1], vertices[v3_index + 2]); @@ -1333,7 +1348,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } if (validatedIndices.size() == 0) { - qWarning(modelformat) << "Indices out of range for model " << _url; + qWarning(modelformat) << "No valid indices for model " << _url; continue; } @@ -1744,8 +1759,8 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { hifi::URL textureUrl = _url.resolved(url); fbxtex.name = fname; fbxtex.filename = textureUrl.toEncoded(); - - if (_url.toString().endsWith("glb") && !_glbBinary.isEmpty()) { + + if (_url.path().endsWith("glb") && !_glbBinary.isEmpty()) { int bufferView = _file.images[texture.source].bufferView; GLTFBufferView& imagesBufferview = _file.bufferviews[bufferView]; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp index f4e81448cc..c9a5856a8d 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp @@ -15,7 +15,7 @@ #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f #define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f -#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)2048) #define MAX_RESOURCE_TEXTURES_PER_FRAME 2 #define NO_BUFFER_WORK_SLEEP_TIME_MS 2 #define THREADED_TEXTURE_BUFFERING 1 diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index acef5a5bd4..29f8079422 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -36,39 +36,6 @@ namespace scriptable { using ModelProviderPointer = std::shared_ptr; using WeakModelProviderPointer = std::weak_ptr; - /**jsdoc - * @typedef {object} Graphics.Material - * @property {string} name - * @property {string} model - * @property {number|string} opacity - * @property {number|string} roughness - * @property {number|string} metallic - * @property {number|string} scattering - * @property {boolean|string} unlit - * @propety {Vec3|string} emissive - * @propety {Vec3|string} albedo - * @property {string} emissiveMap - * @property {string} albedoMap - * @property {string} opacityMap - * @property {string} opacityMapMode - * @property {number|string} opacityCutoff - * @property {string} metallicMap - * @property {string} specularMap - * @property {string} roughnessMap - * @property {string} glossMap - * @property {string} normalMap - * @property {string} bumpMap - * @property {string} occlusionMap - * @property {string} lightMap - * @property {string} scatteringMap - * @property {Mat4|string} texCoordTransform0 - * @property {Mat4|string} texCoordTransform1 - * @property {string} lightmapParams - * @property {string} materialParams - * @property {string} cullFaceMode - * @property {boolean} defaultFallthrough - * @property {string} procedural - */ class ScriptableMaterial { public: ScriptableMaterial() {} @@ -110,9 +77,11 @@ namespace scriptable { }; /**jsdoc + * A material layer. * @typedef {object} Graphics.MaterialLayer - * @property {Graphics.Material} material - This layer's material. - * @property {number} priority - The priority of this layer. If multiple materials are applied to a mesh part, only the highest priority layer is used. + * @property {Graphics.Material} material - The layer's material. + * @property {number} priority - The priority of the layer. If multiple materials are applied to a mesh part, only the + * layer with the highest priority is applied, with materials of the same priority randomly assigned. */ class ScriptableMaterialLayer { public: @@ -138,8 +107,29 @@ namespace scriptable { ScriptableMeshBase(const ScriptableMeshBase& other, QObject* parent = nullptr) : QObject(parent) { *this = other; } ScriptableMeshBase& operator=(const ScriptableMeshBase& view); virtual ~ScriptableMeshBase(); + + /**jsdoc + * @function GraphicsMesh.getMeshPointer + * @deprecated This method is deprecated and will be removed. + * @returns {undefined} + */ + // scriptable::MeshPointer is not registered as a JavaScript type. Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return weakMesh.lock(); } + + /**jsdoc + * @function GraphicsMesh.getModelProviderPointer + * @deprecated This method is deprecated and will be removed. + * @returns {undefined} + */ + // scriptable::ModelProviderPointer is not registered as a JavaScript type. Q_INVOKABLE const scriptable::ModelProviderPointer getModelProviderPointer() const { return provider.lock(); } + + /**jsdoc + * @function GraphicsMesh.getModelBasePointer + * @deprecated This method is deprecated and will be removed. + * @returns {undefined} + */ + // scriptable::ScriptableModelBasePointer is not registered as a JavaScript type. Q_INVOKABLE const scriptable::ScriptableModelBasePointer getModelBasePointer() const { return model; } }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index c0a2d4bf25..a27eff9cbe 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -168,14 +168,15 @@ scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVar // in the future we want to support a formal C++ structure data type here instead /**jsdoc + * IFS (Indexed-Face Set) data defining a mesh. * @typedef {object} Graphics.IFSData - * @property {string} [name=""] - mesh name (useful for debugging / debug prints). - * @property {string} [topology=""] - * @property {number[]} indices - vertex indices to use for the mesh faces. - * @property {Vec3[]} vertices - vertex positions (model space) - * @property {Vec3[]} [normals=[]] - vertex normals (normalized) - * @property {Vec3[]} [colors=[]] - vertex colors (normalized) - * @property {Vec2[]} [texCoords0=[]] - vertex texture coordinates (normalized) + * @property {string} [name=""] - Mesh name. (Useful for debugging.) + * @property {Graphics.MeshTopology} topology - Element interpretation. Currently only triangles is supported. + * @property {number[]} indices - Vertex indices to use for the mesh faces, in tuples per the topology. + * @property {Vec3[]} positions - Vertex positions, in model coordinates. + * @property {Vec3[]} [normals=[]] - Vertex normals (normalized). + * @property {Vec3[]} [colors=[]] - Vertex colors (normalized). + * @property {Vec2[]} [texCoords0=[]] - Vertex texture coordinates (normalized). */ QString meshName = ifsMeshData.value("name").toString(); QString topologyName = ifsMeshData.value("topology").toString(); @@ -258,8 +259,8 @@ scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVar return scriptable::make_scriptowned(mesh, nullptr); } -QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::ScriptableModel& _in) { - const auto& in = _in.getConstMeshes(); +QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::ScriptableModelPointer& model) { + const auto& in = model->getConstMeshes(); if (in.size()) { QList meshes; foreach (auto meshProxy, in) { @@ -354,6 +355,120 @@ namespace scriptable { qScriptValueToSequence(array, result); } + /**jsdoc + * A material in a {@link GraphicsModel}. + * @typedef {object} Graphics.Material + * @property {string} name - The name of the material. + * @property {string} model - Different material models support different properties and rendering modes. Supported models + * are: "hifi_pbr" and "hifi_shader_simple". + * @property {Vec3|string} [albedo] - The albedo color. Component values are in the range 0.0 – + * 1.0. + * If "fallthrough" then it falls through to the material below. + * @property {number|string} [opacity] - The opacity, range 0.01.0. + * If "fallthrough" then it falls through to the material below. + * + * @property {number|string} [opacityCutoff] - The opacity cutoff threshold used to determine the opaque texels of the + * opacityMap when opacityMapMode is "OPACITY_MAP_MASK". Range 0.0 + * – 1.0. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {number|string} [roughness] - The roughness, range 0.01.0. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {number|string} [metallic] - The metallicness, range 0.01.0. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {number|string} [scattering] - The scattering, range 0.01.0. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {boolean|string} [unlit] - true if the material is unaffected by lighting, false if it + * it is lit by the key light and local lights. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {Vec3|string} [emissive] - The emissive color, i.e., the color that the material emits. Component values are + * in the range 0.01.0. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [albedoMap] - The URL of the albedo texture image. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [opacityMap] - The URL of the opacity texture image. + * "hifi_pbr" model only. + * @property {string} [opacityMapMode] - The mode defining the interpretation of the opacity map. Values can be: + *
    + *
  • "OPACITY_MAP_OPAQUE" for ignoring the opacity map information.
  • + *
  • "OPACITY_MAP_MASK" for using the opacityMap as a mask, where only the texel greater + * than opacityCutoff are visible and rendered opaque.
  • + *
  • "OPACITY_MAP_BLEND" for using the opacityMap for alpha blending the material surface + * with the background.
  • + *
+ * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [occlusionMap] - The URL of the occlusion texture image. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [lightMap] - The URL of the light map texture image. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [lightmapParams] - Parameters for controlling how lightMap is used. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + *

Currently not used.

+ * @property {string} [scatteringMap] - The URL of the scattering texture image. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [emissiveMap] - The URL of the emissive texture image. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {string} [metallicMap] - The URL of the metallic texture image. + * If "fallthrough" then it and specularMap fall through to the material below. + * Only use one of metallicMap and specularMap. + * "hifi_pbr" model only. + * @property {string} [specularMap] - The URL of the specular texture image. + * Only use one of metallicMap and specularMap. + * "hifi_pbr" model only. + * @property {string} [roughnessMap] - The URL of the roughness texture image. + * If "fallthrough" then it and glossMap fall through to the material below. + * Only use one of roughnessMap and glossMap. + * "hifi_pbr" model only. + * @property {string} [glossMap] - The URL of the gloss texture image. + * Only use one of roughnessMap and glossMap. + * "hifi_pbr" model only. + * @property {string} [normalMap] - The URL of the normal texture image. + * If "fallthrough" then it and bumpMap fall through to the material below. + * Only use one of normalMap and bumpMap. + * "hifi_pbr" model only. + * @property {string} [bumpMap] - The URL of the bump texture image. + * Only use one of normalMap and bumpMap. + * "hifi_pbr" model only. + * @property {string} [materialParams] - Parameters for controlling the material projection and repetition. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + *

Currently not used.

+ * @property {string} [cullFaceMode="CULL_BACK"] - Specifies Which faces of the geometry to render. Values can be: + *
    + *
  • "CULL_NONE" to render both sides of the geometry.
  • + *
  • "CULL_FRONT" to cull the front faces of the geometry.
  • + *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • + *
+ * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {Mat4|string} [texCoordTransform0] - The transform to use for all of the maps apart from + * occlusionMap and lightMap. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * @property {Mat4|string} [texCoordTransform1] - The transform to use for occlusionMap and + * lightMap. + * If "fallthrough" then it falls through to the material below. + * "hifi_pbr" model only. + * + * @property {string} procedural - The definition of a procedural shader material. + * "hifi_shader_simple" model only. + *

Currently not used.

+ * + * @property {boolean} defaultFallthrough - true if all properties fall through to the material below unless + * they are set, false if properties respect their individual fall-through settings. + */ QScriptValue scriptableMaterialToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMaterial &material) { QScriptValue obj = engine->newObject(); obj.setProperty("name", material.name); diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 808d3f221f..0f24ae8461 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -23,7 +23,10 @@ /**jsdoc - * The experimental Graphics API (experimental) lets you query and manage certain graphics-related structures (like underlying meshes and textures) from scripting. + * The Graphics API enables you to access and manipulate avatar, entity, and overlay models in the rendered scene. + * This includes getting mesh and material information for applying {@link Entities.EntityProperties-Material|Material} + * entities. + * * @namespace Graphics * * @hifi-interface @@ -40,44 +43,83 @@ public: public slots: /**jsdoc - * Returns a model reference object associated with the specified UUID ({@link EntityID} or {@link AvatarID}). - * + * Gets a handle to the model data used for displaying an avatar, 3D entity, or 3D overlay. + *

Note: The model data may be used for more than one instance of the item displayed in the scene.

* @function Graphics.getModel - * @param {UUID} entityID - The objectID of the model whose meshes are to be retrieved. - * @returns {Graphics.Model} the resulting Model object + * @param {UUID} id - The ID of the avatar, 3D entity, or 3D overlay. + * @returns {GraphicsModel} The model data for the avatar, entity, or overlay, as displayed. This includes the results of + * applying any {@link Entities.EntityProperties-Material|Material} entities to the item. + * @example Report some details of your avatar's model. + * var model = Graphics.getModel(MyAvatar.sessionUUID); + * var meshes = model.meshes; + * var numMeshparts = 0; + * for (var i = 0; i < meshes.length; i++) { + * numMeshparts += meshes[i].numParts; + * } + * + * print("Avatar:", MyAvatar.skeletonModelURL); + * print("Number of meshes:", model.numMeshes); + * print("Number of mesh parts:", numMeshparts); + * print("Material names: ", model.materialNames); + * print("Material layers:", Object.keys(model.materialLayers)); */ scriptable::ScriptableModelPointer getModel(const QUuid& uuid); /**jsdoc + * Updates the model for an avatar, 3D entity, or 3D overlay in the rendered scene. * @function Graphics.updateModel - * @param {Uuid} id - * @param {Graphics.Model} model - * @returns {boolean} + * @param {Uuid} id - The ID of the avatar, 3D entity, or 3D overlay to update. + * @param {GraphicsModel} model - The model to update the avatar, 3D entity, or 3D overlay with. + * @returns {boolean} true if the update was completed successfully, false if it wasn't. */ bool updateModel(const QUuid& uuid, const scriptable::ScriptableModelPointer& model); /**jsdoc + * Checks whether the model for an avatar, entity, or overlay can be updated in the rendered scene. Only avatars, + * "Model" entities and "model" overlays can have their meshes updated. * @function Graphics.canUpdateModel - * @param {Uuid} id - * @param {number} [meshIndex=-1] - * @param {number} [partNumber=-1] - * @returns {boolean} + * @param {Uuid} id - The ID of the avatar, entity, or overlay. + * @param {number} [meshIndex=-1] - Not used. + * @param {number} [partNumber=-1] - Not used. + * @returns {boolean} true if the model can be updated, false if it can't. + * @example Test whether different types of items can be updated. + * var modelEntityID = Entities.addEntity({ + * type: "Model", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(Camera.orientation, { x: -0.5, y: 0, z: -3 })), + * rotation: MyAvatar.orientation, + * modelURL: "https://apidocs.vircadia.dev/models/cowboy-hat.fbx", + * dimensions: { x: 0.8569, y: 0.3960, z: 1.0744 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * var shapeEntityID = Entities.addEntity({ + * type: "Shape", + * shape: "Cylinder", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(Camera.orientation, { x: 0.5, y: 0, z: -3 })), + * dimensions: { x: 0.4, y: 0.6, z: 0.4 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * print("Can update avatar:", Graphics.canUpdateModel(MyAvatar.sessionUUID)); // true + * print("Can update model entity:", Graphics.canUpdateModel(modelEntityID)); // true + * print("Can update shape entity:", Graphics.canUpdateModel(shapeEntityID)); // false + * }, 1000); // Wait for the entities to rez. */ bool canUpdateModel(const QUuid& uuid, int meshIndex = -1, int partNumber = -1); /**jsdoc + * Creates a new graphics model from meshes. * @function Graphics.newModel - * @param {Graphics.Mesh[]} meshes - * @returns {Graphics.Model} + * @param {GraphicsMesh[]} meshes - The meshes to include in the model. + * @returns {GraphicsModel} The new graphics model. */ scriptable::ScriptableModelPointer newModel(const scriptable::ScriptableMeshes& meshes); /**jsdoc - * Create a new Mesh / Mesh Part with the specified data buffers. - * + * Creates a new graphics mesh. * @function Graphics.newMesh - * @param {Graphics.IFSData} ifsMeshData Index-Faced Set (IFS) arrays used to create the new mesh. - * @returns {Graphics.Mesh} the resulting Mesh / Mesh Part object + * @param {Graphics.IFSData} ifsMeshData - Index-Faced Set (IFS) data defining the mesh. + * @returns {GraphicsMesh} The new graphics mesh. */ scriptable::ScriptableMeshPointer newMesh(const QVariantMap& ifsMeshData); @@ -89,11 +131,13 @@ public slots: #endif /**jsdoc + * Exports a model to OBJ format. * @function Graphics.exportModelToOBJ - * @param {Graphics.Model} model - * @returns {string} + * @param {GraphicsModel} model - The model to export. + * @returns {string} The OBJ format representation of the model. */ - QString exportModelToOBJ(const scriptable::ScriptableModel& in); + // FIXME: If you put the OBJ on the Asset Server and rez it, Interface keeps crashing until the model is removed. + QString exportModelToOBJ(const scriptable::ScriptableModelPointer& model); private: scriptable::ModelProviderPointer getModelProvider(const QUuid& uuid); diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp index da582b2d21..92dddc953e 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp @@ -37,6 +37,14 @@ QVariant toVariant(const Extents& box) { }; } +/**jsdoc + * The extents of a mesh. + * @typedef {object} Graphics.MeshExtents + * @property {Vec3} brn - The bottom right near (minimum axes values) corner of the enclosing box. + * @property {Vec3} tfl - The top far left (maximum axes values) corner of the enclosing box. + * @property {Vec3} center - The center of the enclosing box. + * @property {Vec3} dimensions - The dimensions of the enclosing box. + */ QVariant toVariant(const AABox& box) { return QVariantMap{ { "brn", glmVecToVariant(box.getCorner()) }, @@ -48,6 +56,16 @@ QVariant toVariant(const AABox& box) { }; } +/**jsdoc + * Details of a buffer element's format. + * @typedef {object} Graphics.BufferElementFormat + * @property {string} type - Type. + * @property {string} semantic - Semantic. + * @property {string} dimension - Dimension. + * @property {number} scalarCount - Scalar count. + * @property {number} byteSize - Byte size. + * @property {number} BYTES_PER_ELEMENT - Bytes per element. + */ QVariant toVariant(const gpu::Element& element) { return QVariantMap{ { "type", gpu::toString(element.getType()) }, diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 72d2adb48f..3de5119fa7 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -127,6 +127,16 @@ int scriptable::ScriptableMesh::getSlotNumber(const QString& attributeName) cons return -1; } +/**jsdoc + * Details of buffer's format. + * @typedef {object} Graphics.BufferFormat + * @property {number} slot - Slot. + * @property {number} length - Length. + * @property {number} byteLength - Byte length. + * @property {number} offset - Offset. + * @property {number} stride - Stride. + * @property {Graphics.BufferElementFormat} element - Element format. + */ QVariantMap scriptable::ScriptableMesh::getBufferFormats() const { QVariantMap result; for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { @@ -247,6 +257,13 @@ bool scriptable::ScriptableMesh::setVertexProperty(glm::uint32 vertexIndex, cons return buffer_helpers::setValue(bufferView, vertexIndex, value); } +/**jsdoc + * Called for each vertex when {@link GraphicsMesh.updateVertexAttributes} is called. + * @callback GraphicsMesh~forEachVertextCallback + * @param {Object} attributes - The attributes of the vertex. + * @param {number} index - The vertex index. + * @param {object} properties - The properties of the mesh, per {@link GraphicsMesh}. + */ glm::uint32 scriptable::ScriptableMesh::forEachVertex(QScriptValue _callback) { auto mesh = getMeshPointer(); if (!mesh) { @@ -275,7 +292,16 @@ glm::uint32 scriptable::ScriptableMesh::forEachVertex(QScriptValue _callback) { return numProcessed; } - +/**jsdoc + * Called for each vertex when {@link GraphicsMesh.updateVertexAttributes} is called. The value returned by the script function + * should be the modified attributes to update the vertex with, or false to not update the particular vertex. + * @callback GraphicsMesh~updateVertexAttributesCallback + * @param {Object} attributes - The attributes of the vertex. + * @param {number} index - The vertex index. + * @param {object} properties - The properties of the mesh, per {@link GraphicsMesh}. + * @returns {Object|boolean} The attribute values to update the vertex with, or + * false to not update the vertex. + */ glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(QScriptValue _callback) { auto mesh = getMeshPointer(); if (!mesh) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index dcb1c53759..0e7eecc03b 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -29,17 +29,41 @@ namespace scriptable { /**jsdoc - * @typedef {object} Graphics.Mesh - * @property {Graphics.MeshPart[]} parts - Array of submesh part references. - * @property {string[]} attributeNames - Vertex attribute names (color, normal, etc.) - * @property {number} numParts - The number of parts contained in the mesh. - * @property {number} numIndices - Total number of vertex indices in the mesh. - * @property {number} numVertices - Total number of vertices in the Mesh. - * @property {number} numAttributes - Number of currently defined vertex attributes. - * @property {boolean} valid - * @property {boolean} strong - * @property {object} extents - * @property {object} bufferFormats + * A handle to in-memory mesh data in a {@link GraphicsModel}. + * + *

Create using the {@link Graphics} API, {@link GraphicsModel.cloneModel}, or {@link GraphicsMesh.cloneMesh}.

+ * + * @class GraphicsMesh + * @hideconstructor + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {number} numParts - The number of mesh parts. + * Read-only. + * @property {GraphicsMeshPart[]} parts - The mesh parts. + * Read-only. + * @property {number} numIndices - The total number of vertex indices in the mesh. + * Read-only. + * @property {number} numVertices - The total number of vertices in the mesh. + * Read-only. + * @property {number} numAttributes - The number of vertex attributes. + * Read-only. + * @property {Graphics.BufferTypeName[]} attributeNames - The names of the vertex attributes. + * Read-only. + * @property {boolean} valid - true if the mesh is valid, false if it isn't. + * Read-only. + * @property {boolean} strong - true if the mesh is valid and able to be used, false if it isn't. + * Read-only. + * @property {Graphics.MeshExtents} extents - The mesh extents, in model coordinates. + * Read-only. + * @property {Object} bufferFormats - Details of the buffers used for the + * mesh. + * Read-only. + * + * @borrows GraphicsMesh.getVertextAttributes as getVertextAttributes + * @borrows GraphicsMesh.setVertextAttributes as setVertextAttributes */ class ScriptableMesh : public ScriptableMeshBase, QScriptable { Q_OBJECT @@ -82,26 +106,153 @@ namespace scriptable { int getSlotNumber(const QString& attributeName) const; public slots: + + /**jsdoc + * Gets the model the mesh is part of. + *

Currently doesn't work.

+ * @function GraphicsMesh.getParentModel + * @returns {GraphicsModel} The model the mesh is part of, null if it isn't part of a model. + */ const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } + + /**jsdoc + * Gets the vertex indices. + * @function GraphicsMesh.getIndices + * @returns {number[]} The vertex indices. + */ QVector getIndices() const; + + /**jsdoc + * Gets the indices of nearby vertices. + * @function GraphicsMesh.findNearbyVertexIndices + * @param {Vec3} origin - The search position, in model coordinates. + * @param {number} [epsilon=1e-6] - The search distance. If a vertex is within this distance from the + * origin it is considered to be "nearby". + * @returns {number[]} The indices of nearby vertices. + */ QVector findNearbyVertexIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + /**jsdoc + * Adds an attribute for all vertices. + * @function GraphicsMesh.addAttribute + * @param {Graphics.BufferTypeName} name - The name of the attribute. + * @param {Graphics.BufferType} [defaultValue] - The value to give the attributes added to the vertices. + * @returns {number} The number of vertices the attribute was added to, 0 if the name was + * invalid or all vertices already had the attribute. + */ glm::uint32 addAttribute(const QString& attributeName, const QVariant& defaultValue = QVariant()); + + /**jsdoc + * Sets the value of an attribute for all vertices. + * @function GraphicsMesh.fillAttribute + * @param {Graphics.BufferTypeName} name - The name of the attribute. The attribute is added to the vertices if not + * already present. + * @param {Graphics.BufferType} value - The value to give the attributes. + * @returns {number} 1 if the attribute name was valid and the attribute values were set, 0 + * otherwise. + */ glm::uint32 fillAttribute(const QString& attributeName, const QVariant& value); + + /**jsdoc + * Removes an attribute from all vertices. + *

Note: The "position" attribute cannot be removed.

+ * @function GraphicsMesh.removeAttribute + * @param {Graphics.BufferTypeName} name - The name of the attribute to remove. + * @returns {boolean} true if the attribute existed and was removed, false otherwise. + */ bool removeAttribute(const QString& attributeName); + /**jsdoc + * Gets the value of an attribute for all vertices. + * @function GraphicsMesh.queryVertexAttributes + * @param {Graphics.BufferTypeName} name - The name of the attribute to get the vertex values of. + * @throws Throws an error if the name is invalid or isn't used in the mesh. + * @returns {Graphics.BufferType[]} The attribute values for all vertices. + */ QVariantList queryVertexAttributes(QVariant selector) const; + + /**jsdoc + * Gets the attributes and attribute values of a vertex. + * @function GraphicsMesh.getVertexAttributes + * @param {number} index - The vertex to get the attributes for. + * @returns {Object} The attribute names and values for the vertex. + * @throws Throws an error if the index is invalid. + */ QVariantMap getVertexAttributes(glm::uint32 vertexIndex) const; + + /**jsdoc + * Updates attribute values of a vertex. + * @function GraphicsMesh.setVertexAttributes + * @param {number} index - The vertex to set the attributes for. + * @param {Object} values - The attribute names and values. Need not + * specify unchanged values. + * @returns {boolean} true if the index and the attribute names and values were valid and the vertex was + * updated, false otherwise. + * @throws Throws an error if the index is invalid or one of the attribute names is invalid or isn't used + * in the mesh. + */ + // @borrows jsdoc from GraphicsMesh bool setVertexAttributes(glm::uint32 vertexIndex, const QVariantMap& attributeValues); + /**jsdoc + * Gets the value of a vertex's attribute. + * @function GraphicsMesh.getVertexProperty + * @param {number} index - The vertex index. + * @param {Graphics.BufferTypeName} name - The name of the vertex attribute to get. + * @returns {Graphics.BufferType} The value of the vertex attribute. + * @throws Throws an error if the index is invalid or name is invalid or isn't used in the + * mesh. + */ QVariant getVertexProperty(glm::uint32 vertexIndex, const QString& attributeName) const; + + /**jsdoc + * Sets the value of a vertex's attribute. + * @function GraphicsMesh.setVertexProperty + * @param {number} index - The vertex index. + * @param {Graphics.BufferTypeName} name - The name of the vertex attribute to set. + * @param {Graphics.BufferType} value - The vertex attribute value to set. + * @returns {boolean} true if the vertex attribute value was set, false if it wasn't. + * @throws Throws an error if the index is invalid or name is invalid or isn't used in the + * mesh. + */ bool setVertexProperty(glm::uint32 vertexIndex, const QString& attributeName, const QVariant& value); + /**jsdoc + * Makes a copy of the mesh. + * @function GraphicsMesh.cloneMesh + * @returns {GraphicsMesh} A copy of the mesh. + */ scriptable::ScriptableMeshPointer cloneMesh(); // QScriptEngine-specific wrappers + + /**jsdoc + * Updates vertex attributes by calling a function for each vertex. The function can return modified attributes to + * update the vertex with. + * @function GraphicsMesh.updateVertexAttributes + * @param {GraphicsMesh~updateVertexAttributesCallback} callback - The function to call for each vertex. + * @returns {number} The number of vertices the callback was called for. + */ glm::uint32 updateVertexAttributes(QScriptValue callback); + + /**jsdoc + * Calls a function for each vertex. + * @function GraphicsMesh.forEachVertex + * @param {GraphicsMesh~forEachVertexCallback} callback - The function to call for each vertex. + * @returns {number} The number of vertices the callback was called for. + */ glm::uint32 forEachVertex(QScriptValue callback); + + /**jsdoc + * Checks if an index is valid and, optionally, that vertex has a particular attribute. + * @function GraphicsMesh.isValidIndex + * @param {number} index - The index to check. + * @param {Graphics.BufferTypeName} [attribute] - The attribute to check. + * @returns {boolean} true if the index is valid and that vertex has the attribute if specified. + * @throws Throws an error if the index if invalid or name is invalid or isn't used in the + * mesh. + */ + // FIXME: Should return false rather than throw an error. bool isValidIndex(glm::uint32 vertexIndex, const QString& attributeName = QString()) const; }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h index 7352fcd0f6..878b239f3d 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h @@ -11,23 +11,50 @@ namespace scriptable { /**jsdoc - * @typedef {object} Graphics.MeshPart - * @property {boolean} valid - * @property {number} partIndex - The part index (within the containing Mesh). - * @property {number} firstVertexIndex - * @property {number} baseVertexIndex - * @property {number} lastVertexIndex - * @property {Graphics.Topology} topology - element interpretation (currently only 'triangles' is supported). - * @property {string[]} attributeNames - Vertex attribute names (color, normal, etc.) - * @property {number} numIndices - Number of vertex indices that this mesh part refers to. - * @property {number} numVerticesPerFace - Number of vertices per face (eg: 3 when topology is 'triangles'). - * @property {number} numFaces - Number of faces represented by the mesh part (numIndices / numVerticesPerFace). - * @property {number} numVertices - Total number of vertices in the containing Mesh. - * @property {number} numAttributes - Number of currently defined vertex attributes. - * @property {object} extents - * @property {object} bufferFormats - */ + * A handle to in-memory mesh part data in a {@link GraphicsModel}. + * + *

Create using the {@link Graphics} API, {@link GraphicsModel.cloneModel}, {@link GraphicsMesh.cloneMesh}, or + * {@link GraphicsMeshPart.cloneMeshPart}.

+ * + * @class GraphicsMeshPart + * @hideconstructor + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {boolean} valid - true if the mesh part is valid, false if it isn't. + * Read-only. + * @property {number} partIndex - The index of the part within the whole mesh (i.e., parent and mesh parts). + * Read-only. + * @property {number} firstVertexIndex - The index of the first vertex. + * @property {number} baseVertexIndex - The index of the base vertex. + * @property {number} lastVertexIndex - The index of the last vertex. + * @property {Graphics.MeshTopology} topology - The element interpretation. Currently only triangles is supported. + * @property {number} numIndices - The number of vertex indices in the mesh part. + * @property {number} numVertices - The number of vertices in the whole mesh (i.e., parent and mesh parts). + * Read-only. + * @property {number} numVerticesPerFace - The number of vertices per face, per the topology (e.g., 3 for + * triangles). + * Read-only. + * @property {number} numFaces - The number of faces represented by the mesh part. + * Read-only. + * @property {number} numAttributes - The number of vertex attributes in the whole mesh (i.e., parent and mesh + * parts). + * Read-only. + * @property {Graphics.BufferTypeName[]} attributeNames - The names of the vertex attributes in the whole mesh + * (i.e., parent and mesh parts). + * Read-only. + * @property {Graphics.MeshExtents} extents - The mesh part extents, in model coordinates. + * Read-only. + * @property {Object} bufferFormats - Details of the buffers used for the + * whole mesh (i.e., parent and mesh parts). + * Read-only. + * @borrows GraphicsMesh.addAttribute as addAttribute + * @borrows GraphicsMesh.getVertexAttributes as getVertextAttributes + * @borrows GraphicsMesh.setVertexAttributes as setVertextAttributes + */ class ScriptableMeshPart : public QObject, QScriptable { Q_OBJECT Q_PROPERTY(bool valid READ isValid) @@ -55,42 +82,228 @@ namespace scriptable { bool isValid() const { auto mesh = getMeshPointer(); return mesh && partIndex < mesh->getNumParts(); } public slots: + + /**jsdoc + * Gets the vertex indices. + * @function GraphicsMeshPart.getIndices + * @returns {number[]} The vertex indices. + */ QVector getIndices() const; + + /**jsdoc + * Sets the vertex indices. + * @function GraphicsMeshPart.setIndices + * @param {number[]} indices - The vertex indices. + * @returns {boolean} true if successful, false if not. + * @throws Throws an error if the number of indices isn't the same, or an index is invalid. + */ bool setIndices(const QVector& indices); + + /**jsdoc + * Gets the indices of nearby vertices in the mesh part. + * @function GraphicsMeshPart.findNearbyPartVertexIndices + * @param {Vec3} origin - The search position, in model coordinates. + * @param {number} [epsilon=1e-6] - The search distance. If a vertex is within this distance from the + * origin it is considered to be "nearby". + * @returns {number[]} The indices of nearby vertices. + */ QVector findNearbyPartVertexIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + + /**jsdoc + * Gets the value of an attribute for all vertices in the whole mesh (i.e., parent and mesh parts). + * @function GraphicsMeshPArt.queryVertexAttributes + * @param {Graphics.BufferTypeName} name - The name of the attribute to get the vertex values of. + * @throws Throws an error if the name is invalid or isn't used in the mesh. + * @returns {Graphics.BufferType[]} The attribute values for all vertices. + */ QVariantList queryVertexAttributes(QVariant selector) const; + + // @borrows jsdoc from GraphicsMesh. QVariantMap getVertexAttributes(glm::uint32 vertexIndex) const; + + // @borrows jsdoc from GraphicsMesh. bool setVertexAttributes(glm::uint32 vertexIndex, const QVariantMap& attributeValues); + /**jsdoc + * Gets the value of a vertex's attribute. + * @function GraphicsMeshPart.getVertexProperty + * @param {number} index - The vertex index. + * @param {Graphics.BufferTypeName} name - The name of the vertex attribute to get. + * @returns {Graphics.BufferType} The value of the vertex attribute. + * @throws Throws an error if the index is invalid or name is invalid or isn't used in the + * mesh. + */ QVariant getVertexProperty(glm::uint32 vertexIndex, const QString& attributeName) const; + + /**jsdoc + * Sets the value of a vertex's attribute. + * @function GraphicsMeshPart.setVertexProperty + * @param {number} index - The vertex index. + * @param {Graphics.BufferTypeName} name - The name of the vertex attribute to set. + * @param {Graphics.BufferType} value - The vertex attribute value to set. + * @returns {boolean} true if the vertex attribute value was set, false if it wasn't. + * @throws Throws an error if the index is invalid or name is invalid or isn't used in the + * mesh. + */ bool setVertexProperty(glm::uint32 vertexIndex, const QString& attributeName, const QVariant& attributeValues); + /**jsdoc + * Gets the vertex indices that make up a face. + * @function GraphicsMeshPart.getFace + * @param {number} index - The index of the face. + * @returns {number[]} The vertex indices that make up the face, of number per the mesh topology. + */ QVector getFace(glm::uint32 faceIndex) const; + /**jsdoc + * Scales the mesh to so that it's maximum model coordinate dimension is a specified length. + * @function GraphicsMeshPart.scaleToFit + * @param {number} scale - The target dimension. + * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. + */ QVariantMap scaleToFit(float unitScale); + + /**jsdoc + * Translates the mesh part. + * @function GraphicsMeshPart.translate + * @param {Vec3} translation - The translation to apply, in model coordinates. + * @returns {Graphics.MeshExtents} The rseulting mesh extents, in model coordinates. + */ QVariantMap translate(const glm::vec3& translation); + + /**jsdoc + * Scales the mesh part. + * @function GraphicsMeshPart.scale + * @param {Vec3} scale - The scale to apply in each model coordinate direction. + * @param {Vec3} [origin] - The origin to scale about. If not specified, the center of the mesh part is used. + * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. + */ QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); + + /**jsdoc + * Rotates the mesh part, using Euler angles. + * @function GraphicsMeshPart.rotateDegrees + * @param {Vec3} eulerAngles - The rotation to perform, in mesh coordinates, as Euler angles in degrees. + * @param {Vec3} [origin] - The point about which to rotate, in model coordinates. + *

Warning: Currently doesn't work as expected.

+ * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. + */ QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); + + /**jsdoc + * Rotates the mesh part, using a quaternion. + * @function GraphicsMeshPart.rotate + * @param {Quat} rotation - The rotation to perform, in model coordinates. + * @param {Vec3} [origin] - The point about which to rotate, in model coordinates. + *

Warning: Currently doesn't work as expected.

+ * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. + */ QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); + + /**jsdoc + * Scales, rotates, and translates the mesh. + * @function GraphicsMeshPart.transform + * @param {Mat4} transform - The scale, rotate, and translate transform to apply. + * @returns {Graphics.MeshExtents} The resulting mesh extents, in model coordinates. + */ QVariantMap transform(const glm::mat4& transform); + // @borrows jsdoc from GraphicsMesh. glm::uint32 addAttribute(const QString& attributeName, const QVariant& defaultValue = QVariant()); + + /**jsdoc + * Sets the value of an attribute for all vertices in the whole mesh (i.e., parent and mesh parts). + * @function GraphicsMeshPart.fillAttribute + * @param {Graphics.BufferTypeName} name - The name of the attribute. The attribute is added to the vertices if not + * already present. + * @param {Graphics.BufferType} value - The value to give the attributes. + * @returns {number} 1 if the attribute name was valid and the attribute values were set, 0 + * otherwise. + */ glm::uint32 fillAttribute(const QString& attributeName, const QVariant& value); + + /**jsdoc + * Removes an attribute from all vertices in the whole mesh (i.e., parent and mesh parts). + *

Note: The "position" attribute cannot be removed.

+ * @function GraphicsMeshPArt.removeAttribute + * @param {Graphics.BufferTypeName} name - The name of the attribute to remove. + * @returns {boolean} true if the attribute existed and was removed, false otherwise. + */ bool removeAttribute(const QString& attributeName); + + /**jsdoc + * Deduplicates vertices. + * @function GraphicsMeshPart.dedupeVertices + * @param {number} [epsilon=1e-6] - The deduplicadtion distance. If a pair of vertices is within this distance of each + * other they are combined into a single vertex. + * @returns {boolean} true if the deduplication succeeded, false if it didn't. + */ bool dedupeVertices(float epsilon = 1e-6); + /**jsdoc + * Gets the parent mesh. + * @function GraphicsMeshPart.getParentMesh + * @returns {GraphicsMesh} The parent mesh. + */ scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } + /**jsdoc + * Replaces a mesh part with a copy of another mesh part. + * @function GraphicsMeshPart.replaceMeshPartData + * @param {GrphicsMeshPart} source - The mesh part to copy. + * @param {Graphics.BufferTypeName[]} [attributes] - The attributes to copy. If not specified, all attributes are + * copied from the source. + * @throws Throws an error if the mesh part of source mesh part aren't valid. + * @returns {boolean} true if the mesh part was successfully replaced, false if it wasn't. + */ bool replaceMeshPartData(scriptable::ScriptableMeshPartPointer source, const QVector& attributeNames = QVector()); + + /**jsdoc + * Makes a copy of the mesh part. + * @function GraphicsMeshPart.cloneMeshPart + * @returns {GraphicsMeshPart} A copy of the mesh part. + */ scriptable::ScriptableMeshPartPointer cloneMeshPart(); + /**jsdoc + * Exports the mesh part to OBJ format. + * @function GraphicsMeshPart.toOBJ + * @returns {string} The OBJ format representation of the mesh part. + */ QString toOBJ(); + // QScriptEngine-specific wrappers + + /**jsdoc + * Updates vertex attributes by calling a function for each vertex in the whole mesh (i.e., the parent and + * mesh parts). The function can return modified attributes to update the vertex with. + * @function GraphicsMeshPart.updateVertexAttributes + * @param {GraphicsMesh~updateVertexAttributesCallback} callback - The function to call for each vertex. + * @returns {number} The number of vertices the callback was called for. + */ glm::uint32 updateVertexAttributes(QScriptValue callback); + + /**jsdoc + * Calls a function for each vertex in the whole mesh (i.e., parent and mesh parts). + * @function GraphicsMeshPArt.forEachVertex + * @param {GraphicsMesh~forEachVertexCallback} callback - The function to call for each vertex. + * @returns {number} The number of vertices the callback was called for. + */ glm::uint32 forEachVertex(QScriptValue callback); + /**jsdoc + * Checks if an index is valid and, optionally, that vertex has a particular attribute. + * @function GraphicsMeshPart.isValidIndex + * @param {number} index - The index to check. + * @param {Graphics.BufferTypeName} [attribute] - The attribute to check. + * @returns {boolean} true if the index is valid and that vertex has the attribute if specified. + * @throws Throws an error if the index if invalid or name is invalid or isn't used in the + * mesh. + */ + // FIXME: Should return false rather than throw an error. bool isValidIndex(glm::uint32 vertexIndex, const QString& attributeName = QString()) const; + public: scriptable::ScriptableMeshPointer parentMesh; glm::uint32 partIndex; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index 7d1ca5f560..a6f135c321 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -17,14 +17,36 @@ namespace scriptable { using ScriptableMeshes = QVector; /**jsdoc - * @typedef {object} Graphics.Model - * @property {Uuid} objectID - UUID of corresponding inworld object (if model is associated) - * @property {number} numMeshes - The number of submeshes contained in the model. - * @property {Graphics.Mesh[]} meshes - Array of submesh references. - * @property {Object.} materialLayers - Map of materials layer lists. You can look up a material layer list by mesh part number or by material name. - * @property {string[]} materialNames - Array of all the material names used by the mesh parts of this model, in order (e.g. materialNames[0] is the name of the first mesh part's material). + * A handle to in-memory model data such as may be used in displaying avatars, 3D entities, or 3D overlays in the rendered + * scene. Changes made to the model are visible only to yourself; they are not persisted. + *

Note: The model may be used for more than one instance of an item displayed in the scene. Modifying the model updates + * all instances displayed.

+ * + *

Create using the {@link Graphics} API or {@link GraphicsModel.cloneModel}.

+ * + * @class GraphicsModel + * @hideconstructor + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {Uuid} objectID - The ID of the entity or avatar that the model is associated with, if any; null + * if the model is not associated with an entity or avatar. + * Read-only. + * @property {number} numMeshes - The number of meshes in the model. + * Read-only. + * @property {GraphicsMesh[]} meshes - The meshes in the model. Each mesh may have more than one mesh part. + * Read-only. + * @property {string[]} materialNames - The names of the materials used by each mesh part in the model. The names are in + * the order of the meshes and their mesh parts. + * Read-only. + * @property {Object.} materialLayers - The mapping from mesh parts and material + * names to material layers. The mesh parts are numbered from "0" per the array indexes of + * materialNames. The material names are those used in materialNames. (You can look up a + * material layer by mesh part number or by material name.) + * Read-only. */ - class ScriptableModel : public ScriptableModelBase { Q_OBJECT Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT) @@ -49,7 +71,23 @@ namespace scriptable { QVector getMaterialNames() { return materialNames; } public slots: + + /**jsdoc + * Makes a copy of the model. + * @function GraphicsModel.cloneModel + * @param {object} [options] - Not used. + * @returns {GraphicsModel} A copy of the model. + */ scriptable::ScriptableModelPointer cloneModel(const QVariantMap& options = QVariantMap()); + + /**jsdoc + * Gets a string description of the model. + * @function GraphicsModel.toString + * @returns {string} A string description of the model. + * @example Report the string description of your avatar's model. + * var model = Graphics.getModel(MyAvatar.sessionUUID); + * print("Avatar model info:", model.toString()); + */ QString toString() const; protected: diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 301f5d8d73..88caa3cea4 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -34,6 +34,50 @@ namespace buffer_helpers { const std::array XYZW = { { "x", "y", "z", "w" } }; const std::array ZERO123 = { { "0", "1", "2", "3" } }; +/**jsdoc + *

The type name of a graphics buffer.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"position"Position buffer.
"normal"normal buffer.
"tangent"Tangent buffer.
"color"Color buffer.
"skin_cluster_index"Skin cluster index buffer.
"skin_cluster_weight"Skin cluster weight buffer.
"texcoord0"First UV coordinates buffer.
"texcoord1"Second UV coordinates buffer.
"texcoord2"Third UV coordinates buffer.
"texcoord3"Fourth UV coordinates buffer.
"texcoord4"Fifth UV coordinates buffer.
+ * @typedef {string} Graphics.BufferTypeName + */ +/**jsdoc + *

The type of a graphics buffer value as accessed by JavaScript.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TypeNameDescription
{@link Vec3}"position"Position buffer.
{@link Vec3}"normal"normal buffer.
{@link Vec3}"tangent"Tangent buffer.
{@link Vec4}"color"Color buffer.
{@link Vec4}"skin_cluster_index"Skin cluster index buffer.
{@link Vec4}"skin_cluster_weight"Skin cluster weight buffer.
{@link Vec2}"texcoord0"First UV coordinates buffer.
{@link Vec2}"texcoord1"Second UV coordinates buffer.
{@link Vec2}"texcoord2"Third UV coordinates buffer.
{@link Vec2}"texcoord3"Fourth UV coordinates buffer.
{@link Vec2}"texcoord4"Fifth UV coordinates buffer.
+ * @typedef {Vec4|Vec3|Vec2} Graphics.BufferType + */ QMap ATTRIBUTES{ {"position", gpu::Stream::POSITION }, {"normal", gpu::Stream::NORMAL }, @@ -76,50 +120,6 @@ template<> glm::uint32 forEach(const gpu::BufferView& view, std::func return forEachGlmVec(view, func); } -template -QVariant glmVecToVariant(const T& v, bool asArray /*= false*/) { - static const auto len = T().length(); - if (asArray) { - QVariantList list; - for (int i = 0; i < len ; i++) { - list << v[i]; - } - return list; - } else { - QVariantMap obj; - for (int i = 0; i < len ; i++) { - obj[XYZW[i]] = v[i]; - } - return obj; - } -} - -template -const T glmVecFromVariant(const QVariant& v) { - auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; - static const auto len = T().length(); - const auto& components = isMap ? XYZW : ZERO123; - T result; - QVariantMap map; - QVariantList list; - if (isMap) map = v.toMap(); else list = v.toList(); - for (int i = 0; i < len ; i++) { - float value; - if (isMap) { - value = map.value(components[i]).toFloat(); - } else { - value = list.value(i).toFloat(); - } -#ifdef DEBUG_BUFFERVIEW_HELPERS - if (value != value) { // NAN - qWarning().nospace()<< "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); - } -#endif - result[i] = value; - } - return result; -} - // QVector => BufferView template gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index 3635ef64e5..c42bb0426d 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -27,8 +27,54 @@ namespace buffer_helpers { extern const std::array XYZW; extern const std::array ZERO123; - template QVariant glmVecToVariant(const T& v, bool asArray = false); - template const T glmVecFromVariant(const QVariant& v); + template + QVariant glmVecToVariant(const T& v, bool asArray = false) { + static const auto len = T().length(); + if (asArray) { + QVariantList list; + for (int i = 0; i < len; i++) { + list << v[i]; + } + return list; + } else { + QVariantMap obj; + for (int i = 0; i < len; i++) { + obj[XYZW[i]] = v[i]; + } + return obj; + } + } + + template + const T glmVecFromVariant(const QVariant& v) { + auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; + static const auto len = T().length(); + const auto& components = isMap ? XYZW : ZERO123; + T result; + QVariantMap map; + QVariantList list; + if (isMap) { + map = v.toMap(); + } else { + list = v.toList(); + } + for (int i = 0; i < len; i++) { + float value; + if (isMap) { + value = map.value(components[i]).toFloat(); + } else { + value = list.value(i).toFloat(); + } +#ifdef DEBUG_BUFFERVIEW_HELPERS + if (value != value) { // NAN + qWarning().nospace() << "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); + } +#endif + result[i] = value; + } + return result; + } + glm::uint32 forEachVariant(const gpu::BufferView& view, std::function func, const char* hint = ""); template glm::uint32 forEach(const gpu::BufferView& view, std::function func); @@ -36,7 +82,7 @@ namespace buffer_helpers { template gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType); template gpu::BufferView newFromVariantList(const QVariantList& list, const gpu::Element& elementType); - template QVector variantToVector(const QVariant& list); + template QVector variantToVector(const QVariant& value); template QVector bufferToVector(const gpu::BufferView& view, const char *hint = ""); // note: these do value conversions from the underlying buffer type into the template type diff --git a/libraries/graphics/src/graphics/Geometry.h b/libraries/graphics/src/graphics/Geometry.h index 20018ba71b..fe1981c0e9 100755 --- a/libraries/graphics/src/graphics/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -79,23 +79,6 @@ public: // Access vertex position value const Vec3& getPos(Index index) const { return _vertexBuffer.get(index); } - /**jsdoc - * - * - * - * - * - * - * - * - * - * - * - * - * - *
ValueDescription
0Points.
1Lines.
2Line strip.
3Triangles.
4Triangle strip.
5Quads.
6Quad strip.
- * @typedef {number} Graphics.Topology - */ enum Topology { POINTS = 0, LINES, diff --git a/libraries/graphics/src/graphics/GpuHelpers.cpp b/libraries/graphics/src/graphics/GpuHelpers.cpp index b864b0f040..dd911e33c2 100644 --- a/libraries/graphics/src/graphics/GpuHelpers.cpp +++ b/libraries/graphics/src/graphics/GpuHelpers.cpp @@ -8,6 +8,24 @@ #include "GpuHelpers.h" +/**jsdoc + *

The interpretation of mesh elements.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"points"Points.
"lines"Lines.
"line_strip"Line strip.
"triangles"Triangles.
"triangle_strip"Triangle strip.
"quads"Quads.
"quad_strip"Quad strip.
+ * @typedef {string} Graphics.MeshTopology + */ namespace graphics { DebugEnums TOPOLOGIES{ { Mesh::Topology::POINTS, "points" }, diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index 328ff4a3af..274dbc1cdd 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -13,7 +13,6 @@ <@include graphics/ShaderConstants.h@> - const int MAX_TEXCOORDS = 2; struct TexMapArray { diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 7111ad2e65..d37d509d38 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -51,7 +51,7 @@ using ColorType = glm::vec3; #define HFM_COLOR_ELEMENT gpu::Element::VEC3F_XYZ #endif -const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; +const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 8192 * 8192; using ShapeVertices = std::vector; diff --git a/libraries/image/src/image/TextureProcessing.cpp b/libraries/image/src/image/TextureProcessing.cpp index c144ed530a..53991ae431 100644 --- a/libraries/image/src/image/TextureProcessing.cpp +++ b/libraries/image/src/image/TextureProcessing.cpp @@ -41,7 +41,7 @@ using namespace gpu; static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE_GLES(2048); -static const glm::uvec2 MAX_TEXTURE_SIZE_GL(4096); +static const glm::uvec2 MAX_TEXTURE_SIZE_GL(8192); bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; diff --git a/libraries/image/src/image/TextureProcessing.h b/libraries/image/src/image/TextureProcessing.h index 677d48c61f..326ec700a9 100644 --- a/libraries/image/src/image/TextureProcessing.h +++ b/libraries/image/src/image/TextureProcessing.h @@ -33,7 +33,7 @@ namespace TextureUsage { /**jsdoc *

Describes the type of texture.

*

See also: {@link Material} and - * {@link https://docs.projectathena.dev/create/3d-models/pbr-materials-guide.html|PBR Materials Guide}.

+ * {@link https://docs.vircadia.dev/create/3d-models/pbr-materials-guide.html|PBR Materials Guide}.

* * * diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index b1746951bb..c436a5b510 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -126,12 +127,66 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) { } } -void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { - auto currentMove = event->angleDelta() / 120.0f; - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()].value = currentMove.x() > 0 ? currentMove.x() : 0; - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()].value = currentMove.x() < 0 ? -currentMove.x() : 0; - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()].value = currentMove.y() > 0 ? currentMove.y() : 0; - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()].value = currentMove.y() < 0 ? -currentMove.y() : 0; +bool KeyboardMouseDevice::isWheelByTouchPad(QWheelEvent* event) { + // This function is only used to track two finger swipe using the touchPad on Windows. + // That gesture gets sent as a wheel event. This wheel delta values are used to orbit the camera. + // On MacOS the two finger swipe fires touch events and wheel events. + // In that case we always return false to avoid interference between both. +#ifdef Q_OS_MAC + return false; +#endif + QPoint delta = event->angleDelta(); + int deltaValueX = abs(delta.x()); + int deltaValueY = abs(delta.y()); + const int COMMON_WHEEL_DELTA_VALUE = 120; + // If deltaValueX or deltaValueY are multiple of 120 they are triggered by a mouse wheel + bool isMouseWheel = (deltaValueX + deltaValueY) % COMMON_WHEEL_DELTA_VALUE == 0; + if (!isMouseWheel) { + // We track repetition in wheel values to detect non-standard mouse wheels + const int MAX_WHEEL_DELTA_REPEAT = 10; + if (deltaValueX != 0) { + if (abs(_lastWheelDelta.x()) == deltaValueX) { + _wheelDeltaRepeatCount.setX(_wheelDeltaRepeatCount.x() + 1); + } else { + _wheelDeltaRepeatCount.setX(0); + } + return _wheelDeltaRepeatCount.x() < MAX_WHEEL_DELTA_REPEAT; + } + if (deltaValueY != 0) { + if (abs(_lastWheelDelta.y()) == deltaValueY) { + _wheelDeltaRepeatCount.setY(_wheelDeltaRepeatCount.y() + 1); + } else { + _wheelDeltaRepeatCount.setY(0); + } + return _wheelDeltaRepeatCount.y() < MAX_WHEEL_DELTA_REPEAT; + } + } + return false; +} + +void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { + if (isWheelByTouchPad(event)) { + // Check for horizontal and vertical scroll not triggered by the mouse. + // These are most likelly triggered by two fingers gesture on touchpad for windows. + QPoint delta = event->angleDelta(); + float deltaX = (float)delta.x(); + float deltaY = (float)delta.y(); + const float WHEEL_X_ATTENUATION = 0.3f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()].value = (deltaX > 0 ? WHEEL_X_ATTENUATION * deltaX : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()].value = (deltaX < 0 ? -WHEEL_X_ATTENUATION * deltaX : 0.0f); + // Y mouse is inverted positive is pointing up the screen + const float WHEEL_Y_ATTENUATION = 0.02f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()].value = (deltaY < 0 ? -WHEEL_Y_ATTENUATION * deltaY : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()].value = (deltaY > 0 ? WHEEL_Y_ATTENUATION * deltaY : 0.0f); + } else { + auto currentMove = event->angleDelta() / 120.0f; + float currentMoveX = (float)currentMove.x(); + float currentMoveY = (float)currentMove.y(); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()].value = currentMoveX > 0 ? currentMoveX : 0.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()].value = currentMoveX < 0 ? -currentMoveX : 0.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()].value = currentMoveY > 0 ? currentMoveY : 0.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()].value = currentMoveY < 0 ? -currentMoveY : 0.0f; + } } glm::vec2 evalAverageTouchPoints(const QList& points) { @@ -145,6 +200,37 @@ glm::vec2 evalAverageTouchPoints(const QList& points) { return averagePoint; } +void KeyboardMouseDevice::touchGestureEvent(const QGestureEvent* event) { + QPinchGesture* pinchGesture = (QPinchGesture*) event->gesture(Qt::PinchGesture); + + if (pinchGesture) { + switch (pinchGesture->state()) { + case Qt::GestureStarted: + _lastTotalScaleFactor = pinchGesture->totalScaleFactor(); + break; + + case Qt::GestureUpdated: { + const float PINCH_DELTA_STEP = 0.04f; + qreal totalScaleFactor = pinchGesture->totalScaleFactor(); + qreal scaleFactorDelta = _lastTotalScaleFactor - totalScaleFactor; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_POS).getChannel()].value = scaleFactorDelta > 0.0 ? PINCH_DELTA_STEP : 0.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_NEG).getChannel()].value = scaleFactorDelta < 0.0 ? PINCH_DELTA_STEP : 0.0f; + _lastTotalScaleFactor = totalScaleFactor; + break; + } + + case Qt::GestureFinished: { + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_POS).getChannel()].value = 0.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_NEG).getChannel()].value = 0.0f; + break; + } + + default: + break; + } + } +} + void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) { if (_enableTouch) { _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); @@ -167,7 +253,7 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { _lastTouchTime = _clock.now(); if (!_isTouching) { - _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); + _isTouching = true; } else { auto currentMove = currentPos - _lastTouch; _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()].value = (currentMove.x > 0 ? currentMove.x : 0.0f); @@ -270,17 +356,17 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic * new x-coordinate value. * - * - * - * + * + * - * @@ -292,7 +378,11 @@ controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevic * moved up. The data value is how far the average position of all touch points moved. * - * + * + * + * *
ValueNameDescription
MouseYnumbernumberThe mouse y-coordinate changed. The data value is its * new y-coordinate value.
MouseWheelRightnumbernumberThe mouse wheel rotated right. The data value - * is the number of units rotated (typically 1.0).
MouseWheelLeftnumbernumberThe mouse wheel rotated left. The data value - * is the number of units rotated (typically 1.0).
MouseWheelUpnumbernumberThe mouse wheel rotated up. The data value - * is the number of units rotated (typically 1.0). + *
MouseWheelRightnumbernumberThe mouse wheel rotated right or two-finger + * swipe moved right. The data value is the number of units moved (typically 1.0).
MouseWheelLeftnumbernumberThe mouse wheel rotated left or two-finger + * swipe moved left. The data value is the number of units moved (typically 1.0).
MouseWheelUpnumbernumberThe mouse wheel rotated up or two-finger swipe + * moved up. The data value is the number of units move3d (typically 1.0). *

Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of * up/down.

*
MouseWheelDownnumbernumberThe mouse wheel rotated down. The data value - * is the number of units rotated (typically 1.0). + *
MouseWheelDownnumbernumberThe mouse wheel rotated down or two-finger + * swipe moved down. The data value is the number of units moved (typically 1.0). *

Warning: The mouse wheel in an ordinary mouse generates left/right wheel events instead of * up/down.

*
TouchpadDownnumbernumberThe average touch on a touch-enabled device * moved down. The data value is how far the average position of all touch points moved.
GesturePinchOutnumbernumberThe average of two touches on a touch-enabled + * device moved out. The data value is how far the average positions of the touch points moved out.
GesturePinchOutnumbernumberThe average of two touches on a touch-enabled + * device moved in. The data value is how far the average positions of the touch points moved in.
* @typedef {object} Controller.Hardware-Keyboard */ @@ -344,6 +434,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft")); availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp")); availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_GESTURE_PINCH_POS), "GesturePinchOut")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_GESTURE_PINCH_NEG), "GesturePinchIn")); }); return availableInputs; } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index f6921c8e23..4286ced477 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -23,6 +23,7 @@ class QTouchEvent; class QKeyEvent; class QMouseEvent; class QWheelEvent; +class QGestureEvent; class KeyboardMouseDevice : public InputPlugin { Q_OBJECT @@ -60,6 +61,8 @@ public: TOUCH_AXIS_X_NEG, TOUCH_AXIS_Y_POS, TOUCH_AXIS_Y_NEG, + TOUCH_GESTURE_PINCH_POS, + TOUCH_GESTURE_PINCH_NEG, }; enum TouchButtonChannel { @@ -81,11 +84,13 @@ public: void mouseReleaseEvent(QMouseEvent* event); void eraseMouseClicked(); + void touchGestureEvent(const QGestureEvent* event); void touchBeginEvent(const QTouchEvent* event); void touchEndEvent(const QTouchEvent* event); void touchUpdateEvent(const QTouchEvent* event); void wheelEvent(QWheelEvent* event); + bool isWheelByTouchPad(QWheelEvent* event); static void enableTouch(bool enableTouch) { _enableTouch = enableTouch; } @@ -121,6 +126,7 @@ protected: QPoint _previousCursor; QPoint _mousePressPos; quint64 _mousePressTime; + qreal _lastTotalScaleFactor; bool _clickDeadspotActive; glm::vec2 _lastTouch; std::shared_ptr _inputDevice { std::make_shared() }; @@ -130,6 +136,8 @@ protected: std::chrono::high_resolution_clock::time_point _lastTouchTime; static bool _enableTouch; + QPoint _lastWheelDelta; + QPoint _wheelDeltaRepeatCount; private: void updateDeltaAxisValue(int channel, float value); diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h index 617e40ce06..cd3eb109d7 100644 --- a/libraries/ktx/src/khronos/KHR.h +++ b/libraries/ktx/src/khronos/KHR.h @@ -11,6 +11,7 @@ #define khronos_khr_hpp #include +#include namespace khronos { diff --git a/libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.h b/libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.h index 58d2784855..5ff15c03d9 100644 --- a/libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.h +++ b/libraries/material-networking/src/material-networking/TextureCacheScriptingInterface.h @@ -37,6 +37,10 @@ class TextureCacheScriptingInterface : public ScriptableResourceCache, public De * @property {number} numCached - Total number of cached resource. Read-only. * @property {number} sizeTotal - Size in bytes of all resources. Read-only. * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers). + * Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers). + * Read-only. * * @borrows ResourceCache.getResourceList as getResourceList * @borrows ResourceCache.updateTotalSize as updateTotalSize diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 986682d7f9..e2b84ec333 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -23,7 +23,7 @@ /**jsdoc * The Midi API provides the ability to connect Interface with musical instruments and other external or virtual * devices via the MIDI protocol. For further information and examples, see the tutorial: - * Use MIDI to Control Your Environment. + * Use MIDI to Control Your Environment. * *

Note: Only works on Windows.

* diff --git a/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h b/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h index cea2a6cd40..d83b853b01 100644 --- a/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h +++ b/libraries/model-networking/src/model-networking/ModelCacheScriptingInterface.h @@ -37,6 +37,10 @@ class ModelCacheScriptingInterface : public ScriptableResourceCache, public Depe * @property {number} numCached - Total number of cached resource. Read-only. * @property {number} sizeTotal - Size in bytes of all resources. Read-only. * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers). + * Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers). + * Read-only. * * @borrows ResourceCache.getResourceList as getResourceList * @borrows ResourceCache.updateTotalSize as updateTotalSize diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 5473f1a010..5589defd80 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -52,11 +52,13 @@ const int PULL_SETTINGS_RETRY_INTERVAL = 2 * MSECS_PER_SECOND; const int MAX_PULL_RETRIES = 10; JSONCallbackParameters::JSONCallbackParameters(QObject* callbackReceiver, - const QString& jsonCallbackMethod, - const QString& errorCallbackMethod) : + const QString& jsonCallbackMethod, + const QString& errorCallbackMethod, + const QJsonObject& callbackData) : callbackReceiver(callbackReceiver), jsonCallbackMethod(jsonCallbackMethod), - errorCallbackMethod(errorCallbackMethod) + errorCallbackMethod(errorCallbackMethod), + callbackData(callbackData) { } @@ -348,9 +350,17 @@ void AccountManager::sendRequest(const QString& path, [callbackParams, networkReply] { if (networkReply->error() == QNetworkReply::NoError) { if (!callbackParams.jsonCallbackMethod.isEmpty()) { - bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, - qPrintable(callbackParams.jsonCallbackMethod), - Q_ARG(QNetworkReply*, networkReply)); + bool invoked = false; + if (callbackParams.callbackData.isEmpty()) { + invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, + qPrintable(callbackParams.jsonCallbackMethod), + Q_ARG(QNetworkReply*, networkReply)); + } else { + invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, + qPrintable(callbackParams.jsonCallbackMethod), + Q_ARG(QNetworkReply*, networkReply), + Q_ARG(QJsonObject, callbackParams.callbackData)); + } if (!invoked) { QString error = "Could not invoke " + callbackParams.jsonCallbackMethod + " with QNetworkReply* " @@ -366,9 +376,18 @@ void AccountManager::sendRequest(const QString& path, } } else { if (!callbackParams.errorCallbackMethod.isEmpty()) { - bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, - qPrintable(callbackParams.errorCallbackMethod), - Q_ARG(QNetworkReply*, networkReply)); + bool invoked = false; + if (callbackParams.callbackData.isEmpty()) { + invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, + qPrintable(callbackParams.errorCallbackMethod), + Q_ARG(QNetworkReply*, networkReply)); + } + else { + invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver, + qPrintable(callbackParams.errorCallbackMethod), + Q_ARG(QNetworkReply*, networkReply), + Q_ARG(QJsonObject, callbackParams.callbackData)); + } if (!invoked) { QString error = "Could not invoke " + callbackParams.errorCallbackMethod + " with QNetworkReply* " @@ -489,7 +508,9 @@ bool AccountManager::checkAndSignalForAccessToken() { if (!hasToken) { // emit a signal so somebody can call back to us and request an access token given a username and password - emit authRequired(); + + // Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed. + QTimer::singleShot(500, this, [this] { emit this->authRequired(); }); } return hasToken; diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 37cb548b7e..edc7b652b4 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -31,14 +31,16 @@ class JSONCallbackParameters { public: JSONCallbackParameters(QObject* callbackReceiver = nullptr, - const QString& jsonCallbackMethod = QString(), - const QString& errorCallbackMethod = QString()); + const QString& jsonCallbackMethod = QString(), + const QString& errorCallbackMethod = QString(), + const QJsonObject& callbackData = QJsonObject()); bool isEmpty() const { return !callbackReceiver; } QObject* callbackReceiver; QString jsonCallbackMethod; QString errorCallbackMethod; + QJsonObject callbackData; }; namespace AccountManagerAuth { @@ -56,7 +58,7 @@ const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit(); using UserAgentGetter = std::function; -const auto DEFAULT_USER_AGENT_GETTER = []() -> QString { return HIGH_FIDELITY_USER_AGENT; }; +const auto DEFAULT_USER_AGENT_GETTER = []() -> QString { return NetworkingConstants::VIRCADIA_USER_AGENT; }; class AccountManager : public QObject, public Dependency { Q_OBJECT diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 1939e6401a..5f08f19d2b 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -243,7 +243,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrlIn, LookupTrigger trigger) { QUrl lookupUrl = lookupUrlIn; - qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); + if (!lookupUrl.host().isEmpty() && !lookupUrl.path().isEmpty()) { + // Assignment clients ping for empty url until assigned. Don't spam. + qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); + } if (lookupUrl.scheme().isEmpty() && !lookupUrl.path().startsWith("/")) { // 'urls' without schemes are taken as domain names, as opposed to diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 31bda960ec..148c4f8580 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -49,10 +49,10 @@ const QString GET_PLACE = "/api/v1/places/%1"; * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid(0)|Uuid.NULL} if you're not * connected to the domain or are in a serverless domain. * Read-only. - * @property {string} hostname - The name of the domain for your current metaverse address (e.g., "AvatarIsland", + * @property {string} hostname - The name of the domain for your current metaverse address (e.g., "DomainName", * localhost, or an IP address). Is blank if you're in a serverless domain. * Read-only. - * @property {string} href - Your current metaverse address (e.g., "hifi://avatarisland/15,-10,26/0,0,0,1") + * @property {string} href - Your current metaverse address (e.g., "hifi://domainname/15,-10,26/0,0,0,1") * regardless of whether or not you're connected to the domain. Starts with "file:///" if you're in a * serverless domain. * Read-only. @@ -62,12 +62,72 @@ const QString GET_PLACE = "/api/v1/places/%1"; * (e.g., "/15,-10,26/0,0,0,1"). * Read-only. * @property {string} placename - The place name in your current href metaverse address - * (e.g., "AvatarIsland"). Is blank if your hostname is an IP address. + * (e.g., "DomainName"). Is blank if your hostname is an IP address. * Read-only. * @property {string} protocol - The protocol of your current href metaverse address (e.g., "hifi"). * Read-only. */ +/**jsdoc + * The AddressManager API provides facilities related to your current location in the metaverse. + * + * @namespace AddressManager + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @deprecated This API is deprecated and will be removed. Use the {@link location} or {@link Window|Window.location} APIs + * instead. + * + * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid(0)|Uuid.NULL} if you're not + * connected to the domain or are in a serverless domain. + * Read-only. + * @property {string} hostname - The name of the domain for your current metaverse address (e.g., "DomainName", + * localhost, or an IP address). Is blank if you're in a serverless domain. + * Read-only. + * @property {string} href - Your current metaverse address (e.g., "hifi://domainname/15,-10,26/0,0,0,1") + * regardless of whether or not you're connected to the domain. Starts with "file:///" if you're in a + * serverless domain. + * Read-only. + * @property {boolean} isConnected - true if you're connected to the domain in your current href + * metaverse address, otherwise false. + * @property {string} pathname - The location and orientation in your current href metaverse address + * (e.g., "/15,-10,26/0,0,0,1"). + * Read-only. + * @property {string} placename - The place name in your current href metaverse address + * (e.g., "DomainName"). Is blank if your hostname is an IP address. + * Read-only. + * @property {string} protocol - The protocol of your current href metaverse address (e.g., "hifi"). + * Read-only. + * + * @borrows location.handleLookupString as handleLookupString + * @borrows location.goToViewpointForPath as goToViewpointForPath + * @borrows location.goBack as goBack + * @borrows location.goForward as goForward + * @borrows location.goToLocalSandbox as goToLocalSandbox + * @borrows location.goToEntry as goToEntry + * @borrows location.goToUser as goToUser + * @borrows location.goToLastAddress as goToLastAddress + * @borrows location.canGoBack as canGoBack + * @borrows location.refreshPreviousLookup as refreshPreviousLookup + * @borrows location.storeCurrentAddress as storeCurrentAddress + * @borrows location.copyAddress as copyAddress + * @borrows location.copyPath as copyPath + * @borrows location.lookupShareableNameForDomainID as lookupShareableNameForDomainID + * + * @borrows location.lookupResultsFinished as lookupResultsFinished + * @borrows location.lookupResultIsOffline as lookupResultIsOffline + * @borrows location.lookupResultIsNotFound as lookupResultIsNotFound + * @borrows location.possibleDomainChangeRequired as possibleDomainChangeRequired + * @borrows location.locationChangeRequired as locationChangeRequired + * @borrows location.possibleDomainChangeRequiredViaICEForID as possibleDomainChangeRequiredViaICEForID + * @borrows location.pathChangeRequired as pathChangeRequired + * @borrows location.hostChanged as hostChanged + * @borrows location.goBackPossible as goBackPossible + * @borrows location.goForwardPossible as goForwardPossible + */ + class AddressManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -189,8 +249,9 @@ public slots: * Takes you to a specified metaverse address. * @function location.handleLookupString * @param {string} address - The address to go to: a "hifi://" address, an IP address (e.g., - * "127.0.0.1" or "localhost"), a domain name, a named path on a domain (starts with - * "/"), a position or position and orientation, or a user (starts with "@"). + * "127.0.0.1" or "localhost"), a file:/// address, a domain name, a named path + * on a domain (starts with "/"), a position or position and orientation, or a user (starts with + * "@"). * @param {boolean} [fromSuggestions=false] - Set to true if the address is obtained from the "Goto" dialog. * Helps ensure that user's location history is correctly maintained. */ diff --git a/libraries/networking/src/DomainAccountManager.cpp b/libraries/networking/src/DomainAccountManager.cpp new file mode 100644 index 0000000000..928b581a5b --- /dev/null +++ b/libraries/networking/src/DomainAccountManager.cpp @@ -0,0 +1,203 @@ +// +// DomainAccountManager.cpp +// libraries/networking/src +// +// Created by David Rowe on 23 Jul 2020. +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DomainAccountManager.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "NetworkingConstants.h" +#include "NetworkAccessManager.h" +#include "NetworkLogging.h" +#include "NodeList.h" + +// FIXME: Generalize to other OAuth2 sources for domain login. + +const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false; + +// ####### TODO: Enable and use these? +// ####### TODO: Add storing domain URL and check against it when retrieving values? +// ####### TODO: Add storing _authURL and check against it when retrieving values? +/* +Setting::Handle domainAccessToken {"private/domainAccessToken", "" }; +Setting::Handle domainAccessRefreshToken {"private/domainAccessToken", "" }; +Setting::Handle domainAccessTokenExpiresIn {"private/domainAccessTokenExpiresIn", -1 }; +Setting::Handle domainAccessTokenType {"private/domainAccessTokenType", "" }; +*/ + +DomainAccountManager::DomainAccountManager() : + _authURL(), + _username(), + _access_token(), + _refresh_token(), + _domain_name() +{ + connect(this, &DomainAccountManager::loginComplete, this, &DomainAccountManager::sendInterfaceAccessTokenToServer); +} + +void DomainAccountManager::setAuthURL(const QUrl& authURL) { + if (_authURL != authURL) { + _authURL = authURL; + + qCDebug(networking) << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString()); + + _access_token = ""; + _refresh_token = ""; + + // ####### TODO: Restore and refresh OAuth2 tokens if have them for this domain. + + // ####### TODO: Handle "keep me logged in". + } +} + +bool DomainAccountManager::isLoggedIn() { + return !_authURL.isEmpty() && hasValidAccessToken(); +} + +void DomainAccountManager::requestAccessToken(const QString& username, const QString& password) { + + _username = username; + _access_token = ""; + _refresh_token = ""; + + QNetworkRequest request; + + request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + // miniOrange WordPress API Authentication plugin: + // - Requires "client_id" parameter. + // - Ignores "state" parameter. + QByteArray formData; + formData.append("grant_type=password&"); + formData.append("username=" + QUrl::toPercentEncoding(username) + "&"); + formData.append("password=" + QUrl::toPercentEncoding(password) + "&"); + formData.append("client_id=" + _clientID); + + request.setUrl(_authURL); + + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* requestReply = networkAccessManager.post(request, formData); + connect(requestReply, &QNetworkReply::finished, this, &DomainAccountManager::requestAccessTokenFinished); +} + +void DomainAccountManager::requestAccessTokenFinished() { + + QNetworkReply* requestReply = reinterpret_cast(sender()); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); + const QJsonObject& rootObject = jsonResponse.object(); + + auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (200 <= httpStatus && httpStatus < 300) { + + // miniOrange plugin provides no scope. + if (rootObject.contains("access_token")) { + // Success. + auto nodeList = DependencyManager::get(); + _domain_name = nodeList->getDomainHandler().getHostname(); + QUrl rootURL = requestReply->url(); + rootURL.setPath(""); + setTokensFromJSON(rootObject, rootURL); + emit loginComplete(); + } else { + // Failure. + qCDebug(networking) << "Received a response for password grant that is missing one or more expected values."; + emit loginFailed(); + } + + } else { + // Failure. + qCDebug(networking) << "Error in response for password grant -" << httpStatus << requestReply->error() + << "-" << rootObject["error"].toString() << rootObject["error_description"].toString(); + emit loginFailed(); + } +} + +void DomainAccountManager::sendInterfaceAccessTokenToServer() { + emit newTokens(); +} + +bool DomainAccountManager::accessTokenIsExpired() { + // ####### TODO: accessTokenIsExpired() + return true; + /* + return domainAccessTokenExpiresIn.get() != -1 && domainAccessTokenExpiresIn.get() <= QDateTime::currentMSecsSinceEpoch(); + */ +} + + +bool DomainAccountManager::hasValidAccessToken() { + // ###### TODO: wire this up to actually retrieve a token (based on session or storage) and confirm that it is in fact valid and relevant to the current domain. + // QString currentDomainAccessToken = domainAccessToken.get(); + QString currentDomainAccessToken = _access_token; + + // if (currentDomainAccessToken.isEmpty() || accessTokenIsExpired()) { + if (currentDomainAccessToken.isEmpty()) { + if (VERBOSE_HTTP_REQUEST_DEBUGGING) { + qCDebug(networking) << "An access token is required for requests to" + << qPrintable(_authURL.toString()); + } + + return false; + } + + // ####### TODO + + // if (!_isWaitingForTokenRefresh && needsToRefreshToken()) { + // refreshAccessToken(); + // } + + return true; +} + +void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, const QUrl& url) { + _access_token = jsonObject["access_token"].toString(); + _refresh_token = jsonObject["refresh_token"].toString(); + + // ####### TODO: Enable and use these? + // ####### TODO: Protect these per AccountManager? + // ######: TODO: clientID needed? + // qCDebug(networking) << "Storing a domain account with access-token for" << qPrintable(url.toString()); + // domainAccessToken.set(jsonObject["access_token"].toString()); + // domainAccessRefreshToken.set(jsonObject["refresh_token"].toString()); + // domainAccessTokenExpiresIn.set(QDateTime::currentMSecsSinceEpoch() + (jsonObject["expires_in"].toDouble() * 1000)); + // domainAccessTokenType.set(jsonObject["token_type"].toString()); +} + +bool DomainAccountManager::checkAndSignalForAccessToken() { + bool hasToken = hasValidAccessToken(); + + // ####### TODO: Handle hasToken == true. + // It causes the login dialog not to display (OK) but somewhere the domain server needs to be sent it (and if domain server + // gets error when trying to use it then user should be prompted to login). + hasToken = false; + + if (!hasToken) { + // Emit a signal so somebody can call back to us and request an access token given a user name and password. + + // Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed. + auto domain = _authURL.host(); + QTimer::singleShot(500, this, [this, domain] { + emit this->authRequired(domain); + }); + } + + return hasToken; +} diff --git a/libraries/networking/src/DomainAccountManager.h b/libraries/networking/src/DomainAccountManager.h new file mode 100644 index 0000000000..31226d6990 --- /dev/null +++ b/libraries/networking/src/DomainAccountManager.h @@ -0,0 +1,66 @@ +// +// DomainAccountManager.h +// libraries/networking/src +// +// Created by David Rowe on 23 Jul 2020. +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DomainAccountManager_h +#define hifi_DomainAccountManager_h + +#include +#include + +#include + + +class DomainAccountManager : public QObject, public Dependency { + Q_OBJECT +public: + DomainAccountManager(); + + void setAuthURL(const QUrl& authURL); + void setClientID(const QString& clientID) { _clientID = clientID; } + + QString getUsername() { return _username; } + QString getAccessToken() { return _access_token; } + QString getRefreshToken() { return _refresh_token; } + QString getAuthedDomain() { return _domain_name; } + + bool isLoggedIn(); + + Q_INVOKABLE bool checkAndSignalForAccessToken(); + +public slots: + void requestAccessToken(const QString& username, const QString& password); + + void requestAccessTokenFinished(); + +signals: + void authRequired(const QString& domain); + void loginComplete(); + void loginFailed(); + void logoutComplete(); + void newTokens(); + +private slots: + +private: + bool hasValidAccessToken(); + bool accessTokenIsExpired(); + void setTokensFromJSON(const QJsonObject&, const QUrl& url); + void sendInterfaceAccessTokenToServer(); + + QUrl _authURL; + QString _clientID; + QString _username; // ####### TODO: Store elsewhere? + QString _access_token; // ####... "" + QString _refresh_token; // ####... "" + QString _domain_name; // ####... "" +}; + +#endif // hifi_DomainAccountManager_h diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index fa4e3dc29b..c1a24748f4 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2/18/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -24,6 +25,7 @@ #include "AddressManager.h" #include "Assignment.h" +#include "DomainAccountManager.h" #include "HifiSockAddr.h" #include "NodeList.h" #include "udt/Packet.h" @@ -144,7 +146,8 @@ void DomainHandler::hardReset(QString reason) { bool DomainHandler::isHardRefusal(int reasonCode) { return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch || reasonCode == (int)ConnectionRefusedReason::TooManyUsers || - reasonCode == (int)ConnectionRefusedReason::NotAuthorized || + reasonCode == (int)ConnectionRefusedReason::NotAuthorizedMetaverse || + reasonCode == (int)ConnectionRefusedReason::NotAuthorizedDomain || reasonCode == (int)ConnectionRefusedReason::TimedOut); } @@ -211,6 +214,7 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { // if it's in the error state, reset and try again. if (_domainURL != domainURL || (_sockAddr.getPort() != domainPort && domainURL.scheme() == URL_SCHEME_HIFI) + || isServerless() // For reloading content in serverless domain. || _isInErrorState) { // re-set the domain info so that auth information is reloaded hardReset("Changing domain URL"); @@ -218,6 +222,8 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { QString previousHost = _domainURL.host(); _domainURL = domainURL; + _hasCheckedForDomainAccessToken = false; + if (previousHost != domainURL.host()) { qCDebug(networking) << "Updated domain hostname to" << domainURL.host(); @@ -488,16 +494,33 @@ void DomainHandler::processICEResponsePacket(QSharedPointer mes } } -bool DomainHandler::reasonSuggestsLogin(ConnectionRefusedReason reasonCode) { +bool DomainHandler::reasonSuggestsMetaverseLogin(ConnectionRefusedReason reasonCode) { switch (reasonCode) { - case ConnectionRefusedReason::LoginError: - case ConnectionRefusedReason::NotAuthorized: + case ConnectionRefusedReason::LoginErrorMetaverse: + case ConnectionRefusedReason::NotAuthorizedMetaverse: return true; default: case ConnectionRefusedReason::Unknown: case ConnectionRefusedReason::ProtocolMismatch: case ConnectionRefusedReason::TooManyUsers: + case ConnectionRefusedReason::NotAuthorizedDomain: + return false; + } + return false; +} + +bool DomainHandler::reasonSuggestsDomainLogin(ConnectionRefusedReason reasonCode) { + switch (reasonCode) { + case ConnectionRefusedReason::LoginErrorDomain: + case ConnectionRefusedReason::NotAuthorizedDomain: + return true; + + default: + case ConnectionRefusedReason::Unknown: + case ConnectionRefusedReason::ProtocolMismatch: + case ConnectionRefusedReason::TooManyUsers: + case ConnectionRefusedReason::NotAuthorizedMetaverse: return false; } return false; @@ -527,7 +550,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer(); - // Some connection refusal reasons imply that a login is required. If so, suggest a new login - if (reasonSuggestsLogin(reasonCode)) { - qCWarning(networking) << "Make sure you are logged in."; + // Some connection refusal reasons imply that a login is required. If so, suggest a new login. + if (reasonSuggestsMetaverseLogin(reasonCode)) { + qCWarning(networking) << "Make sure you are logged in to the metaverse."; + + auto accountManager = DependencyManager::get(); if (!_hasCheckedForAccessToken) { accountManager->checkAndSignalForAccessToken(); @@ -558,6 +584,23 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointergenerateNewUserKeypair(); _connectionDenialsSinceKeypairRegen = 0; } + } else if (reasonSuggestsDomainLogin(reasonCode)) { + qCWarning(networking) << "Make sure you are logged in to the domain."; + + auto accountManager = DependencyManager::get(); + if (!extraInfo.isEmpty()) { + auto extraInfoComponents = extraInfo.split("|"); + accountManager->setAuthURL(extraInfoComponents.value(0)); + accountManager->setClientID(extraInfoComponents.value(1)); + } + + if (!_hasCheckedForDomainAccessToken) { + accountManager->checkAndSignalForAccessToken(); + _hasCheckedForDomainAccessToken = true; + } + + // ####### TODO: regenerate key-pair after several failed connection attempts, similar to metaverse login code? + } } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 68059fb158..d0bd40f7a7 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2/18/2014. // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -33,7 +34,7 @@ #include "NetworkingConstants.h" #include "MetaverseAPI.h" -const unsigned short DEFAULT_DOMAIN_SERVER_PORT = +const unsigned short DEFAULT_DOMAIN_SERVER_PORT = QProcessEnvironment::systemEnvironment() .contains("HIFI_DOMAIN_SERVER_PORT") ? QProcessEnvironment::systemEnvironment() @@ -41,7 +42,7 @@ const unsigned short DEFAULT_DOMAIN_SERVER_PORT = .toUShort() : 40102; -const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = +const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = QProcessEnvironment::systemEnvironment() .contains("HIFI_DOMAIN_SERVER_DTLS_PORT") ? QProcessEnvironment::systemEnvironment() @@ -49,7 +50,7 @@ const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = .toUShort() : 40103; -const quint16 DOMAIN_SERVER_HTTP_PORT = +const quint16 DOMAIN_SERVER_HTTP_PORT = QProcessEnvironment::systemEnvironment() .contains("HIFI_DOMAIN_SERVER_HTTP_PORT") ? QProcessEnvironment::systemEnvironment() @@ -57,7 +58,7 @@ const quint16 DOMAIN_SERVER_HTTP_PORT = .toUInt() : 40100; -const quint16 DOMAIN_SERVER_HTTPS_PORT = +const quint16 DOMAIN_SERVER_HTTPS_PORT = QProcessEnvironment::systemEnvironment() .contains("HIFI_DOMAIN_SERVER_HTTPS_PORT") ? QProcessEnvironment::systemEnvironment() @@ -65,6 +66,15 @@ const quint16 DOMAIN_SERVER_HTTPS_PORT = .toUInt() : 40101; +const quint16 DOMAIN_SERVER_EXPORTER_PORT = + QProcessEnvironment::systemEnvironment() + .contains("VIRCADIA_DOMAIN_SERVER_EXPORTER_PORT") + ? QProcessEnvironment::systemEnvironment() + .value("VIRCADIA_DOMAIN_SERVER_EXPORTER_PORT") + .toUInt() + : 9703; + + const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5; class DomainHandler : public QObject { @@ -115,6 +125,7 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); + bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; } bool getInterstitialModeEnabled() const; void setInterstitialModeEnabled(bool enableInterstitialMode); @@ -163,14 +174,14 @@ public: * The communications protocols of the domain and your Interface are not the same. * * - * LoginError + * LoginErrorMetaverse * 2 - * You could not be logged into the domain. + * You could not be logged into the domain per your metaverse login. * * - * NotAuthorized + * NotAuthorizedMetaverse * 3 - * You are not authorized to connect to the domain. + * You are not authorized to connect to the domain per your metaverse login. * * * TooManyUsers @@ -182,6 +193,16 @@ public: * 5 * Connecting to the domain timed out. * + * + * LoginErrorDomain + * 2 + * You could not be logged into the domain per your domain login. + * + * + * NotAuthorizedDomain + * 6 + * You are not authorized to connect to the domain per your domain login. + * * * * @typedef {number} Window.ConnectionRefusedReason @@ -189,10 +210,12 @@ public: enum class ConnectionRefusedReason : uint8_t { Unknown, ProtocolMismatch, - LoginError, - NotAuthorized, + LoginErrorMetaverse, + NotAuthorizedMetaverse, TooManyUsers, - TimedOut + TimedOut, + LoginErrorDomain, + NotAuthorizedDomain }; public slots: @@ -238,7 +261,8 @@ signals: void limitOfSilentDomainCheckInsReached(); private: - bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode); + bool reasonSuggestsMetaverseLogin(ConnectionRefusedReason reasonCode); + bool reasonSuggestsDomainLogin(ConnectionRefusedReason reasonCode); void sendDisconnectPacket(); void hardReset(QString reason); @@ -269,6 +293,7 @@ private: QSet _domainConnectionRefusals; bool _hasCheckedForAccessToken { false }; + bool _hasCheckedForDomainAccessToken { false }; int _connectionDenialsSinceKeypairRegen { 0 }; int _checkInPacketsSinceLastReply { 0 }; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 50221a136a..b13b66f740 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -21,6 +21,7 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" +#include "NetworkingConstants.h" HTTPResourceRequest::~HTTPResourceRequest() { if (_reply) { @@ -54,7 +55,7 @@ void HTTPResourceRequest::doSend() { QNetworkRequest networkRequest(_url); networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); if (_cacheEnabled) { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); diff --git a/libraries/networking/src/MetaverseAPI.cpp b/libraries/networking/src/MetaverseAPI.cpp index 0fb0bcecad..73316ecda3 100644 --- a/libraries/networking/src/MetaverseAPI.cpp +++ b/libraries/networking/src/MetaverseAPI.cpp @@ -3,7 +3,7 @@ // libraries/networking/src // // Created by Kalila (kasenvr) on 2019-12-16. -// Copyright 2019 Project Athena +// Copyright 2019 Vircadia // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/networking/src/MetaverseAPI.h b/libraries/networking/src/MetaverseAPI.h index 423f465229..026f8d8b70 100644 --- a/libraries/networking/src/MetaverseAPI.h +++ b/libraries/networking/src/MetaverseAPI.h @@ -3,7 +3,7 @@ // libraries/networking/src // // Created by Kalila (kasenvr) on 2019-12-16. -// Copyright 2019 Project Athena +// Copyright 2019 Vircadia // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index fabf8b0e44..edc1c1a1ef 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -26,7 +26,37 @@ namespace NetworkingConstants { // at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true const QUrl METAVERSE_SERVER_URL_STABLE { "https://metaverse.highfidelity.com" }; - const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging.projectathena.io" }; + const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging-metaverse.vircadia.com" }; + + // Web Engine requests to this parent domain have an account authorization header added + const QString AUTH_HOSTNAME_BASE = "highfidelity.com"; + const QStringList IS_AUTHABLE_HOSTNAME = { "highfidelity.com", "highfidelity.io" }; + + // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. + const QByteArray VIRCADIA_USER_AGENT = "Mozilla/5.0 (VircadiaInterface)"; + + const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (VircadiaInterface)"; + const QString METAVERSE_USER_AGENT = "Chrome/48.0 (VircadiaInterface)"; + const QString MOBILE_USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36"; + + const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); + const QUrl MASTER_BUILDS_XML_URL("https://highfidelity.com/dev-builds.xml"); + + +#if USE_STABLE_GLOBAL_SERVICES + const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; +#else + const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com"; +#endif + + const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; + + const QUrl HELP_DOCS_URL { "https://docs.vircadia.dev" }; + const QUrl HELP_FORUM_URL { "https://forums.vircadia.dev" }; + const QUrl HELP_SCRIPTING_REFERENCE_URL{ "https://apidocs.vircadia.dev/" }; + const QUrl HELP_RELEASE_NOTES_URL{ "https://docs.vircadia.dev/release-notes.html" }; + const QUrl HELP_BUG_REPORT_URL{ "https://github.com/kasenvr/project-athena/issues" }; + } const QString HIFI_URL_SCHEME_ABOUT = "about"; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 2c584b1c48..e02a8dd56e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2/15/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -33,6 +34,7 @@ #include "AddressManager.h" #include "Assignment.h" #include "AudioHelpers.h" +#include "DomainAccountManager.h" #include "HifiSockAddr.h" #include "FingerprintUtils.h" @@ -103,6 +105,13 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) // clear our NodeList when logout is requested connect(accountManager.data(), &AccountManager::logoutComplete , this, [this]{ reset("Logged out"); }); + // Only used in Interface. + auto domainAccountManager = DependencyManager::get(); + if (domainAccountManager) { + _hasDomainAccountManager = true; + connect(domainAccountManager.data(), &DomainAccountManager::newTokens, this, &NodeList::sendDomainServerCheckIn); + } + // anytime we get a new node we will want to attempt to punch to it connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); connect(this, &LimitedNodeList::nodeSocketUpdated, this, &NodeList::startNodeHolePunch); @@ -379,6 +388,7 @@ void NodeList::sendDomainServerCheckIn() { if (domainPacketType == PacketType::DomainConnectRequest) { #if (PR_BUILD || DEV_BUILD) + // ####### if (_shouldSendNewerVersion) { domainPacket->setVersion(versionForPacketType(domainPacketType) + 1); } @@ -466,6 +476,7 @@ void NodeList::sendDomainServerCheckIn() { packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList(); packetStream << DependencyManager::get()->getPlaceName(); + // ####### TODO: Also send if need to send new domainLogin data? if (!domainIsConnected) { DataServerAccountInfo& accountInfo = accountManager->getAccountInfo(); packetStream << accountInfo.getUsername(); @@ -474,6 +485,23 @@ void NodeList::sendDomainServerCheckIn() { if (requiresUsernameSignature && accountManager->getAccountInfo().hasPrivateKey()) { const QByteArray& usernameSignature = accountManager->getAccountInfo().getUsernameSignature(connectionToken); packetStream << usernameSignature; + } else { + // ####### TODO: Only append if are going to send domain username? + packetStream << QString(""); // Placeholder in case have domain username. + } + } else { + // ####### TODO: Only append if are going to send domainUsername? + packetStream << QString("") << QString(""); // Placeholders in case have domain username. + } + + // Send domain domain login data from Interface to domain server. + if (_hasDomainAccountManager) { + auto domainAccountManager = DependencyManager::get(); + if (!domainAccountManager->getUsername().isEmpty()) { + packetStream << domainAccountManager->getUsername(); + if (!domainAccountManager->getAccessToken().isEmpty()) { + packetStream << (domainAccountManager->getAccessToken() + ":" + domainAccountManager->getRefreshToken()); + } } } diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index c377ea89cb..4954c53c84 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -196,6 +196,8 @@ private: #if (PR_BUILD || DEV_BUILD) bool _shouldSendNewerVersion { false }; #endif + + bool _hasDomainAccountManager { false }; }; #endif // hifi_NodeList_h diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 583c1b29ac..82c008feef 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -4,6 +4,7 @@ // // Created by Seth Alves on 2016-6-1. // Copyright 2016 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -51,6 +52,9 @@ public: void setVerifiedUserName(QString userName) { _verifiedUserName = userName.toLower(); } const QString& getVerifiedUserName() const { return _verifiedUserName; } + void setVerifiedDomainUserName(QString userName) { _verifiedDomainUserName = userName.toLower(); } + const QString& getVerifiedDomainUserName() const { return _verifiedDomainUserName; } + void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; }} QUuid getGroupID() const { return _groupID; } bool isGroup() const { return _groupIDSet; } @@ -99,6 +103,7 @@ protected: QString _id; QUuid _rankID { QUuid() }; // 0 unless this is for a group QString _verifiedUserName; + QString _verifiedDomainUserName; bool _groupIDSet { false }; QUuid _groupID; diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp index b1e04da4b6..a036588646 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.cpp +++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp @@ -39,7 +39,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O && req.url().host() == MetaverseAPI::getCurrentMetaverseServerURL().host()) { QNetworkRequest authenticatedRequest(req); authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); authenticatedRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue()); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 4213d92fc0..35261298b9 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -91,10 +91,11 @@ private: class ScriptableResource : public QObject { /**jsdoc - * Information about a cached resource. Created by {@link AnimationCache.prefetch}, {@link ModelCache.prefetch}, - * {@link SoundCache.prefetch}, or {@link TextureCache.prefetch}. + * Information about a cached resource. Created by {@link AnimationCache.prefetch}, {@link MaterialCache.prefetch}, + * {@link ModelCache.prefetch}, {@link SoundCache.prefetch}, or {@link TextureCache.prefetch}. * * @class ResourceObject + * @hideconstructor * * @hifi-interface * @hifi-client-entity @@ -318,9 +319,11 @@ class ScriptableResourceCache : public QObject { Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty) /**jsdoc - * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource managers). Read-only. - * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource managers). Read-only. - */ + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers). + * Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers). + * Read-only. + */ Q_PROPERTY(size_t numGlobalQueriesPending READ getNumGlobalQueriesPending NOTIFY dirty) Q_PROPERTY(size_t numGlobalQueriesLoading READ getNumGlobalQueriesLoading NOTIFY dirty) @@ -332,7 +335,7 @@ public: * @function ResourceCache.getResourceList * @returns {string[]} The URLs of all resources in the cache. * @example Report cached resources. - * // Replace AnimationCache with ModelCache, SoundCache, or TextureCache as appropriate. + * // Replace AnimationCache with MaterialCache, ModelCache, SoundCache, or TextureCache as appropriate. * * var cachedResources = AnimationCache.getResourceList(); * print("Cached resources: " + JSON.stringify(cachedResources)); @@ -352,7 +355,7 @@ public: * @param {string} url - The URL of the resource to prefetch. * @returns {ResourceObject} A resource object. * @example Prefetch a resource and wait until it has loaded. - * // Replace AnimationCache with ModelCache, SoundCache, or TextureCache as appropriate. + * // Replace AnimationCache with MaterialCache, ModelCache, SoundCache, or TextureCache as appropriate. * // TextureCache has its own version of this function. * * var resourceURL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx"; diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index f4f5525ddc..0b9d5a2d60 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -25,6 +25,7 @@ #include "HTTPResourceRequest.h" #include "NetworkAccessManager.h" #include "NetworkLogging.h" +#include "NetworkingConstants.h" ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(atpSupportEnabled) { _thread.setObjectName("Resource Manager Thread"); @@ -157,7 +158,7 @@ bool ResourceManager::resourceExists(const QUrl& url) { QNetworkRequest request{ url }; request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); auto reply = networkAccessManager.head(request); diff --git a/libraries/networking/src/SandboxUtils.cpp b/libraries/networking/src/SandboxUtils.cpp index 4a348b0662..e3ef2a787d 100644 --- a/libraries/networking/src/SandboxUtils.cpp +++ b/libraries/networking/src/SandboxUtils.cpp @@ -22,6 +22,7 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" +#include "NetworkingConstants.h" namespace SandboxUtils { @@ -29,7 +30,7 @@ QNetworkReply* getStatus() { auto& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL); sandboxStatus.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); return networkAccessManager.get(sandboxStatus); } diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 269ff94b80..94d6371ba4 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -34,6 +34,10 @@ void UserActivityLogger::disable(bool disable) { _disabled.set(disable); } +void UserActivityLogger::crashMonitorDisable(bool disable) { + _crashMonitorDisabled.set(disable); +} + void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCallbackParameters params) { // qCDebug(networking).nospace() << ">>> UserActivityLogger::logAction(" << action << "," << QJsonDocument(details).toJson(); // This logs what the UserActivityLogger would normally send to centralized servers. diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index e4b91b1e81..39efee03c4 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -35,7 +35,11 @@ public slots: bool isEnabled() { return !_disabled.get(); } bool isDisabledSettingSet() const { return _disabled.isSet(); } + bool isCrashMonitorEnabled() { return !_crashMonitorDisabled.get(); } + bool isCrashMonitorDisabledSettingSet() const { return _crashMonitorDisabled.isSet(); } + void disable(bool disable); + void crashMonitorDisable(bool disable); void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); void launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime); @@ -55,6 +59,7 @@ private slots: private: UserActivityLogger(); Setting::Handle _disabled { "UserActivityLoggerDisabled", true }; + Setting::Handle _crashMonitorDisabled { "CrashMonitorDisabled2", true }; QElapsedTimer _timer; }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index fbf575065e..a34be09db7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -135,6 +135,7 @@ public: AudioSoloRequest, BulkAvatarTraitsAck, StopInjector, + AvatarZonePresence, NUM_PACKET_TYPE }; @@ -185,7 +186,8 @@ public: << PacketTypeEnum::Value::OctreeFileReplacement << PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho << PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio << PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity - << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData; + << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData + << PacketTypeEnum::Value::AvatarZonePresence; return NON_SOURCED_PACKETS; } @@ -210,10 +212,11 @@ using PacketType = PacketTypeEnum::Value; const int NUM_BYTES_MD5_HASH = 16; -typedef char PacketVersion; +// NOTE: There is a max limit of 255, hopefully we have a better way to manage this by then. +typedef uint8_t PacketVersion; PacketVersion versionForPacketType(PacketType packetType); -QByteArray protocolVersionsSignature(); /// returns a unqiue signature for all the current protocols +QByteArray protocolVersionsSignature(); /// returns a unique signature for all the current protocols QString protocolVersionsSignatureBase64(); #if (PR_BUILD || DEV_BUILD) @@ -224,7 +227,7 @@ uint qHash(const PacketType& key, uint seed); QDebug operator<<(QDebug debug, const PacketType& type); // Due to the different legacy behaviour, we need special processing for domains that were created before -// the zone inheritance modes were added. These have version numbers up to 80 +// the zone inheritance modes were added. These have version numbers up to 80. enum class EntityVersion : PacketVersion { StrokeColorProperty = 0, HasDynamicOwnershipTests, @@ -275,6 +278,9 @@ enum class EntityVersion : PacketVersion { ShadowBiasAndDistance, TextEntityFonts, ScriptServerKinematicMotion, + ScreenshareZone, + ZoneOcclusion, + ModelBlendshapes, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index aac0de27a1..897ac142bf 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -734,7 +734,8 @@ QString getMarketplaceID(const QString& urlString) { bool Octree::readFromURL( const QString& urlString, const bool isObservable, - const qint64 callerId + const qint64 callerId, + const bool isImport ) { QString trimmedUrl = urlString.trimmed(); QString marketplaceID = getMarketplaceID(trimmedUrl); @@ -766,7 +767,7 @@ bool Octree::readFromURL( } QDataStream inputStream(data); - return readFromStream(data.size(), inputStream, marketplaceID); + return readFromStream(data.size(), inputStream, marketplaceID, isImport); } bool Octree::readFromByteArray( @@ -791,7 +792,8 @@ bool Octree::readFromByteArray( bool Octree::readFromStream( uint64_t streamLength, QDataStream& inputStream, - const QString& marketplaceID + const QString& marketplaceID, + const bool isImport ) { // decide if this is binary SVO or JSON-formatted SVO QIODevice *device = inputStream.device(); @@ -804,7 +806,7 @@ bool Octree::readFromStream( return false; } else { qCDebug(octree) << "Reading from JSON SVO Stream length:" << streamLength; - return readJSONFromStream(streamLength, inputStream, marketplaceID); + return readJSONFromStream(streamLength, inputStream, marketplaceID, isImport); } } @@ -834,7 +836,8 @@ const int READ_JSON_BUFFER_SIZE = 2048; bool Octree::readJSONFromStream( uint64_t streamLength, QDataStream& inputStream, - const QString& marketplaceID /*=""*/ + const QString& marketplaceID, /*=""*/ + const bool isImport ) { // if the data is gzipped we may not have a useful bytesAvailable() result, so just keep reading until // we get an eof. Leave streamLength parameter for consistency. @@ -866,7 +869,7 @@ bool Octree::readJSONFromStream( addMarketplaceIDToDocumentEntities(asMap, marketplaceID); } - bool success = readFromMap(asMap); + bool success = readFromMap(asMap, isImport); delete[] rawData; return success; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 45dae3049d..a7885801de 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -216,13 +216,12 @@ public: // Octree importers bool readFromFile(const char* filename); - bool readFromURL(const QString& url, const bool isObservable = true, const qint64 callerId = -1); // will support file urls as well... + bool readFromURL(const QString& url, const bool isObservable = true, const qint64 callerId = -1, const bool isImport = false); // will support file urls as well... bool readFromByteArray(const QString& url, const QByteArray& byteArray); - bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); - bool readSVOFromStream(uint64_t streamLength, QDataStream& inputStream); - bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); + bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID="", const bool isImport = false); + bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID="", const bool isImport = false); bool readJSONFromGzippedFile(QString qFileName); - virtual bool readFromMap(QVariantMap& entityDescription) = 0; + virtual bool readFromMap(QVariantMap& entityDescription, const bool isImport = false) = 0; uint64_t getOctreeElementsCount(); diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 8ab502e951..5e6825c886 100755 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -482,6 +482,19 @@ bool OctreePacketData::appendValue(const QVector& value) { return success; } +bool OctreePacketData::appendValue(const QVector& value) { + uint16_t qVecSize = value.size(); + bool success = appendValue(qVecSize); + if (success) { + success = append((const unsigned char*)value.constData(), qVecSize * sizeof(QUuid)); + if (success) { + _bytesOfValues += qVecSize * sizeof(QUuid); + _totalBytesOfValues += qVecSize * sizeof(QUuid); + } + } + return success; +} + bool OctreePacketData::appendValue(const glm::quat& value) { const size_t VALUES_PER_QUAT = 4; const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT; @@ -774,6 +787,15 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto return (dataBytes - start) + (int)sizeof(uint16_t); } +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVector& result) { + uint16_t length; + memcpy(&length, dataBytes, sizeof(uint16_t)); + dataBytes += sizeof(length); + result.resize(length); + memcpy(result.data(), dataBytes, length * sizeof(QUuid)); + return sizeof(uint16_t) + length * sizeof(QUuid); +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 46e5de9bda..2050dd1487 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -186,6 +186,9 @@ public: /// appends a QVector of bools to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QVector& value); + /// appends a QVector of QUuids to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const QVector& value); + /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::quat& value); @@ -284,6 +287,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QRect& result); diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index a796ea28a0..e222692aea 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -543,12 +543,8 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const _minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); _maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius); - if (_physicsEngine) { - // must REMOVE from world prior to shape update - _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION; - } - _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_ADD_DETAILED_TO_SIMULATION; + _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE | PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION | + PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_ADD_DETAILED_TO_SIMULATION; } // it's ok to change offset immediately -- there are no thread safety issues here diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e48f0603bd..f3d129871f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -62,7 +62,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer // rather than pass the legit shape pointer to the ObjectMotionState ctor above. setShape(shape); - if (_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) { + if (_entity->isAvatarEntity() && !_entity->isMyAvatarEntity()) { // avatar entities are always thus, so we cache this fact in _ownershipState _ownershipState = EntityMotionState::OwnershipState::Unownable; } @@ -407,8 +407,8 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. - // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor - assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); + // this case is prevented by setting _ownershipState to OwnershipState::Unownable in EntityMotionState::ctor + assert(!(_entity->isAvatarEntity() && !_entity->isMyAvatarEntity())); if (_entity->getTransitingWithAvatar()) { return false; @@ -768,7 +768,7 @@ uint8_t EntityMotionState::computeFinalBidPriority() const { } bool EntityMotionState::isLocallyOwned() const { - return _entity->getSimulatorID() == Physics::getSessionUUID(); + return _entity->getSimulatorID() == Physics::getSessionUUID() || _entity->isMyAvatarEntity(); } bool EntityMotionState::isLocallyOwnedOrShouldBe() const { @@ -786,13 +786,21 @@ void EntityMotionState::setRegion(uint8_t region) { } void EntityMotionState::initForBid() { - assert(_ownershipState != EntityMotionState::OwnershipState::Unownable); - _ownershipState = EntityMotionState::OwnershipState::PendingBid; + if (_ownershipState != EntityMotionState::OwnershipState::Unownable) { + _ownershipState = EntityMotionState::OwnershipState::PendingBid; + } } void EntityMotionState::initForOwned() { - assert(_ownershipState != EntityMotionState::OwnershipState::Unownable); - _ownershipState = EntityMotionState::OwnershipState::LocallyOwned; + if (_ownershipState != EntityMotionState::OwnershipState::Unownable) { + _ownershipState = EntityMotionState::OwnershipState::LocallyOwned; + } +} + +void EntityMotionState::clearOwnershipState() { + if (_ownershipState != OwnershipState::Unownable) { + _ownershipState = OwnershipState::NotLocallyOwned; + } } void EntityMotionState::clearObjectVelocities() const { diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 7456837777..be6f5c0658 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -107,7 +107,7 @@ protected: uint64_t getNextBidExpiry() const { return _nextBidExpiry; } void initForBid(); void initForOwned(); - void clearOwnershipState() { _ownershipState = OwnershipState::NotLocallyOwned; } + void clearOwnershipState(); void updateServerPhysicsVariables(); bool remoteSimulationOutOfSync(uint32_t simulationStep); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index df8c3fa32e..4a2ee9184f 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -40,14 +40,9 @@ void PhysicalEntitySimulation::init( } // begin EntitySimulation overrides -void PhysicalEntitySimulation::updateEntitiesInternal(uint64_t now) { - // Do nothing here because the "internal" update the PhysicsEngine::stepSimulation() which is done elsewhere. -} - -void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { - QMutexLocker lock(&_mutex); - assert(entity); - assert(!entity->isDead()); +void PhysicalEntitySimulation::addEntityToInternalLists(EntityItemPointer entity) { + EntitySimulation::addEntityToInternalLists(entity); + entity->deserializeActions(); // TODO: do this elsewhere uint8_t region = _space->getRegion(entity->getSpaceIndex()); bool maybeShouldBePhysical = (region < workload::Region::R3 || region == workload::Region::UNKNOWN) && entity->shouldBePhysical(); bool canBeKinematic = region <= workload::Region::R3; @@ -66,23 +61,20 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { } } -void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) { - if (entity->isSimulated()) { - EntitySimulation::removeEntityInternal(entity); - _entitiesToAddToPhysics.remove(entity); - - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState) { - removeOwnershipData(motionState); - _entitiesToRemoveFromPhysics.insert(entity); - } - if (entity->isDead() && entity->getElement()) { - _deadEntities.insert(entity); - } +void PhysicalEntitySimulation::removeEntityFromInternalLists(EntityItemPointer entity) { + _entitiesToAddToPhysics.remove(entity); + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + removeOwnershipData(motionState); + _entitiesToRemoveFromPhysics.insert(entity); + } + if (entity->isDead() && entity->getElement()) { + _deadEntitiesToRemoveFromTree.insert(entity); } if (entity->isAvatarEntity()) { _deadAvatarEntities.insert(entity); } + EntitySimulation::removeEntityFromInternalLists(entity); } void PhysicalEntitySimulation::removeOwnershipData(EntityMotionState* motionState) { @@ -115,18 +107,6 @@ void PhysicalEntitySimulation::clearOwnershipData() { _bids.clear(); } -void PhysicalEntitySimulation::takeDeadEntities(SetOfEntities& deadEntities) { - QMutexLocker lock(&_mutex); - for (auto entity : _deadEntities) { - EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState) { - _entitiesToRemoveFromPhysics.insert(entity); - } - } - _deadEntities.swap(deadEntities); - _deadEntities.clear(); -} - void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntities) { _deadAvatarEntities.swap(deadEntities); _deadAvatarEntities.clear(); @@ -190,11 +170,43 @@ void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& ent } } -void PhysicalEntitySimulation::clearEntitiesInternal() { +void PhysicalEntitySimulation::processDeadEntities() { + // Note: this override is a complete rewite of the base class's method because we cannot assume all entities + // are domain entities, and the consequence of trying to delete a domain-entity in this case is very different. + if (_deadEntitiesToRemoveFromTree.empty()) { + return; + } + PROFILE_RANGE(simulation_physics, "Deletes"); + std::vector entitiesToDeleteImmediately; + entitiesToDeleteImmediately.reserve(_deadEntitiesToRemoveFromTree.size()); + QUuid sessionID = Physics::getSessionUUID(); + QMutexLocker lock(&_mutex); + for (auto entity : _deadEntitiesToRemoveFromTree) { + EntityMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + _entitiesToRemoveFromPhysics.insert(entity); + } + if (entity->isDomainEntity()) { + // interface-client can't delete domainEntities outright, they must roundtrip through the entity-server + _entityPacketSender->queueEraseEntityMessage(entity->getID()); + } else if (entity->isLocalEntity() || entity->isMyAvatarEntity()) { + entitiesToDeleteImmediately.push_back(entity); + entity->collectChildrenForDelete(entitiesToDeleteImmediately, sessionID); + } + } + _deadEntitiesToRemoveFromTree.clear(); + + if (!entitiesToDeleteImmediately.empty()) { + getEntityTree()->deleteEntitiesByPointer(entitiesToDeleteImmediately); + } +} + +void PhysicalEntitySimulation::clearEntities() { // TODO: we should probably wait to lock the _physicsEngine so we don't mess up data structures // while it is in the middle of a simulation step. As it is, we're probably in shutdown mode // anyway, so maybe the simulation was already properly shutdown? Cross our fingers... + QMutexLocker lock(&_mutex); // remove the objects (aka MotionStates) from physics _physicsEngine->removeSetOfObjects(_physicalObjects); @@ -216,11 +228,20 @@ void PhysicalEntitySimulation::clearEntitiesInternal() { _entitiesToAddToPhysics.clear(); _incomingChanges.clear(); _entitiesToDeleteLater.clear(); + + EntitySimulation::clearEntities(); +} + +void PhysicalEntitySimulation::queueEraseDomainEntity(const QUuid& id) const { + if (_entityPacketSender) { + _entityPacketSender->queueEraseEntityMessage(id); + } } // virtual void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) { - // this can be called on any thread + // DANGER! this can be called on any thread + // do no dirty deeds here --> assemble list for later assert(entity); assert(entity->isDead()); QMutexLocker lock(&_mutex); @@ -228,11 +249,11 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity) } void PhysicalEntitySimulation::removeDeadEntities() { - // only ever call this on the main thread + // DANGER! only ever call this on the main thread QMutexLocker lock(&_mutex); for (auto& entity : _entitiesToDeleteLater) { entity->clearActions(getThisPointer()); - removeEntityInternal(entity); + EntitySimulation::prepareEntityForDelete(entity); } _entitiesToDeleteLater.clear(); } @@ -647,10 +668,16 @@ void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) { "dynamic that was already in _physicsEngine"; } } - EntitySimulation::addDynamic(dynamic); + QMutexLocker lock(&_dynamicsMutex); + _dynamicsToAdd += dynamic; } } +void PhysicalEntitySimulation::removeDynamic(const QUuid dynamicID) { + QMutexLocker lock(&_dynamicsMutex); + _dynamicsToRemove += dynamicID; +} + void PhysicalEntitySimulation::applyDynamicChanges() { QList dynamicsFailedToAdd; if (_physicsEngine) { @@ -665,8 +692,8 @@ void PhysicalEntitySimulation::applyDynamicChanges() { } } } - // applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd - EntitySimulation::applyDynamicChanges(); + _dynamicsToAdd.clear(); + _dynamicsToRemove.clear(); } // put back the ones that couldn't yet be added diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index f5213f7fef..0f0a8e9295 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -58,22 +59,24 @@ public: void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender); void setWorkloadSpace(const workload::SpacePointer space) { _space = space; } - virtual void addDynamic(EntityDynamicPointer dynamic) override; - virtual void applyDynamicChanges() override; + void addDynamic(EntityDynamicPointer dynamic) override; + void removeDynamic(const QUuid dynamicID) override; + void applyDynamicChanges() override; - virtual void takeDeadEntities(SetOfEntities& deadEntities) override; void takeDeadAvatarEntities(SetOfEntities& deadEntities); + virtual void clearEntities() override; + void queueEraseDomainEntity(const QUuid& id) const override; + signals: void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); protected: // only called by EntitySimulation // overrides for EntitySimulation - virtual void updateEntitiesInternal(uint64_t now) override; - virtual void addEntityInternal(EntityItemPointer entity) override; - virtual void removeEntityInternal(EntityItemPointer entity) override; + void addEntityToInternalLists(EntityItemPointer entity) override; + void removeEntityFromInternalLists(EntityItemPointer entity) override; void processChangedEntity(const EntityItemPointer& entity) override; - virtual void clearEntitiesInternal() override; + void processDeadEntities() override; void removeOwnershipData(EntityMotionState* motionState); void clearOwnershipData(); @@ -121,8 +124,13 @@ private: VectorOfEntityMotionStates _owned; VectorOfEntityMotionStates _bids; - SetOfEntities _deadAvatarEntities; + SetOfEntities _deadAvatarEntities; // to remove from Avatar's lists std::vector _entitiesToDeleteLater; + + QList _dynamicsToAdd; + QSet _dynamicsToRemove; + QMutex _dynamicsMutex { QMutex::Recursive }; + workload::SpacePointer _space; uint64_t _nextBidExpiry; uint32_t _lastStepSendPackets { 0 }; diff --git a/libraries/plugins/src/plugins/SteamClientPlugin.h b/libraries/plugins/src/plugins/SteamClientPlugin.h index 07e320f8eb..e4d765882f 100644 --- a/libraries/plugins/src/plugins/SteamClientPlugin.h +++ b/libraries/plugins/src/plugins/SteamClientPlugin.h @@ -73,7 +73,7 @@ public slots: /**jsdoc * Opens Steam's "Choose Friends to invite" dialog if Interface is running under Steam. * @function Steam.openInviteOverlay - * @example Invite Steam friends to join you in High Fidelity. + * @example Invite Steam friends to join you in Vircadia. * if (Steam.running) { * print("Invite Steam friends to joint you..."); * Steam.openInviteOverlay(); diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index e4257140fd..9e6c5f9898 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -93,7 +93,7 @@ public: * collisions. */ - /**jsdoc + /**jsdoc *

A type of pick.

* * diff --git a/libraries/procedural/src/procedural/MaterialCacheScriptingInterface.h b/libraries/procedural/src/procedural/MaterialCacheScriptingInterface.h index 3a13652aec..2e0d319b7a 100644 --- a/libraries/procedural/src/procedural/MaterialCacheScriptingInterface.h +++ b/libraries/procedural/src/procedural/MaterialCacheScriptingInterface.h @@ -24,7 +24,7 @@ class MaterialCacheScriptingInterface : public ScriptableResourceCache, public D // Properties are copied over from ResourceCache (see ResourceCache.h for reason). /**jsdoc - * The TextureCache API manages texture cache resources. + * The MaterialCache API manages material cache resources. * * @namespace MaterialCache * @@ -36,6 +36,10 @@ class MaterialCacheScriptingInterface : public ScriptableResourceCache, public D * @property {number} numCached - Total number of cached resource. Read-only. * @property {number} sizeTotal - Size in bytes of all resources. Read-only. * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers). + * Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers). + * Read-only. * * @borrows ResourceCache.getResourceList as getResourceList * @borrows ResourceCache.updateTotalSize as updateTotalSize diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index 89f21218e6..9b3d0a9bd4 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -27,7 +27,6 @@ using UniformLambdas = std::list>; const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 }; /**jsdoc - * An object containing user-defined uniforms for communicating data to shaders. * @typedef {object} ProceduralUniforms */ diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp index a97cb294b4..130f11f3ef 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp @@ -113,9 +113,9 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater /**jsdoc * A material used in a {@link Entities.MaterialResource|MaterialResource}. * @typedef {object} Entities.Material + * @property {string} name="" - A name for the material. Supported by all material models. * @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes. * Supported models are: "hifi_pbr", "hifi_shader_simple". - * @property {string} name="" - A name for the material. Supported by all material models. * @property {ColorFloat|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A * {@link ColorFloat} value is treated as sRGB and must have component values in the range 0.0 – * 1.0. A {@link RGBS} value can be either RGB or sRGB. @@ -144,12 +144,24 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * value for transparency. * "hifi_pbr" model only. * @property {string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: - * "OPACITY_MAP_OPAQUE" for ignoring the opacity map information. - * "OPACITY_MAP_MASK" for using the opacity map as a mask, where only the texel greater than opacityCutoff are visible and rendered opaque. - * "OPACITY_MAP_BLEND" for using the opacity map for alpha blending the material surface with the background. + *
    + *
  • "OPACITY_MAP_OPAQUE" for ignoring the opacity map information.
  • + *
  • "OPACITY_MAP_MASK" for using the opacityMap as a mask, where only the texel greater + * than opacityCutoff are visible and rendered opaque.
  • + *
  • "OPACITY_MAP_BLEND" for using the opacityMap for alpha blending the material surface + * with the background.
  • + *
* Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the Opacity map - * when opacityMapMode is "OPACITY_MAP_MASK", range 0.01.0. + * @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the + * opacityMap when opacityMapMode is "OPACITY_MAP_MASK". Range 0.0 + * – 1.0. + * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * @property {string} cullFaceMode="CULL_BACK" - The mode defining which side of the geometry should be rendered. Values can be: + *
    + *
  • "CULL_NONE" to render both sides of the geometry.
  • + *
  • "CULL_FRONT" to cull the front faces of the geometry.
  • + *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • + *
* Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. * @property {string} cullFaceMode - The mode defining which side of the geometry should be rendered. Values can be: *
    @@ -186,7 +198,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. * @property {Mat4|string} texCoordTransform1 - The transform to use for occlusionMap and lightMap. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} lightmapParams - Parameters for controlling how lightMap is used. + * @property {string} lightmapParams - Parameters for controlling how lightMap is used. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. *

    Currently not used.

    * @property {string} materialParams - Parameters for controlling the material projection and repetition. diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 7f42b05fa4..94b7661b2f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -175,12 +175,7 @@ void CauterizedModel::updateClusterMatrices() { } } - // post the blender if we're not currently waiting for one to finish - auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { - _blendedBlendshapeCoefficients = _blendshapeCoefficients; - modelBlender->noteRequiresBlend(getThisPointer()); - } + updateBlendshapes(); } void CauterizedModel::updateRenderItems() { diff --git a/libraries/render-utils/src/CullFace.slh b/libraries/render-utils/src/CullFace.slh new file mode 100644 index 0000000000..b077095fc7 --- /dev/null +++ b/libraries/render-utils/src/CullFace.slh @@ -0,0 +1,20 @@ + +<@if not CULL_FACE_SLH@> +<@def CULL_FACE_SLH@> + +// NOTE: this calculation happens once per fragment. this could be optimized by creating different shaders (via defines) +// for front, back, and double-sided. for front/back-only triangles, this will simplify to always 1 or always -1 +vec3 evalFrontOrBackFaceNormal(vec3 normal) { + return (2.0 * float(gl_FrontFacing) - 1.0) * normal; +} + +<@endif@> diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 938efbef52..5a8b09b018 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -193,8 +193,8 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade(), - ShapeKey::Builder().withOwnPipeline().withDeformed(), ShapeKey::Builder().withOwnPipeline().withDeformed().withFade(), - ShapeKey::Builder().withOwnPipeline().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withOwnPipeline().withDeformed().withDualQuatSkinned().withFade(), + ShapeKey::Builder().withDeformed().withOwnPipeline(), ShapeKey::Builder().withDeformed().withOwnPipeline().withFade(), + ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline().withFade(), }; std::vector> sortedShapeKeys(keys.size()); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e82af5395f..d150e18406 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -55,6 +55,13 @@ template <> const ShapeKey shapeGetShapeKey(const MeshPartPayload::Pointer& payl template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { return payload->render(args); } + +template <> bool payloadPassesZoneOcclusionTest(const MeshPartPayload::Pointer& payload, const std::unordered_set& containingZones) { + if (payload) { + return payload->passesZoneOcclusionTest(containingZones); + } + return false; +} } MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material, const uint64_t& created) : @@ -167,10 +174,23 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputStream(0, _drawMesh->getVertexStream()); } - void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { +void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { batch.setModelTransform(_drawTransform); } +bool MeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& containingZones) const { + if (!_renderWithZones.isEmpty()) { + if (!containingZones.empty()) { + for (auto renderWithZone : _renderWithZones) { + if (containingZones.find(renderWithZone) != containingZones.end()) { + return true; + } + } + } + return false; + } + return true; +} void MeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("MeshPartPayload::render"); @@ -242,6 +262,12 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren return payload->render(args); } +template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones) { + if (payload) { + return payload->passesZoneOcclusionTest(containingZones); + } + return false; +} } ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, @@ -450,6 +476,7 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, PrimitiveMode pr } } + _prevUseDualQuaternionSkinning = useDualQuaternionSkinning; _shapeKey = builder.build(); } @@ -559,6 +586,14 @@ void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_mapsecond; + if (_isSkinned || (_isBlendShaped && _meshBlendshapeBuffer)) { + ShapeKey::Builder builder(_shapeKey); + builder.withDeformed(); + if (_prevUseDualQuaternionSkinning) { + builder.withDualQuatSkinned(); + } + _shapeKey = builder.build(); + } } } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ee205bd778..8ed799bd74 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -75,11 +75,15 @@ public: void setCullWithParent(bool value) { _cullWithParent = value; } + void setRenderWithZones(const QVector& renderWithZones) { _renderWithZones = renderWithZones; } + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; + static bool enableMaterialProceduralShaders; protected: render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; bool _cullWithParent { false }; + QVector _renderWithZones; uint64_t _created; }; @@ -88,6 +92,7 @@ namespace render { template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const MeshPartPayload::Pointer& payload); template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); + template <> bool payloadPassesZoneOcclusionTest(const MeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); } class ModelMeshPartPayload : public MeshPartPayload { @@ -145,6 +150,7 @@ private: gpu::BufferPointer _meshBlendshapeBuffer; int _meshNumVertices; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; + bool _prevUseDualQuaternionSkinning { false }; bool _cauterized { false }; }; @@ -154,6 +160,7 @@ namespace render { template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); + template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); } #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d8172112ff..6150097f14 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -226,6 +226,7 @@ void Model::updateRenderItems() { modelTransform.setScale(glm::vec3(1.0f)); PrimitiveMode primitiveMode = self->getPrimitiveMode(); + auto renderWithZones = self->getRenderWithZones(); auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); bool cauterized = self->isCauterized(); @@ -241,7 +242,8 @@ void Model::updateRenderItems() { bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, - invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) { + invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, + cauterized, renderWithZones](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions); data.computeAdjustedLocalBound(meshState.clusterDualQuaternions); @@ -268,6 +270,7 @@ void Model::updateRenderItems() { data.updateTransformForSkinnedMesh(renderTransform, modelTransform); data.setCauterized(cauterized); + data.setRenderWithZones(renderWithZones); data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); }); @@ -282,11 +285,6 @@ void Model::setRenderItemsNeedUpdate() { emit requestRenderUpdate(); } -void Model::setPrimitiveMode(PrimitiveMode primitiveMode) { - _primitiveMode = primitiveMode; - setRenderItemsNeedUpdate(); -} - void Model::reset() { if (isLoaded()) { const HFMModel& hfmModel = getHFMModel(); @@ -880,8 +878,8 @@ void Model::updateRenderItemsKey(const render::ScenePointer& scene) { } auto renderItemsKey = _renderItemKeyGlobalFlags; render::Transaction transaction; - foreach(auto item, _modelMeshRenderItemsMap.keys()) { - transaction.updateItem(item, [renderItemsKey](ModelMeshPartPayload& data) { + for (auto itemID: _modelMeshRenderItemIDs) { + transaction.updateItem(itemID, [renderItemsKey](ModelMeshPartPayload& data) { data.updateKey(renderItemsKey); }); } @@ -960,13 +958,20 @@ void Model::setCauterized(bool cauterized, const render::ScenePointer& scene) { } } +void Model::setPrimitiveMode(PrimitiveMode primitiveMode) { + if (_primitiveMode != primitiveMode) { + _primitiveMode = primitiveMode; + setRenderItemsNeedUpdate(); + } +} + void Model::setCullWithParent(bool cullWithParent) { if (_cullWithParent != cullWithParent) { _cullWithParent = cullWithParent; render::Transaction transaction; auto renderItemsKey = _renderItemKeyGlobalFlags; - for(auto item : _modelMeshRenderItemIDs) { + for (auto item : _modelMeshRenderItemIDs) { transaction.updateItem(item, [cullWithParent, renderItemsKey](ModelMeshPartPayload& data) { data.setCullWithParent(cullWithParent); data.updateKey(renderItemsKey); @@ -976,6 +981,13 @@ void Model::setCullWithParent(bool cullWithParent) { } } +void Model::setRenderWithZones(const QVector& renderWithZones) { + if (_renderWithZones != renderWithZones) { + _renderWithZones = renderWithZones; + setRenderItemsNeedUpdate(); + } +} + const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { return _renderItemKeyGlobalFlags; } @@ -984,7 +996,9 @@ bool Model::addToScene(const render::ScenePointer& scene, render::Transaction& transaction, render::Item::Status::Getters& statusGetters, BlendShapeOperator modelBlendshapeOperator) { + if (!_addedToScene && isLoaded()) { + updateGeometry(); updateClusterMatrices(); if (_modelMeshRenderItems.empty()) { createRenderItemSet(); @@ -1427,9 +1441,13 @@ void Model::updateClusterMatrices() { } } + updateBlendshapes(); +} + +void Model::updateBlendshapes() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (modelBlender->shouldComputeBlendshapes() && getHFMModel().hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b677861e9f..412a1b7638 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -76,17 +76,6 @@ struct SortedTriangleSet { int subMeshIndex; }; -struct BlendshapeOffsetPacked { - glm::uvec4 packedPosNorTan; -}; - -struct BlendshapeOffsetUnpacked { - glm::vec3 positionOffset; - glm::vec3 normalOffset; - glm::vec3 tangentOffset; -}; - -using BlendshapeOffset = BlendshapeOffsetPacked; using BlendShapeOperator = std::function&, const QVector&, const render::ItemIDs&)>; /// A generic 3D model displaying geometry loaded from a URL. @@ -131,6 +120,9 @@ public: void setCullWithParent(bool value); + void setRenderWithZones(const QVector& renderWithZones); + const QVector& getRenderWithZones() const { return _renderWithZones; } + // Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model. const render::ItemKey getRenderItemKeyGlobalFlags() const; @@ -178,6 +170,7 @@ public: virtual void simulate(float deltaTime, bool fullUpdate = true); virtual void updateClusterMatrices(); + virtual void updateBlendshapes(); /// Returns a reference to the shared geometry. const Geometry::Pointer& getGeometry() const { return _renderGeometry; } @@ -365,6 +358,8 @@ public: void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); + void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } + public slots: void loadURLFinished(bool success); @@ -382,7 +377,6 @@ protected: std::mutex _materialMappingMutex; void applyMaterialMapping(); - void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } /// Clear the joint states @@ -506,6 +500,7 @@ protected: render::ItemKey _renderItemKeyGlobalFlags; bool _cauterized { false }; bool _cullWithParent { false }; + QVector _renderWithZones; bool shouldInvalidatePayloadShapeKey(int meshIndex); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index fe005df2d4..dbfa23a143 100755 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -258,8 +258,8 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade(), - ShapeKey::Builder().withOwnPipeline().withDeformed(), ShapeKey::Builder().withOwnPipeline().withDeformed().withFade(), - ShapeKey::Builder().withOwnPipeline().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withOwnPipeline().withDeformed().withDualQuatSkinned().withFade(), + ShapeKey::Builder().withDeformed().withOwnPipeline(), ShapeKey::Builder().withDeformed().withOwnPipeline().withFade(), + ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withOwnPipeline().withFade(), }; std::vector> sortedShapeKeys(keys.size()); diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 186f9e682a..24d6081743 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -69,10 +69,5 @@ void SoftAttachmentModel::updateClusterMatrices() { } } - // post the blender if we're not currently waiting for one to finish - auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { - _blendedBlendshapeCoefficients = _blendshapeCoefficients; - modelBlender->noteRequiresBlend(getThisPointer()); - } + updateBlendshapes(); } diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index a6cc82e335..98abc29d8c 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -13,6 +13,7 @@ <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> +<@include CullFace.slh@> <@if not HIFI_USE_SHADOW@> <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> @@ -129,7 +130,7 @@ void main(void) { _fragColor0 = vec4(albedo * isUnlitEnabled(), opacity); <@else@> packDeferredFragmentUnlit( - normalize(_normalWS), + evalFrontOrBackFaceNormal(normalize(_normalWS)), opacity, albedo * isUnlitEnabled()); <@endif@> @@ -195,7 +196,7 @@ void main(void) { <@else@> vec3 fragNormalWS = _normalWS; <@endif@> - fragNormalWS = normalize(fragNormalWS); + fragNormalWS = evalFrontOrBackFaceNormal(normalize(fragNormalWS)); <@if HIFI_USE_FORWARD@> TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 9760216682..fabe85cb4f 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -13,6 +13,7 @@ <@include gpu/Color.slh@> <@include DefaultMaterials.slh@> <@include render-utils/ShaderConstants.h@> +<@include CullFace.slh@> <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> <@if not HIFI_USE_UNLIT@> @@ -94,7 +95,7 @@ void main(void) { 1.0, DEFAULT_OCCLUSION, fragPosition, - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), texel.rgb, fresnel, metallic, @@ -111,7 +112,7 @@ void main(void) { 1.0, DEFAULT_OCCLUSION, fragPosition, - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), texel.rgb, fresnel, metallic, @@ -119,7 +120,7 @@ void main(void) { texel.a); <@else@> packDeferredFragment( - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), 1.0, texel.rgb, DEFAULT_ROUGHNESS, @@ -141,7 +142,7 @@ void main(void) { , texel.a); <@else@> packDeferredFragmentUnlit( - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), 1.0, texel.rgb <@if HIFI_USE_FADE@> diff --git a/libraries/render-utils/src/simple_procedural.slf b/libraries/render-utils/src/simple_procedural.slf index 5b0eb62cca..cc8edbb415 100644 --- a/libraries/render-utils/src/simple_procedural.slf +++ b/libraries/render-utils/src/simple_procedural.slf @@ -24,6 +24,7 @@ <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> +<@include CullFace.slh@> <@include render-utils/ShaderConstants.h@> layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _positionMS; @@ -66,7 +67,7 @@ float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition pro #line 2030 void main(void) { - vec3 normal = normalize(_normalWS.xyz) * (2.0 * float(gl_FrontFacing) - 1.0); + vec3 normal = evalFrontOrBackFaceNormal(normalize(_normalWS.xyz)); vec3 diffuse = _color.rgb; vec3 fresnel = DEFAULT_FRESNEL; float roughness = DEFAULT_ROUGHNESS; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 5cb5709252..024be6598d 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -15,6 +15,7 @@ #include "../StencilMaskPass.h" #include "NetworkAccessManager.h" +#include "NetworkingConstants.h" static std::mutex fontMutex; @@ -97,7 +98,7 @@ Font::Pointer Font::load(const QString& family) { QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); networkRequest.setUrl(family); auto networkReply = networkAccessManager.get(networkRequest); diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index a75192bad7..5fee00b370 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -13,15 +13,16 @@ #include #include #include +#include #include #include #include +#include #include #include "Forward.h" - class AABox; namespace render { @@ -142,13 +143,6 @@ namespace render { bool _takingSnapshot { false }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; std::function _stencilMaskOperator; - - float _visionSqueezeX { 0.0f }; - float _visionSqueezeY { 0.0f }; - float _visionSqueezeTransition { 0.15f }; - int _visionSqueezePerEye { 0 }; - float _visionSqueezeGroundPlaneY { 0.0f }; - float _visionSqueezeSpotlightSize { 0.02f }; }; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 8cedbc7f3e..164d8e9f21 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -19,6 +19,9 @@ using namespace render; +std::unordered_set CullTest::_containingZones = std::unordered_set(); +std::unordered_set CullTest::_prevContainingZones = std::unordered_set(); + CullTest::CullTest(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum) : _functor(functor), _args(pargs), @@ -64,46 +67,8 @@ bool CullTest::solidAngleTest(const AABox& bound) { return true; } -void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemBounds& inItems, ItemBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - - RenderArgs* args = renderContext->args; - const ViewFrustum& frustum = args->getViewFrustum(); - - details._considered += (int)inItems.size(); - - // Culling / LOD - for (auto item : inItems) { - if (item.bound.isNull()) { - outItems.emplace_back(item); // One more Item to render - continue; - } - - // TODO: some entity types (like lights) might want to be rendered even - // when they are outside of the view frustum... - bool inView; - { - PerformanceTimer perfTimer("boxIntersectsFrustum"); - inView = frustum.boxIntersectsFrustum(item.bound); - } - if (inView) { - bool bigEnoughToRender; - { - PerformanceTimer perfTimer("shouldRender"); - bigEnoughToRender = cullFunctor(args, item.bound); - } - if (bigEnoughToRender) { - outItems.emplace_back(item); // One more Item to render - } else { - details._tooSmall++; - } - } else { - details._outOfView++; - } - } - details._rendered += (int)outItems.size(); +bool CullTest::zoneOcclusionTest(const render::Item& item) { + return item.passesZoneOcclusionTest(_containingZones); } void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemBounds& outItems) { @@ -117,7 +82,7 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const outItems.reserve(items.size()); for (auto& id : items) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && item.passesZoneOcclusionTest(CullTest::_containingZones)) { outItems.emplace_back(ItemBound(id, item.getBound())); } } @@ -126,7 +91,6 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const void FetchSpatialTree::configure(const Config& config) { _justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum); _freezeFrustum = config.freezeFrustum; - _lodAngle = config.lodAngle; } void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection) { @@ -223,7 +187,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -238,7 +202,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -253,7 +217,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -268,7 +232,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -285,7 +249,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -300,7 +264,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -317,7 +281,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -334,14 +298,12 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { - if (test.solidAngleTest(itemBound.bound)) { - outItems.emplace_back(itemBound); - if (item.getKey().isMetaCullGroup()) { - item.fetchMetaSubItemBounds(outItems, (*scene)); - } + if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + if (item.getKey().isMetaCullGroup()) { + item.fetchMetaSubItemBounds(outItems, (*scene)); } } } @@ -452,3 +414,13 @@ void ApplyCullFunctorOnItemBounds::run(const RenderContextPointer& renderContext args->popViewFrustum(); } } + +void ClearContainingZones::run(const RenderContextPointer& renderContext) { + // This is a bit of a hack. We want to do zone culling as early as possible, so we do it + // during the RenderFetchCullSortTask (in CullSpatialSelection and FetchNonspatialItems), + // but the zones aren't collected until after (in SetupZones). To get around this, + // we actually use the zones from the previous frame to render, and then clear at the beginning + // of the next frame + CullTest::_prevContainingZones = CullTest::_containingZones; + CullTest::_containingZones.clear(); +} \ No newline at end of file diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index e67edd6666..9a7466223d 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -19,9 +19,6 @@ namespace render { using CullFunctor = std::function; - void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemBounds& inItems, ItemBounds& outItems); - // Culling Frustum / solidAngle test helper class struct CullTest { CullFunctor _functor; @@ -36,6 +33,10 @@ namespace render { bool frustumTest(const AABox& bound); bool antiFrustumTest(const AABox& bound); bool solidAngleTest(const AABox& bound); + bool zoneOcclusionTest(const render::Item& item); + + static std::unordered_set _containingZones; + static std::unordered_set _prevContainingZones; }; class FetchNonspatialItems { @@ -48,7 +49,6 @@ namespace render { Q_OBJECT Q_PROPERTY(int numItems READ getNumItems) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) - Q_PROPERTY(float LODAngle MEMBER lodAngle NOTIFY dirty) public: int numItems{ 0 }; @@ -56,7 +56,6 @@ namespace render { bool freezeFrustum{ false }; - float lodAngle{ 2.0 }; public slots: void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } @@ -68,7 +67,6 @@ namespace render { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; ViewFrustum _frozenFrustum; - float _lodAngle; public: using Config = FetchSpatialTreeConfig; @@ -159,6 +157,12 @@ namespace render { render::CullFunctor _cullFunctor; }; + + class ClearContainingZones { + public: + using JobModel = Job::Model; + void run(const RenderContextPointer& renderContext); + }; } #endif // hifi_render_CullTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 532964777f..f21df58368 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -154,4 +154,10 @@ namespace render { return payload->metaFetchMetaSubItems(subItems); } + template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones) { + if (!payload) { + return false; + } + return payload->passesZoneOcclusionTest(containingZones); + } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 3383101b5b..8a67108d0e 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -28,6 +28,8 @@ #include #include "ShapePipeline.h" +#include "BlendshapeConstants.h" + namespace render { typedef int32_t Index; @@ -436,6 +438,8 @@ public: virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const = 0; + virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + ~PayloadInterface() {} // Status interface is local to the base class @@ -487,6 +491,8 @@ public: uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); } uint32_t fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) const; + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } + // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -537,6 +543,10 @@ template const ShapeKey shapeGetShapeKey(const std::shared_ptr& pay // Meta items act as the grouping object for several sub items (typically shapes). template uint32_t metaFetchMetaSubItems(const std::shared_ptr& payloadData, ItemIDs& subItems) { return 0; } +// Zone Occlusion Interface +// Allows payloads to determine if they should render or not, based on the zones that contain the current camera +template bool payloadPassesZoneOcclusionTest(const std::shared_ptr& payloadData, const std::unordered_set& containingZones) { return true; } + // THe Payload class is the real Payload to be used // THis allow anything to be turned into a Payload as long as the required interface functions are available // When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" @@ -561,6 +571,8 @@ public: // Meta Type Interface virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const override { return metaFetchMetaSubItems(_data, subItems); } + virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override { return payloadPassesZoneOcclusionTest(_data, containingZones); } + protected: DataPointer _data; @@ -615,6 +627,11 @@ public: virtual Item::Bound getBound() = 0; virtual void render(RenderArgs* args) = 0; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; + virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + + // FIXME: this isn't the best place for this since it's only used for ModelEntities, but currently all Entities use PayloadProxyInterface + virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) {}; }; template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload); @@ -622,7 +639,7 @@ template <> const Item::Bound payloadGetBound(const PayloadProxyInterface::Point template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, RenderArgs* args); template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems); template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload); - +template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones); typedef Item::PayloadPointer PayloadPointer; typedef std::vector Payloads; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index ebcf5a432b..b2656a597f 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -74,6 +74,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto filteredLayeredOpaque = task.addJob("FilterLayeredOpaque", layeredOpaques, ItemKey::Layer::LAYER_1); const auto filteredLayeredTransparent = task.addJob("FilterLayeredTransparent", layeredTransparents, ItemKey::Layer::LAYER_1); + task.addJob("ClearContainingZones"); output = Output(BucketList{ opaques, transparents, lights, metas, filteredLayeredOpaque.getN(0), filteredLayeredTransparent.getN(0), diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 0fefff1c4c..9052cb89e9 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -3,18 +3,8 @@ set(TARGET_NAME script-engine) setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets) target_zlib() - if (NOT ANDROID) - - add_dependency_external_projects(quazip) - find_package(QuaZip REQUIRED) - target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) - - if (WIN32) - add_paths_to_fixup_libs(${QUAZIP_DLL_PATH}) - endif () - + target_quazip() endif () link_hifi_libraries(shared networking shaders material-networking model-networking recording avatars fbx entities controllers animation audio midi) diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 33be9de2ad..f088ad7a38 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -419,7 +419,7 @@ void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue sc * false to upload and store the data without gzip compression. Synonym: compressed. * @property {string|ArrayBuffer} data - The content to upload. * @property {string} [path] - A user-friendly path for the file in the asset server. May have a leading - * "atp:". IF not specified, no path-to-hash mapping is set. + * "atp:". If not specified, no path-to-hash mapping is set. *

    Note: The asset server destroys any unmapped SHA256-named file at server restart. Either set the mapping path * with this property or use {@link Assets.setMapping} to set a path-to-hash mapping for the uploaded file.

    */ diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.h b/libraries/script-engine/src/ConsoleScriptingInterface.h index deb0d126f9..7eec44d103 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.h +++ b/libraries/script-engine/src/ConsoleScriptingInterface.h @@ -26,25 +26,190 @@ #include #include +/**jsdoc + * The console API provides program logging facilities. + * + * @namespace console + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + */ // Scriptable interface of "console" object. Used exclusively in the JavaScript API class ConsoleScriptingInterface : public QObject, protected QScriptable { Q_OBJECT public: + + /**jsdoc + * Logs an "INFO" message to the program log and triggers {@link Script.infoMessage}. + * The message logged is "INFO -" followed by the message values separated by spaces. + * @function console.info + * @param {...*} [message] - The message values to log. + */ static QScriptValue info(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Logs a message to the program log and triggers {@link Script.printedMessage}. + * The message logged is the message values separated by spaces. + *

    If a {@link console.group} is in effect, the message is indented by an amount proportional to the group level.

    + * @function console.log + * @param {...*} [message] - The message values to log. + * @example
+ * Script.printedMessage.connect(function (message, scriptName) { + * console.info("Console.log message:", "\"" + message + "\"", "in", "[" + scriptName + "]"); + * }); + * + * console.log("string", 7, true); + * + * // string 7 true + * // INFO - Console.log message: "string 7 true" in [console.js] + */ static QScriptValue log(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Logs a message to the program log and triggers {@link Script.printedMessage}. + * The message logged is the message values separated by spaces. + * @function console.debug + * @param {...*} [message] - The message values to log. + */ static QScriptValue debug(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Logs a "WARNING" message to the program log and triggers {@link Script.warningMessage}. + * The message logged is "WARNING - " followed by the message values separated by spaces. + * @function console.warn + * @param {...*} [message] - The message values to log. + */ static QScriptValue warn(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}. + * The message logged is "ERROR - " followed by the message values separated by spaces. + * @function console.error + * @param {...*} [message] - The message values to log. + */ static QScriptValue error(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * A synonym of {@link console.error}. + * Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}. + * The message logged is "ERROR - " followed by the message values separated by spaces. + * @function console.exception + * @param {...*} [message] - The message values to log. + */ static QScriptValue exception(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}, if a test condition fails. + * The message logged is "ERROR - Assertion failed : " followed by the message values separated by spaces. + *

Note: Script execution continues whether or not the test condition fails.

+ * @function console.assert + * @param {boolean} assertion - The test condition value. + * @param {...*} [message] - The message values to log if the assertion evaluates to false. + * @example + * Script.errorMessage.connect(function (message, scriptName) { + * console.info("Error message logged:", "\"" + message + "\"", "in", "[" + scriptName + "]"); + * }); + * + * console.assert(5 === 5, "5 equals 5.", "This assertion will succeed."); // No log output. + * console.assert(5 > 6, "5 is not greater than 6.", "This assertion will fail."); // Log output is generated. + * console.info("Script continues running."); + * + * // ERROR - Assertion failed : 5 is not greater than 6. This assertion will fail. + * // INFO - Error message logged: "Assertion failed : 5 is not greater than 6. This assertion will fail." in [console.js] + * // INFO - Script continues running. + */ + // Note: Is registered in the script engine as "assert" static QScriptValue assertion(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Logs a message to the program log and triggers {@link Script.printedMessage}, then starts indenting subsequent + * {@link console.log} messages until a {@link console.groupEnd}. Groups may be nested. + * @function console.group + * @param {*} message - The message value to log. + * @example + * console.group("Group 1"); + * console.log("Sentence 1"); + * console.log("Sentence 2"); + * console.group("Group 2"); + * console.log("Sentence 3"); + * console.log("Sentence 4"); + * console.groupEnd(); + * console.log("Sentence 5"); + * console.groupEnd(); + * console.log("Sentence 6"); + * + * //Group 1 + * // Sentence 1 + * // Sentence 2 + * // Group 2 + * // Sentence 3 + * // Sentence 4 + * // Sentence 5 + * //Sentence 6 + */ static QScriptValue group(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Has the same behavior as {@link console.group}. + * Logs a message to the program log and triggers {@link Script.printedMessage}, then starts indenting subsequent + * {@link console.log} messages until a {@link console.groupEnd}. Groups may be nested. + * @function console.groupCollapsed + * @param {*} message - The message value to log. + */ static QScriptValue groupCollapsed(QScriptContext* context, QScriptEngine* engine); + + /**jsdoc + * Finishes a group of indented {@link console.log} messages. + * @function console.groupEnd + */ static QScriptValue groupEnd(QScriptContext* context, QScriptEngine* engine); public slots: + + /**jsdoc + * Starts a timer, logs a message to the program log, and triggers {@link Script.printedMessage}. The message logged has + * the timer name and "Timer started". + * @function console.time + * @param {string} name - The name of the timer. + * @example + * function sleep(milliseconds) { + * var start = new Date().getTime(); + * for (var i = 0; i < 1e7; i++) { + * if ((new Date().getTime() - start) > milliseconds) { + * break; + * } + * } + * } + * + * console.time("MyTimer"); + * sleep(1000); // Do some processing. + * console.timeEnd("MyTimer"); + * + * // MyTimer: Timer started + * // MyTimer: 1001ms */ void time(QString labelName); + + /**jsdoc + * Finishes a timer, logs a message to the program log, and triggers {@link Script.printedMessage}. The message logged has + * the timer name and the number of milliseconds since the timer was started. + * @function console.timeEnd + * @param {string} name - The name of the timer. + */ void timeEnd(QString labelName); + + /**jsdoc + * Logs the JavaScript call stack at the point of call to the program log. + * @function console.trace + */ void trace(); + + /**jsdoc + * Clears the Developer > Scripting > Script Log debug window. + * @function console.clear + */ void clear(); private: diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 3c239f006f..39170bb370 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -17,19 +17,79 @@ #include class QScriptEngine; +/**jsdoc + * The Model API provides the ability to manipulate meshes. You can get the meshes for an entity using + * {@link Entities.getMeshes}, or create a new mesh using {@link Model.newMesh}. + *

See also, the {@link Graphics} API.

+ * + * @namespace Model + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @deprecated This API is deprecated. Use the {@link Graphics} API instead. + */ class ModelScriptingInterface : public QObject { Q_OBJECT public: ModelScriptingInterface(QObject* parent); + /**jsdoc + * Exports meshes to an OBJ format model. + * @function Model.meshToOBJ + * @param {MeshProxy[]} meshes - The meshes to export. + * @returns {string} The OBJ format representation of the meshes. + */ Q_INVOKABLE QString meshToOBJ(MeshProxyList in); + + /**jsdoc + * Combines multiple meshes into one. + * @function Model.appendMeshes + * @param {MeshProxy[]} meshes - The meshes to combine. + * @returns {MeshProxy} The combined mesh. + */ Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in); + + /**jsdoc + * Transforms the vertices in a mesh. + * @function Model.transformMesh + * @param {Mat4} transform - The transform to apply. + * @param {MeshProxy} mesh - The mesh to apply the transform to. + * @returns {MeshProxy|boolean} The transformed mesh, if valid. false if an error. + */ Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy); + + /**jsdoc + * Creates a new mesh. + * @function Model.newMesh + * @param {Vec3[]} vertices - The vertices in the mesh. + * @param {Vec3[]} normals - The normals in the mesh. + * @param {MeshFace[]} faces - The faces in the mesh. + * @returns {MeshProxy} A new mesh. + */ Q_INVOKABLE QScriptValue newMesh(const QVector& vertices, const QVector& normals, const QVector& faces); + + /**jsdoc + * Gets the number of vertices in a mesh. + * @function Model.getVertexCount + * @param {MeshProxy} mesh - The mesh to count the vertices in. + * @returns {number|boolean} The number of vertices in the mesh, if valid. false if an error. + */ Q_INVOKABLE QScriptValue getVertexCount(MeshProxy* meshProxy); + + /**jsdoc + * Gets the position of a vertex in a mesh. + * @function Model.getVertex + * @param {MeshProxy} mesh - The mesh. + * @param {number} index - The index of the vertex to get. + * @returns {Vec3|boolean} The local position of the vertex relative to the mesh, if valid. false if an error. + */ Q_INVOKABLE QScriptValue getVertex(MeshProxy* meshProxy, int vertexIndex); private: diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 44abe62b24..23bfe56309 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -223,7 +223,7 @@ public slots: /**jsdoc * Gets the "front" direction that the camera would face if its orientation was set to the quaternion value. * This is a synonym for {@link Quat(0).getForward|Quat.getForward}. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getFront * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -233,7 +233,7 @@ public slots: /**jsdoc * Gets the "forward" direction that the camera would face if its orientation was set to the quaternion value. * This is a synonym for {@link Quat(0).getFront|Quat.getFront}. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getForward * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -245,7 +245,7 @@ public slots: /**jsdoc * Gets the "right" direction that the camera would have if its orientation was set to the quaternion value. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getRight * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The x-axis rotated by orientation. @@ -254,7 +254,7 @@ public slots: /**jsdoc * Gets the "up" direction that the camera would have if its orientation was set to the quaternion value. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getUp * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The y-axis rotated by orientation. diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 98f1f3082f..3b2a122e71 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -736,6 +736,13 @@ void ScriptEngine::init() { QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); globalObject().setProperty("WebSocket", webSocketConstructorValue); + /**jsdoc + * Prints a message to the program log and emits {@link Script.printedMessage}. + * The message logged is the message values separated by spaces. + *

Alternatively, you can use {@link Script.print} or one of the {@link console} API methods.

+ * @function print + * @param {...*} [message] - The message values to print. + */ globalObject().setProperty("print", newFunction(debugPrint)); QScriptValue audioEffectOptionsConstructorValue = newFunction(AudioEffectOptions::constructor); @@ -1012,6 +1019,12 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }); // Two common cases of event handler, differing only in argument signature. + + /**jsdoc + * Called when an entity event occurs on an entity as registered with {@link Script.addEventHandler}. + * @callback Script~entityEventCallback + * @param {Uuid} entityID - The ID of the entity the event has occured on. + */ using SingleEntityHandler = std::function; auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler { return [this, eventName](const EntityItemID& entityItemID) { @@ -1019,6 +1032,12 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }; }; + /**jsdoc + * Called when a pointer event occurs on an entity as registered with {@link Script.addEventHandler}. + * @callback Script~pointerEventCallback + * @param {Uuid} entityID - The ID of the entity the event has occurred on. + * @param {PointerEvent} pointerEvent - Details of the event. + */ using PointerHandler = std::function; auto makePointerHandler = [this](QString eventName) -> PointerHandler { return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) { @@ -1028,6 +1047,13 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }; }; + /**jsdoc + * Called when a collision event occurs on an entity as registered with {@link Script.addEventHandler}. + * @callback Script~collisionEventCallback + * @param {Uuid} entityA - The ID of one entity in the collision. + * @param {Uuid} entityB - The ID of the other entity in the collision. + * @param {Collision} collisionEvent - Details of the collision. + */ using CollisionHandler = std::function; auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler { return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { @@ -1037,28 +1063,39 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }; /**jsdoc - *

The name of an entity event. When the entity event occurs, any function that has been registered for that event via - * {@link Script.addEventHandler} is called with parameters per the entity event.

+ *

The name of an entity event. When the entity event occurs, any function that has been registered for that event + * via {@link Script.addEventHandler} is called with parameters per the entity event.

*
Log some values.Demonstrate assertion behavior.Nested groups.Time some processing.
* - * + * * * - * - * - * - * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * * *
Event NameEntity Event
Event NameCallback TypeEntity Event
"enterEntity"{@link Entities.enterEntity}
"leaveEntity"{@link Entities.leaveEntity}
"mousePressOnEntity"{@link Entities.mousePressOnEntity}
"mouseMoveOnEntity"{@link Entities.mouseMoveOnEntity}
"mouseReleaseOnEntity"{@link Entities.mouseReleaseOnEntity}
"clickDownOnEntity"{@link Entities.clickDownOnEntity}
"holdingClickOnEntity"{@link Entities.holdingClickOnEntity}
"clickReleaseOnEntity"{@link Entities.clickReleaseOnEntity}
"hoverEnterEntity"{@link Entities.hoverEnterEntity}
"hoverOverEntity"{@link Entities.hoverOverEntity}
"hoverLeaveEntity"{@link Entities.hoverLeaveEntity}
"collisionWithEntity"{@link Entities.collisionWithEntity}
"enterEntity"{@link Script~entityEventCallback|entityEventCallback}{@link Entities.enterEntity}
"leaveEntity"{@link Script~entityEventCallback|entityEventCallback}{@link Entities.leaveEntity}
"mousePressOnEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.mousePressOnEntity}
"mouseMoveOnEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.mouseMoveOnEntity}
"mouseReleaseOnEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.mouseReleaseOnEntity}
"clickDownOnEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.clickDownOnEntity}
"holdingClickOnEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.holdingClickOnEntity}
"clickReleaseOnEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.clickReleaseOnEntity}
"hoverEnterEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.hoverEnterEntity}
"hoverOverEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.hoverOverEntity}
"hoverLeaveEntity"{@link Script~pointerEventCallback|pointerEventCallback}{@link Entities.hoverLeaveEntity}
"collisionWithEntity"{@link Script~collisionEventCallback|collisionEventCallback}{@link Entities.collisionWithEntity}
- * * @typedef {string} Script.EntityEvent */ connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity")); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 0ae913431f..15166d572f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -320,13 +320,13 @@ public: // NOTE - these are intended to be public interfaces available to scripts /**jsdoc - * Adds a function to the list of functions called when an entity event occurs on a particular entity. + * Adds a function to the list of functions called when a particular event occurs on a particular entity. *

See also, the {@link Entities} API.

* @function Script.addEventHandler * @param {Uuid} entityID - The ID of the entity. - * @param {Script.EntityEvent} eventName - The name of the entity event. - * @param {function} handler - The function to call when the entity event occurs on the entity. It can be either the name - * of a function or an in-line definition. + * @param {Script.EntityEvent} eventName - The name of the event. + * @param {Script~entityEventCallback|Script~pointerEventCallback|Script~collisionEventCallback} handler - The function to + * call when the event occurs on the entity. It can be either the name of a function or an in-line definition. * @example Report when a mouse press occurs on a particular entity. * var entityID = Entities.addEntity({ * type: "Box", @@ -417,7 +417,7 @@ public: /**jsdoc * Provides access to methods or objects provided in an external JavaScript or JSON file. - * See {@link https://docs.projectathena.dev/script/js-tips.html} for further details. + * See {@link https://docs.vircadia.dev/script/js-tips.html} for further details. * @function Script.require * @param {string} module - The module to use. May be a JavaScript file, a JSON file, or the name of a system module such * as "appUi" (i.e., the "appUi.js" system module JavaScript file). @@ -499,17 +499,11 @@ public: Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } /**jsdoc - * Prints a message to the program log. - *

Alternatively, you can use {@link print}, {@link console.log}, or one of the other {@link console} methods.

+ * Prints a message to the program log and emits {@link Script.printedMessage}. + *

Alternatively, you can use {@link print} or one of the {@link console} API methods.

* @function Script.print * @param {string} message - The message to print. */ - /**jsdoc - * Prints a message to the program log. - *

This is an alias of {@link Script.print}.

- * @function print - * @param {string} message - The message to print. - */ Q_INVOKABLE void print(const QString& message); /**jsdoc @@ -534,7 +528,7 @@ public: Q_INVOKABLE QUrl resourcesPath() const; /**jsdoc - * Starts timing a section of code in order to send usage data about it to High Fidelity. Shouldn't be used outside of the + * Starts timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of the * standard scripts. * @function Script.beginProfileRange * @param {string} label - A name that identifies the section of code. @@ -542,7 +536,7 @@ public: Q_INVOKABLE void beginProfileRange(const QString& label) const; /**jsdoc - * Finishes timing a section of code in order to send usage data about it to High Fidelity. Shouldn't be used outside of + * Finishes timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of * the standard scripts. * @function Script.endProfileRange * @param {string} label - A name that identifies the section of code. @@ -763,7 +757,8 @@ signals: /**jsdoc * Triggered when the script prints a message to the program log via {@link print}, {@link Script.print}, - * {@link console.log}, or {@link console.debug}. + * {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or + * {@link console.timeEnd}. * @function Script.printedMessage * @param {string} message - The message. * @param {string} scriptName - The name of the script that generated the message. @@ -772,7 +767,8 @@ signals: void printedMessage(const QString& message, const QString& scriptName); /**jsdoc - * Triggered when the script generates an error or {@link console.error} is called. + * Triggered when the script generates an error, {@link console.error} or {@link console.exception} is called, or + * {@link console.assert} is called and fails. * @function Script.errorMessage * @param {string} message - The error message. * @param {string} scriptName - The name of the script that generated the error message. diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp index 429c7f5518..40482c12ee 100644 --- a/libraries/script-engine/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include "ScriptEngine.h" @@ -191,7 +192,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(request); connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); } diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h index bd6c9687c1..0412bbf0fe 100644 --- a/libraries/script-engine/src/ScriptsModel.h +++ b/libraries/script-engine/src/ScriptsModel.h @@ -87,7 +87,6 @@ public: * * * @class ScriptsModel - * @hideconstructor * * @hifi-interface diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index a8fe51efe8..a8a8adece3 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -22,9 +22,9 @@ #include "GLMHelpers.h" /**jsdoc - * The Vec3 API provides facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a + * The Vec3 API provides facilities for generating and manipulating 3-dimensional vectors. Vircadia uses a * right-handed Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. - * High Fidelity coordinate system + * Vircadia coordinate system * * @namespace Vec3 * @variation 0 diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 9a2a51e0b7..00d379aff4 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -76,6 +76,18 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas _webSocket->close(closeCode, reason); } +/**jsdoc + * Called when the connection closes. + * @callback WebSocket~onCloseCallback + * @param {WebSocket.CloseData} data - Information on the connection closure. + */ +/**jsdoc + * Information on a connection being closed. + * @typedef {object} WebSocket.CloseData + * @property {WebSocket.CloseCode} code - The reason why the connection was closed. + * @property {string} reason - Description of the reason why the connection was closed. + * @property {boolean} wasClean - true if the connection closed cleanly, false if it didn't. + */ void WebSocketClass::handleOnClose() { bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError); if (_onCloseEvent.isFunction()) { @@ -89,12 +101,73 @@ void WebSocketClass::handleOnClose() { } } +/**jsdoc + * Called when an error occurs. + * @callback WebSocket~onErrorCallback + * @param {WebSocket.SocketError} error - The error. + */ +/**jsdoc + *

The type of socket error.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0ConnectionRefusedErrorThe connection was refused or timed out.
1RemoteHostClosedErrorThe remote host closed the connection.
2HostNotFoundErrorThe host address was not found.
3SocketAccessErrorThe socket operation failed because the application doesn't have + * the necessary privileges.
4SocketResourceErrorThe local system ran out of resources (e.g., too many + * sockets).
5SocketTimeoutErrorThe socket operation timed out.
6DatagramTooLargeErrorThe datagram was larger than the OS's limit.
7NetworkErrorAn error occurred with the network.
8AddressInUseErrorThe is already in use and cannot be reused.
9SocketAddressNotAvailableErrorThe address specified does not belong to the + * host.
10UnsupportedSocketOperationErrorThe requested socket operation is not supported + * by the local OS.
11ProxyAuthenticationRequiredErrorThe socket is using a proxy and requires + * authentication.
12SslHandshakeFailedErrorThe SSL/TLS handshake failed.
13UnfinishedSocketOperationErrorThe last operation has not finished yet.
14ProxyConnectionRefusedErrorCould not contact the proxy server because connection + * was denied.
15ProxyConnectionClosedErrorThe connection to the proxy server was unexpectedly + * closed.
16ProxyConnectionTimeoutErrorThe connection to the proxy server timed + * out.
17ProxyNotFoundErrorThe proxy address was not found.
18ProxyProtocolErrorConnection to the proxy server failed because the server + * response could not be understood.
19OperationErrorAn operation failed because the socket state did not permit + * it.
20SslInternalErrorInternal error in the SSL library being used.
21SslInvalidUserDataErrorError in the SSL library because of invalid + * data.
22TemporaryErrorA temporary error occurred.
-1UnknownSocketErrorAn unknown error occurred.
+ * @typedef {number} WebSocket.SocketError + */ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { if (_onErrorEvent.isFunction()) { _onErrorEvent.call(); } } +/**jsdoc + * Triggered when a message is received. + * @callback WebSocket~onMessageCallback + * @param {WebSocket.MessageData} message - The message received. + */ +/**jsdoc + * A message received on a WebSocket connection. + * @typedef {object} WebSocket.MessageData + * @property {string} data - The message content. + */ void WebSocketClass::handleOnMessage(const QString& message) { if (_onMessageEvent.isFunction()) { QScriptValueList args; @@ -124,6 +197,10 @@ void WebSocketClass::handleOnBinaryMessage(const QByteArray& message) { } } +/**jsdoc + * Called when the connection opens. + * @callback WebSocket~onOpenCallback + */ void WebSocketClass::handleOnOpen() { if (_onOpenEvent.isFunction()) { _onOpenEvent.call(); diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 048ba4bc10..3430068eee 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -16,6 +16,68 @@ #include #include +/**jsdoc + * Provides a bi-directional, event-driven communication session between the script and another WebSocket connection. It is a + * near-complete implementation of the WebSocket API described in the Mozilla docs: + * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket. + * + *

Create using new WebSocket(...) in Interface, client entity, avatar, and server entity scripts, or the + * {@link WebSocketServer} class in server entity and assignment client scripts. + * + *

Note: Does not support secure, wss: protocol.

+ * + * @class WebSocket + * @param {string|WebSocket} urlOrWebSocket - The URL to connect to or an existing {@link WebSocket} to reuse the connection of. + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string} binaryType="blob" - Not used. + * @property {number} bufferedAmount=0 - Not implemented. Read-only. + * @property {string} extensions="" - Not implemented. Read-only. + * + * @property {WebSocket~onOpenCallback} onopen - Function called when the connection opens. + * @property {WebSocket~onMessageCallback} onmessage - Function called when a message is received. + * @property {WebSocket~onErrorCallback} onerror - Function called when an error occurs. + * @property {WebSocket~onCloseCallback} onclose - Function called when the connection closes. + * + * @property {string} protocol="" - Not implemented. Read-only. + * @property {WebSocket.ReadyState} readyState - The state of the connection. Read-only. + * @property {string} url - The URL to connect to. Read-only. + * + * @property {WebSocket.ReadyState} CONNECTING - The connection is opening. Read-only. + * @property {WebSocket.ReadyState} OPEN - The connection is open. Read-only. + * @property {WebSocket.ReadyState} CLOSING - The connection is closing. Read-only. + * @property {WebSocket.ReadyState} CLOSED - The connection is closed. Read-only. + * + * @example Echo a message off websocket.org. + * print("Create WebSocket"); + * var WEBSOCKET_PING_URL = "ws://echo.websocket.org"; + * var webSocket = new WebSocket(WEBSOCKET_PING_URL); + * + * webSocket.onclose = function (data) { + * print("WebSocket closed"); + * print("Ready state =", webSocket.readyState); // 3 + * }; + * + * webSocket.onmessage = function (data) { + * print("Message received:", data.data); + * + * print("Close WebSocket"); + * webSocket.close(); + * }; + * + * webSocket.onopen = function () { + * print("WebSocket opened"); + * print("Ready state =", webSocket.readyState); // 1 + * + * print("Send a test message"); + * webSocket.send("Test message"); + * }; + */ class WebSocketClass : public QObject { Q_OBJECT Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType) @@ -43,6 +105,21 @@ public: static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + /**jsdoc + * The state of a WebSocket connection. + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0CONNECTINGThe connection is opening.
1OPENThe connection is open.
2CLOSINGThe connection is closing.
3CLOSEDThe connection is closed.
+ * @typedef {number} WebSocket.ReadyState + */ enum ReadyState { CONNECTING = 0, OPEN, @@ -100,8 +177,44 @@ public: QScriptValue getOnOpen() { return _onOpenEvent; } public slots: + + /**jsdoc + * Sends a message on the connection. + * @function WebSocket.send + * @param {string|object} message - The message to send. If an object, it is converted to a string. + */ void send(QScriptValue message); + /**jsdoc + * Closes the connection. + * @function WebSocket.close + * @param {WebSocket.CloseCode} [closeCode=1000] - The reason for closing. + * @param {string} [reason=""] - A description of the reason for closing. + */ + /**jsdoc + * The reason why the connection was closed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
1000NormalNormal closure.
1001GoingAwayGoing away.
1002ProtocolErrorProtocol error.
1003DatatypeNotSupportedUnsupported data.
1004Reserved1004Reserved.
1005MissingStatusCodeNo status received.
1006AbnormalDisconnectionabnormal closure.
1007WrongDatatypeInvalid frame payload data.
1008PolicyViolatedPolicy violation.
1009TooMuchDataMessage too big.
1010MissingExtensionMandatory extension missing.
1011BadOperationInternal server error.
1015TlsHandshakeFailedTLS handshake failed.
+ * @typedef {number} WebSocket.CloseCode + */ void close(); void close(QWebSocketProtocol::CloseCode closeCode); void close(QWebSocketProtocol::CloseCode closeCode, QString reason); diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index 972bf9c032..fff33d5bfb 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -17,6 +17,62 @@ #include #include "WebSocketClass.h" +/**jsdoc + * Manages {@link WebSocket}s in server entity and assignment client scripts. + * + *

Create using new WebSocketServer(...).

+ * + * @class WebSocketServer + * + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string} url - The URL that the server is listening on. Read-only. + * @property {number} port - The port that the server is listening on. Read-only. + * @property {boolean} listening - true if the server is listening for incoming connections, false if + * it isn't. Read-only. + * + * @example
+ * // Server entity script. Echoes received message back to sender. + * (function () { + * print("Create WebSocketServer"); + * var webSocketServer = new WebSocketServer(); + * print("Server url:", webSocketServer.url); + * + * function onNewConnection(webSocket) { + * print("New connection"); + * + * webSocket.onmessage = function (message) { + * print("Message received:", message.data); + * + * var returnMessage = message.data + " back!"; + * print("Echo a message back:", returnMessage); + * webSocket.send(message.data + " back!"); + * }; + * } + * + * webSocketServer.newConnection.connect(onNewConnection); + * }) + * + * @example + * // Interface script. Bounces message off server entity script. + * // Use the server URL reported by the server entity script. + * var WEBSOCKET_PING_URL = "ws://127.0.0.1:nnnnn"; + * var TEST_MESSAGE = "Hello"; + * + * print("Create WebSocket"); + * var webSocket = new WebSocket(WEBSOCKET_PING_URL); + * + * webSocket.onmessage = function(data) { + * print("Message received:", data.data); + * }; + * + * webSocket.onopen = function() { + * print("WebSocket opened"); + * print("Send test message:", TEST_MESSAGE); + * webSocket.send(TEST_MESSAGE); + * }; + */ class WebSocketServerClass : public QObject { Q_OBJECT Q_PROPERTY(QString url READ getURL) @@ -34,6 +90,11 @@ public: static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); public slots: + + /**jsdoc + * Closes all connections and closes the WebSocketServer. + * @function WebSocketServer.close + */ void close(); private: @@ -45,6 +106,13 @@ private slots: void onNewConnection(); signals: + + /**jsdoc + * Triggered when there is a new connection. + * @function WebSocketServer.newConnection + * @param {WebSocket} webSocket - The {@link WebSocket} for the new connection. + * @returns {Signal} + */ void newConnection(WebSocketClass* client); }; diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index e62726f5d0..1a4ec52f73 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -62,7 +62,7 @@ void XMLHttpRequestClass::abort() { } void XMLHttpRequestClass::setRequestHeader(const QString& name, const QString& value) { - _request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + _request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); _request.setRawHeader(QByteArray(name.toLatin1()), QByteArray(value.toLatin1())); } @@ -109,6 +109,10 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { return QScriptValue::NullValue; } +/**jsdoc + * Called when the request's ready state changes. + * @callback XMLHttpRequest~onReadyStateChangeCallback + */ void XMLHttpRequestClass::setReadyState(ReadyState readyState) { if (readyState != _readyState) { _readyState = readyState; @@ -184,6 +188,10 @@ void XMLHttpRequestClass::doSend() { } } +/**jsdoc + * Called when the request times out. + * @callback XMLHttpRequest~onTimeoutCallback + */ void XMLHttpRequestClass::requestTimeout() { if (_onTimeout.isFunction()) { _onTimeout.call(QScriptValue::NullValue); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index d7f3c2e059..3ab7d38dda 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -21,6 +21,134 @@ #include #include +/* +XMlHttpRequest object +XMlHttpRequest.objectName string +XMlHttpRequest.response undefined +XMlHttpRequest.responseText string +XMlHttpRequest.responseType string +XMlHttpRequest.status number +XMlHttpRequest.statusText string +XMlHttpRequest.readyState number +XMlHttpRequest.errorCode number +XMlHttpRequest.timeout number +XMlHttpRequest.UNSENT number +XMlHttpRequest.OPENED number +XMlHttpRequest.HEADERS_RECEIVED number +XMlHttpRequest.LOADING number +XMlHttpRequest.DONE number +XMlHttpRequest.ontimeout object +XMlHttpRequest.onreadystatechange object +XMlHttpRequest.destroyed(QObject*) function +XMlHttpRequest.destroyed() function +XMlHttpRequest.objectNameChanged(QString) function +XMlHttpRequest.deleteLater() function +XMlHttpRequest.requestComplete() function +XMlHttpRequest.abort() function +XMlHttpRequest.setRequestHeader(QString,QString) function +XMlHttpRequest.open(QString,QString,bool,QString,QString) function +XMlHttpRequest.open(QString,QString,bool,QString) function +XMlHttpRequest.open(QString,QString,bool) function +XMlHttpRequest.open(QString,QString) function +XMlHttpRequest.send() function +XMlHttpRequest.send(QScriptValue) function +XMlHttpRequest.getAllResponseHeaders() function +XMlHttpRequest.getResponseHeader(QString) function +*/ + +/**jsdoc + * Provides a means to interact with web servers. It is a near-complete implementation of the XMLHttpRequest API described in + * the Mozilla docs: + * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest. + * + *

Create using new XMLHttpRequest(...).

+ * + * @class XMLHttpRequest + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {*} response - The response data. + * Read-only. + * @property {string} responseText - The response data as text. + * Read-only. + * @property {string} responseType - The response type required or received (e.g., "text", "json", + * "arraybuffer", ...). + * @property {number} status - The HTTP response status + * code (100599). + * Read-only. + * @property {string} statusText - The HTTP response status text. + * Read-only. + * @property {XMLHttpRequest.ReadyState} readyState - The status of the request. + * Read-only. + * @property {XMLHttpRequest.NetworkError} errorCode - The network result of the request: including, 0 (NoError) + * if there was no error, 4 (TimeoutError) if the request timed out. + * Read-only. + * @property {number} timeout - The time a request can take before timing out, in ms. + * + * @property {XMLHttpRequest.ReadyState} UNSENT - Request has been created; {@link XMLHttpRequest.open} not called yet. + * Read-only. + * @property {XMLHttpRequest.ReadyState} OPENED - {@link XMLHttpRequest.open} has been called. + * Read-only. + * @property {XMLHttpRequest.ReadyState} HEADERS_RECEIVED - {@link XMLHttpRequest.send} has been called; headers and status + * are available. + * Read-only. + * @property {XMLHttpRequest.ReadyState} LOADING - Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has partial + * data. + * Read-only. + * @property {XMLHttpRequest.ReadyState} DONE - Operation complete. + * Read-only. + * + * @property {XMLHttpRequest~onTimeoutCallback} ontimeout - Function called when the request times out. + *

Note: This is called in addition to any function set for onreadystatechange.

+ * @property {XMLHttpRequest~onReadyStateChangeCallback} onreadystatechange - Function called when the request's ready state + * changes. + * + * @example
+ * var URL = "https://www.highfidelity.com/"; + * + * var req = new XMLHttpRequest(); + * req.onreadystatechange = function () { + * if (req.readyState === req.DONE) { + * if (req.status === 200) { + * print("Success"); + * print("Content type:", req.getResponseHeader("content-type")); + * print("Content:", req.responseText.slice(0, 100), "..."); + * + * } else { + * print("Error", req.status, req.statusText); + * } + * + * req = null; + * } + * }; + * + * req.open("GET", URL); + * req.send(); + * + * @example + * var URL = "https://www.highfidelity.com/"; + * + * var req = new XMLHttpRequest(); + * req.requestComplete.connect(function () { + * if (req.status === 200) { + * print("Success"); + * print("Content type:", req.getResponseHeader("content-type")); + * print("Content:", req.responseText.slice(0, 100), "..."); + * + * } else { + * print("Error", req.status, req.statusText); + * } + * + * req = null; + * }); + * + * req.open("GET", URL); + * req.send(); + */ class XMLHttpRequestClass : public QObject { Q_OBJECT Q_PROPERTY(QScriptValue response READ getResponse) @@ -46,6 +174,26 @@ public: ~XMLHttpRequestClass(); static const int MAXIMUM_REDIRECTS = 5; + + /**jsdoc + *

The state of an XMLHttpRequest.

+ *
Echo a message back to sender.Get a web page's HTML.Get a web page's HTML — alternative method.
+ * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0UNSENTRequest has been created; {@link XMLHttpRequest.open} not called + * yet.
1OPENED{@link XMLHttpRequest.open} has been called.
2HEADERS_RECEIVED{@link XMLHttpRequest.send} has been called; headers and + * status are available.
3LOADINGDownloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has + * partial data.
4DONEOperation complete.
+ * @typedef {number} XMLHttpRequest.ReadyState + */ enum ReadyState { UNSENT = 0, OPENED, @@ -79,16 +227,66 @@ public: void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } public slots: + + /**jsdoc + * Aborts the request. + * @function XMLHttpRequest.abort + */ void abort(); + + /**jsdoc + * Sets the value of an HTTP request header. Must be called after {@link XMLHttpRequest.open} but before + * {@link XMLHttpRequest.send}; + * @function XMLHttpRequest.setRequestHeader + * @param {string} name - The name of the header to set. + * @param {string} value - The value of the header. + */ void setRequestHeader(const QString& name, const QString& value); + + /**jsdoc + * Initializes a request. + * @function XMLHttpRequest.open + * @param {string} method - The HTTP request method + * to use, e.g., "GET", "POST", "PUT", or "DELETE". + * @param {string} url - The URL to send the request to. + * @param {boolean} [async=true] - true if the method returns without waiting for the response, + * false if the method returns only once the request is complete. + * @param {string} [username=""] - User name for authentication. + * @param {string} [password=""] - Password for authentication. + */ void open(const QString& method, const QString& url, bool async = true, const QString& username = "", const QString& password = ""); + + /**jsdoc + * Sends the request to the server. + * @function XMLHttpRequest.send + * @param {*} [data] - The data to send. + */ void send(); void send(const QScriptValue& data); + + /**jsdoc + * Gets the response headers. + * @function XMLHttpRequest.getAllResponseHeaders + * @returns {string} The response headers, separated by "\n" characters. + */ QScriptValue getAllResponseHeaders() const; + + /**jsdoc + * Gets a response header. + * @function XMLHttpRequest.getResponseHeader + * @param {string} name - + * @returns {string} The response header. + */ QScriptValue getResponseHeader(const QString& name) const; signals: + + /**jsdoc + * Triggered when the request is complete — with or without error (incl. timeout). + * @function XMLHttpRequest.requestComplete + * @returns {Signal} + */ void requestComplete(); private: @@ -111,7 +309,58 @@ private: QScriptValue _onTimeout { QScriptValue::NullValue }; QScriptValue _onReadyStateChange { QScriptValue::NullValue }; ReadyState _readyState { XMLHttpRequestClass::UNSENT }; + + /**jsdoc + *

The type of network error.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + *
ValueNameDescription
0NoErrorNo error.
1ConnectionRefusedErrorThe server refused the connection.
2RemoteHostClosedErrorThe server closed the connection.
3HostNotFoundErrorHost name not found.
4TimeoutErrorConnection timed out
5OperationCanceledErrorOperation canceled by + * {@link XMLHttpRequest.abort}.
6SslHandshakeFailedErrorSSL/TLS handshake failed.
7TemporaryNetworkFailureErrorTemporarily disconnected from the + * network.
8NetworkSessionFailedErrorDisconnection from the network.
9BackgroundRequestNotAllowedErrorBackground request not allowed.
10TooManyRedirectsErrorToo many redirects.
11InsecureRedirectErrorRedirect from secure to insecure protocol.
101ProxyConnectionRefusedErrorConnection to proxy server refused.
102ProxyConnectionClosedErrorProxy server closed the connection.
103ProxyNotFoundErrorProxy host name not found.
104ProxyTimeoutErrorProxy connection timed out.
105ProxyAuthenticationRequiredErrorProxy requires authentication.
201ContentAccessDeniedAccess denied.
202ContentOperationNotPermittedErrorOperation not permitted.
203ContentNotFoundErrorContent not found.
204AuthenticationRequiredErrorAuthentication required.
205ContentReSendErrorResend failed.
206ContentConflictErrorResource state conflict.
207ContentGoneErrorResource no longer available.
401InternalServerErrorInternal server error.
402OperationNotImplementedErrorOperation not supported.
403ServiceUnavailableErrorRequest not able to be handled at this + * time.
301ProtocolUnknownErrorProtocol unknown.
302ProtocolInvalidOperationErrorOperation invalid fro protocol.
99UnknownNetworkErrorUnknown network-related error.
199UnknownProxyErrorUnknown proxy-related error.
299UnknownContentErrorUnknown content-related error.
399ProtocolFailureProtocol error.
499UnknownServerErrorUnknown server response error.
+ * @typedef {number} XMLHttpRequest.NetworkError + */ QNetworkReply::NetworkError _errorCode { QNetworkReply::NoError }; + int _timeout { 0 }; QTimer _timer; int _numRedirects { 0 }; diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index eb3e286843..57904be586 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -13,6 +13,13 @@ if (ANDROID) target_link_libraries(${TARGET_NAME} android) endif() +if (APPLE) + find_library(FRAMEWORK_IOKIT IOKit) + find_library(CORE_FOUNDATION CoreFoundation) + find_library(OpenGL OpenGL) + target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION} ${OpenGL}) +endif() + target_zlib() target_nsight() target_json() diff --git a/libraries/shared/src/BlendshapeConstants.cpp b/libraries/shared/src/BlendshapeConstants.cpp index 172df461fd..83c8271340 100644 --- a/libraries/shared/src/BlendshapeConstants.cpp +++ b/libraries/shared/src/BlendshapeConstants.cpp @@ -76,3 +76,11 @@ const char* FACESHIFT_BLENDSHAPES[] = { "UserBlendshape9", "" }; + +const QMap BLENDSHAPE_LOOKUP_MAP = [] { + QMap toReturn; + for (int i = 0; i < (int)Blendshapes::BlendshapeCount; i++) { + toReturn[FACESHIFT_BLENDSHAPES[i]] = i; + } + return toReturn; +}(); diff --git a/libraries/shared/src/BlendshapeConstants.h b/libraries/shared/src/BlendshapeConstants.h index e74146eb56..33d02de57d 100644 --- a/libraries/shared/src/BlendshapeConstants.h +++ b/libraries/shared/src/BlendshapeConstants.h @@ -12,8 +12,14 @@ #ifndef hifi_BlendshapeConstants_h #define hifi_BlendshapeConstants_h +#include +#include + +#include + /// The names of the blendshapes expected by Faceshift, terminated with an empty string. extern const char* FACESHIFT_BLENDSHAPES[]; +extern const QMap BLENDSHAPE_LOOKUP_MAP; enum class Blendshapes : int { EyeBlink_L = 0, @@ -115,5 +121,16 @@ enum class LegacyBlendshpaes : int { // * LipsUpperOpen (not in ARKit) // * LipsLowerOpen (not in ARKit) +struct BlendshapeOffsetPacked { + glm::uvec4 packedPosNorTan; +}; + +struct BlendshapeOffsetUnpacked { + glm::vec3 positionOffset; + glm::vec3 normalOffset; + glm::vec3 tangentOffset; +}; + +using BlendshapeOffset = BlendshapeOffsetPacked; #endif // hifi_BlendshapeConstants_h diff --git a/libraries/shared/src/JointData.h b/libraries/shared/src/JointData.h index 7a2420262a..329c2dd151 100644 --- a/libraries/shared/src/JointData.h +++ b/libraries/shared/src/JointData.h @@ -16,6 +16,7 @@ public: // Used by the avatar mixer to describe a single joint // Translations relative to their parent joint // Rotations are absolute (i.e. not relative to parent) and are in rig space. +// No JSDoc because it's not provided as a type to the script engine. class JointData { public: glm::quat rotation; diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index 8377c48960..b7fecfa1e4 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -28,8 +28,8 @@ const float ARCSECONDS_PER_ARCMINUTE = 60.0f; const float ARCSECONDS_PER_DEGREE = ARCMINUTES_PER_DEGREE * ARCSECONDS_PER_ARCMINUTE; const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations -const float SQUARE_ROOT_OF_2 = (float)sqrt(2.0f); -const float SQUARE_ROOT_OF_3 = (float)sqrt(3.0f); +const float SQUARE_ROOT_OF_2 = 1.414214f; +const float SQUARE_ROOT_OF_3 = 1.732051f; const float METERS_PER_DECIMETER = 0.1f; const float METERS_PER_CENTIMETER = 0.01f; const float METERS_PER_MILLIMETER = 0.001f; diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 4ab6a79e22..142cd7bfbc 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -58,7 +58,7 @@ public: static QString generateTemporaryDir(); static bool deleteMyTemporaryDir(QString dirName); - static int removeTemporaryApplicationDirs(QString appName = QString::null); + static int removeTemporaryApplicationDirs(QString appName = QString()); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); diff --git a/libraries/shared/src/PhysicsHelpers.cpp b/libraries/shared/src/PhysicsHelpers.cpp index 092b9d078a..b7f5242429 100644 --- a/libraries/shared/src/PhysicsHelpers.cpp +++ b/libraries/shared/src/PhysicsHelpers.cpp @@ -10,10 +10,12 @@ // #include "PhysicsHelpers.h" -#include "NumericalConstants.h" + #include +#include "NumericalConstants.h" #include "PhysicsCollisionGroups.h" +#include "SharedUtil.h" // This chunk of code was copied from Bullet-2.82, so we include the Bullet license here: /* @@ -91,7 +93,7 @@ int32_t Physics::getDefaultCollisionMask(int32_t group) { QUuid _sessionID; void Physics::setSessionUUID(const QUuid& sessionID) { - _sessionID = sessionID; + _sessionID = sessionID.isNull() ? AVATAR_SELF_ID : sessionID; } const QUuid& Physics::getSessionUUID() { diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h index 5e5f5db3bc..d3d6673f50 100644 --- a/libraries/shared/src/PickFilter.h +++ b/libraries/shared/src/PickFilter.h @@ -10,6 +10,7 @@ #define hifi_PickFilter_h #include +#include // adebug class PickFilter { public: @@ -60,6 +61,8 @@ public: // NOT YET IMPLEMENTED PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections + PICK_BYPASS_IGNORE, // for debug purposes + NUM_FLAGS, // Not a valid flag }; typedef std::bitset Flags; @@ -93,6 +96,8 @@ public: bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; } + bool bypassIgnore() const { return _flags[PICK_BYPASS_IGNORE]; } + // Helpers for RayPickManager Flags getEntityFlags() const { unsigned int toReturn = 0; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 87cd269c97..d47cc0769a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -36,6 +36,7 @@ int qVectorVec3MetaTypeId = qRegisterMetaType>(); int qVectorQuatMetaTypeId = qRegisterMetaType>(); int qVectorBoolMetaTypeId = qRegisterMetaType>(); int qVectorGLMUint32MetaTypeId = qRegisterMetaType>("QVector"); +int qVectorQUuidMetaTypeId = qRegisterMetaType>(); int quatMetaTypeId = qRegisterMetaType(); int pickRayMetaTypeId = qRegisterMetaType(); int collisionMetaTypeId = qRegisterMetaType(); @@ -58,6 +59,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue); qScriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue); qScriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue); + qScriptRegisterMetaType(engine, qVectorQUuidToScriptValue, qVectorQUuidFromScriptValue); qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); @@ -874,6 +876,22 @@ QVector qVectorFloatFromScriptValue(const QScriptValue& array) { return newVector; } +QScriptValue qVectorQUuidToScriptValue(QScriptEngine* engine, const QVector& vector) { + QScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, quuidToScriptValue(engine, vector.at(i))); + } + return array; +} + +void qVectorQUuidFromScriptValue(const QScriptValue& array, QVector& vector) { + int length = array.property("length").toInteger(); + + for (int i = 0; i < length; i++) { + vector << array.property(i).toVariant().toUuid(); + } +} + QVector qVectorQUuidFromScriptValue(const QScriptValue& array) { if (!array.isArray()) { return QVector(); @@ -1306,6 +1324,11 @@ void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) { } +/**jsdoc + * A triangle in a mesh. + * @typedef {object} MeshFace + * @property {number[]} vertices - The indexes of the three vertices that make up the face. + */ QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace) { QScriptValue obj = engine->newObject(); obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices)); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 3b47bb70c6..86e06d7f93 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -247,6 +247,8 @@ QVector qVectorFloatFromScriptValue(const QScriptValue& array); QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vector); +QScriptValue qVectorQUuidToScriptValue(QScriptEngine* engine, const QVector& vector); +void qVectorQUuidFromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorQUuidFromScriptValue(const QScriptValue& array); QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube); @@ -687,8 +689,10 @@ namespace graphics { using MeshPointer = std::shared_ptr; /**jsdoc - * A handle for a mesh in an entity, such as returned by {@link Entities.getMeshes}. + * A mesh, such as returned by {@link Entities.getMeshes} or {@link Model} API functions. + * * @class MeshProxy + * @hideconstructor * * @hifi-interface * @hifi-client-entity @@ -705,16 +709,16 @@ public: virtual MeshPointer getMeshPointer() const = 0; /**jsdoc - * Get the number of vertices in the mesh. + * Gets the number of vertices in the mesh. * @function MeshProxy#getNumVertices * @returns {number} Integer number of vertices in the mesh. */ Q_INVOKABLE virtual int getNumVertices() const = 0; /**jsdoc - * Get the position of a vertex in the mesh. + * Gets the position of a vertex in the mesh. * @function MeshProxy#getPos - * @param {number} index - Integer index of the mesh vertex. + * @param {number} index - Integer index of the vertex. * @returns {Vec3} Local position of the vertex relative to the mesh. */ Q_INVOKABLE virtual glm::vec3 getPos(int index) const = 0; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 709eeca9b2..c40cae5f76 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -81,9 +81,6 @@ const int BYTES_PER_FLAGS = 1; typedef unsigned char colorPart; typedef unsigned char nodeColor[BYTES_PER_COLOR + BYTES_PER_FLAGS]; -// Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. -const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)"; - // Equivalent to time_t but in usecs instead of secs quint64 usecTimestampNow(bool wantDebug = false); void usecTimestampNowForceClockSkew(qint64 clockSkew); diff --git a/libraries/shared/src/SpatialParentFinder.h b/libraries/shared/src/SpatialParentFinder.h index c19babbc7f..8300359b65 100644 --- a/libraries/shared/src/SpatialParentFinder.h +++ b/libraries/shared/src/SpatialParentFinder.h @@ -21,7 +21,7 @@ using SpatiallyNestableWeakPointer = std::weak_ptr; using SpatiallyNestablePointer = std::shared_ptr; class SpatialParentTree { public: - virtual SpatiallyNestablePointer findByID(const QUuid& id) const { return nullptr; } + virtual SpatiallyNestablePointer findByID(const QUuid& id) const = 0; }; class SpatialParentFinder : public Dependency { diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 8387270905..c1c1fd38d9 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1144,7 +1144,7 @@ AACube SpatiallyNestable::calculateInitialQueryAACube(bool& success) { } } -bool SpatiallyNestable::updateQueryAACube() { +bool SpatiallyNestable::updateQueryAACube(bool updateParent) { if (!queryAACubeNeedsUpdate()) { return false; } @@ -1171,9 +1171,39 @@ bool SpatiallyNestable::updateQueryAACube() { _queryAACubeSet = true; - auto parent = getParentPointer(success); - if (success && parent) { - parent->updateQueryAACube(); + if (updateParent) { + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + } + + return true; +} + +bool SpatiallyNestable::updateQueryAACubeWithDescendantAACube(const AACube& descendantAACube, bool updateParent) { + if (!queryAACubeNeedsUpdateWithDescendantAACube(descendantAACube)) { + return false; + } + + bool success; + AACube initialQueryAACube = calculateInitialQueryAACube(success); + if (!success) { + return false; + } + _queryAACube = initialQueryAACube; + _queryAACubeIsPuffed = shouldPuffQueryAACube(); + + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + + _queryAACubeSet = true; + + if (updateParent) { + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } } return true; @@ -1216,6 +1246,24 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { return childNeedsUpdate; } +bool SpatiallyNestable::queryAACubeNeedsUpdateWithDescendantAACube(const AACube& descendantAACube) const { + if (!_queryAACubeSet) { + return true; + } + + bool success; + AACube maxAACube = getMaximumAACube(success); + if (success && !_queryAACube.contains(maxAACube)) { + return true; + } + + if (shouldPuffQueryAACube() != _queryAACubeIsPuffed) { + return true; + } + + return !_queryAACube.contains(descendantAACube); +} + AACube SpatiallyNestable::getQueryAACube(bool& success) const { if (_queryAACubeSet) { success = true; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index f52dc4bf8b..01e3b045ad 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -117,8 +117,10 @@ public: virtual void setQueryAACube(const AACube& queryAACube); virtual bool queryAACubeNeedsUpdate() const; + virtual bool queryAACubeNeedsUpdateWithDescendantAACube(const AACube& descendantAACube) const; virtual bool shouldPuffQueryAACube() const { return false; } - bool updateQueryAACube(); + bool updateQueryAACube(bool updateParent = true); + bool updateQueryAACubeWithDescendantAACube(const AACube& descendentAACube, bool updateParent = true); void forceQueryAACubeUpdate() { _queryAACubeSet = false; } virtual AACube getQueryAACube(bool& success) const; virtual AACube getQueryAACube() const; diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp index b3c02d99f4..156a438bd7 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -28,7 +28,7 @@ QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) break; case QVariant::String: case QVariant::Url: - return scriptEngine.newVariant(qValue); + return qValue.toString(); break; case QVariant::Map: { QVariantMap childMap = qValue.toMap(); diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp index 2ccc247af3..9fc72bd000 100644 --- a/libraries/shared/src/shared/FileLogger.cpp +++ b/libraries/shared/src/shared/FileLogger.cpp @@ -38,12 +38,12 @@ private: QMutex _fileMutex; }; -static const QString FILENAME_FORMAT = "hifi-log_%1%2.txt"; +static const QString FILENAME_FORMAT = "vircadia-log_%1%2.txt"; static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; static const QString LOGS_DIRECTORY = "Logs"; static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[01][0-9]-[0-3][0-9]_[0-2][0-9]\\.[0-6][0-9]\\.[0-6][0-9]"; static const QString SESSION_WILDCARD = "[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}"; -static QRegExp LOG_FILENAME_REGEX { "hifi-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\\.txt" }; +static QRegExp LOG_FILENAME_REGEX { "vircadia-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\\.txt" }; static QUuid SESSION_ID; // Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively @@ -68,7 +68,7 @@ QString getLogRollerFilename() { } const QString& getLogFilename() { - static QString fileName = FileUtils::standardPath(LOGS_DIRECTORY) + "hifi-log.txt"; + static QString fileName = FileUtils::standardPath(LOGS_DIRECTORY) + "vircadia-log.txt"; return fileName; } diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 6a2cecf8b9..f64f2758c3 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index f0b27904ae..34184057e0 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -33,13 +33,20 @@ #include "MainWindow.h" /**jsdoc + * The OffscreenFlags API enables gamepad joystick navigation of UI. + * + *

This API currently has no effect and is not used.

+ * * @namespace OffscreenFlags * * @hifi-interface * @hifi-client-entity * @hifi-avatar - * @property {boolean} navigationFocused - * @property {boolean} navigationFocusDisabled + * + * @property {boolean} navigationFocused - true if UI has joystick navigation focus, false if it + * doesn't. + * @property {boolean} navigationFocusDisabled - true if UI joystick navigation focus is disabled, + * false if it isn't. */ // Needs to match the constants in resources/qml/Global.js @@ -72,12 +79,14 @@ public: signals: /**jsdoc + * Triggered when the value of the navigationFocused property changes. * @function OffscreenFlags.navigationFocusedChanged * @returns {Signal} */ void navigationFocusedChanged(); /**jsdoc + * Triggered when the value of the navigationFocusDisabled property changes. * @function OffscreenFlags.navigationFocusDisabledChanged * @returns {Signal} */ diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index 770f8ec965..feca148614 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -12,17 +12,27 @@ #include "QmlWindowClass.h" /**jsdoc + * A OverlayWebWindow displays an HTML window inside Interface. + * + *

Create using new OverlayWebWindow(...).

+ * * @class OverlayWebWindow - * @param {OverlayWindow.Properties} [properties=null] + * @param {string|OverlayWindow.Properties} [titleOrProperties="WebWindow"] - The window's title or initial property values. + * @param {string} [source="about:blank"] - The URL of the HTML to display. Not used unless the first parameter is the window + * title. + * @param {number} [width=0] - The width of the window interior, in pixels. Not used unless the first parameter is the window + * title. + * @param {number} [height=0] - The height of the window interior, in pixels. Not used unless the first parameter is the + * window title. * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {string} url - Read-only. - * @property {Vec2} position - * @property {Vec2} size - * @property {boolean} visible + * @property {string} url - The URL of the HTML displayed in the window. Read-only. + * @property {Vec2} position - The position of the window, in pixels. + * @property {Vec2} size - The size of the window interior, in pixels. + * @property {boolean} visible - true if the window is visible, false if it isn't. * * @borrows OverlayWindow.initQml as initQml * @borrows OverlayWindow.isVisible as isVisible @@ -35,9 +45,9 @@ * @borrows OverlayWindow.raise as raise * @borrows OverlayWindow.close as close * @borrows OverlayWindow.getEventBridge as getEventBridge - * @borrows OverlayWindow.sendToQml as sendToQml - * @borrows OverlayWindow.clearDebugWindow as clearDebugWindow - * @borrows OverlayWindow.emitScriptEvent as emitScriptEvent + * @comment OverlayWindow.sendToQml - Not applicable to OverlayWebWindow; documented separately. + * @comment OverlayWindow.clearDebugWindow - Not applicable to OverlayWebWindow; documented separately. + * @comment OverlayWindow.emitScriptEvent - Documented separately. * @borrows OverlayWindow.emitWebEvent as emitWebEvent * @borrows OverlayWindow.visibleChanged as visibleChanged * @borrows OverlayWindow.positionChanged as positionChanged @@ -45,14 +55,87 @@ * @borrows OverlayWindow.moved as moved * @borrows OverlayWindow.resized as resized * @borrows OverlayWindow.closed as closed - * @borrows OverlayWindow.fromQml as fromQml + * @comment OverlayWindow.fromQml - Not applicable to OverlayWebWindow; documented separately. * @borrows OverlayWindow.scriptEventReceived as scriptEventReceived - * @borrows OverlayWindow.webEventReceived as webEventReceived + * @comment OverlayWindow.webEventReceived - Documented separately. * @borrows OverlayWindow.hasMoved as hasMoved * @borrows OverlayWindow.hasClosed as hasClosed * @borrows OverlayWindow.qmlToScript as qmlToScript */ +/**jsdoc + * @function OverlayWebWindow.clearDebugWindow + * @deprecated This method is deprecated and will be removed. + */ + +/**jsdoc + * @function OverlayWebWindow.sendToQML + * @param {string | object} message - Message. + * @deprecated This method is deprecated and will be removed. + */ + +/**jsdoc + * @function OverlayWebWindow.fromQML + * @param {object} message - Message. + * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. + */ + +/**jsdoc + * Sends a message to the HTML page. To receive the message, the HTML page's script must connect to the EventBridge + * that is automatically provided for the script: + *
EventBridge.scriptEventReceived.connect(function(message) {
+ *     ...
+ * });
+ * @function OverlayWebWindow.emitScriptEvent + * @param {string|object} message - The message to send to the embedded HTML page. + * @example Send and receive messages with an HTML window. + * // JavaScript file. + * + * var overlayWebWindow = new OverlayWebWindow({ + * title: "Overlay Web Window", + * source: Script.resolvePath("OverlayWebWindow.html"), + * width: 400, + * height: 300 + * }); + * + * overlayWebWindow.webEventReceived.connect(function (message) { + * print("Message received: " + message); + * }); + * + * Script.setTimeout(function () { + * overlayWebWindow.emitScriptEvent("Hello world!"); + * }, 2000); + * + * @example + * // HTML file, "OverlayWebWindow.html". + * + * + * + * + * + * + * + *

...

+ * + * + * + */ + +/**jsdoc + * Triggered when a message from the HTML page is received. The HTML page can send a message by calling: + *
EventBridge.emitWebEvent(message);
+ * @function OverlayWebWindow.webEventReceived + * @param {string|object} message - The message received. + * @returns {Signal} + */ + + // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping class QmlWebWindowClass : public QmlWindowClass { Q_OBJECT @@ -74,24 +157,29 @@ public: public slots: /**jsdoc + * Gets the URL of the HTML displayed. * @function OverlayWebWindow.getURL - * @returns {string} + * @returns {string} - The URL of the HTML page displayed. */ QString getURL(); + /**jsdoc + * Loads HTML into the window, replacing current window content. * @function OverlayWebWindow.setURL - * @param {string} url + * @param {string} url - The URL of the HTML to display. */ void setURL(const QString& url); /**jsdoc + * Injects a script into the HTML page, replacing any currently injected script. * @function OverlayWebWindow.setScriptURL - * @param {string} script + * @param {string} url - The URL of the script to inject. */ void setScriptURL(const QString& script); signals: /**jsdoc + * Triggered when the window's URL changes. * @function OverlayWebWindow.urlChanged * @returns {Signal} */ diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index abce5479c4..13a289a5fd 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -90,12 +90,13 @@ QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) { } /**jsdoc + * Properties used to initialize an {@link OverlayWindow} or {@link OverlayWebWindow}. * @typedef {object} OverlayWindow.Properties - * @property {string} title - * @property {string} source - * @property {number} width - * @property {number} height - * @property {boolean} visible + * @property {string} [title="WebWindow] - The window title. + * @property {string} [source] - The source of the QML or HTML to display. + * @property {number} [width=0] - The width of the window interior, in pixels. + * @property {number} [height=0] - The height of the window interior, in pixels. + * @property {boolean} [visible=true] - true if the window should be visible, false if it shouldn't. */ void QmlWindowClass::initQml(QVariantMap properties) { #ifndef DISABLE_QML diff --git a/libraries/ui/src/QmlWindowClass.h b/libraries/ui/src/QmlWindowClass.h index 8aad9a8b36..3a2f202bd3 100644 --- a/libraries/ui/src/QmlWindowClass.h +++ b/libraries/ui/src/QmlWindowClass.h @@ -20,16 +20,29 @@ class QScriptEngine; class QScriptContext; /**jsdoc + * A OverlayWindow displays a QML window inside Interface. + * + *

The QML can optionally include a WebView control that embeds an HTML-based windows. (The WebView + * control is defined by a "WebView.qml" file included in the Interface install.) Alternatively, an {@link OverlayWebWindow} + * can be used for HTML-based windows.

+ * + *

Create using new OverlayWindow(...).

+ * * @class OverlayWindow - * @param {OverlayWindow.Properties} [properties=null] + * @param {string|OverlayWindow.Properties} [titleOrProperties="WebWindow"] - The window's title or initial property values. + * @param {string} [source] - The source of the QML to display. Not used unless the first parameter is the window title. + * @param {number} [width=0] - The width of the window interior, in pixels. Not used unless the first parameter is the window + * title. + * @param {number} [height=0] - The height of the window interior, in pixels. Not used unless the first parameter is the + * window title. * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {Vec2} position - * @property {Vec2} size - * @property {boolean} visible + * @property {Vec2} position - The position of the window, in pixels. + * @property {Vec2} size - The size of the window interior, in pixels. + * @property {boolean} visible - true if the window is visible, false if it isn't. */ // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping @@ -55,8 +68,10 @@ public: /**jsdoc * @function OverlayWindow.initQml - * @param {OverlayWindow.Properties} properties + * @param {OverlayWindow.Properties} properties - Properties. + * @deprecated This method is deprecated and will be removed. */ + // FIXME: This shouldn't be in the API. It is an internal function used in object construction. Q_INVOKABLE virtual void initQml(QVariantMap properties); QQuickItem* asQuickItem() const; @@ -66,150 +81,220 @@ public: public slots: /**jsdoc + * Gets whether the window is shown or hidden. * @function OverlayWindow.isVisible - * @returns {boolean} + * @returns {boolean} code>true
if the window is shown, false if it is hidden. */ bool isVisible(); /**jsdoc + * Shows or hides the window. * @function OverlayWindow.setVisible - * @param {boolean} visible + * @param {boolean} visible - code>true
to show the window, false to hide it. */ void setVisible(bool visible); /**jsdoc + * Gets the position of the window. * @function OverlayWindow.getPosition - * @returns {Vec2} + * @returns {Vec2} The position of the window, in pixels. */ glm::vec2 getPosition(); /**jsdoc + * Sets the position of the window, from a {@link Vec2}. * @function OverlayWindow.setPosition - * @param {Vec2} position + * @param {Vec2} position - The position of the window, in pixels. */ void setPosition(const glm::vec2& position); /**jsdoc + * Sets the position of the window, from a pair of numbers. * @function OverlayWindow.setPosition - * @param {number} x - * @param {number} y + * @param {number} x - The x position of the window, in pixels. + * @param {number} y - The y position of the window, in pixels. */ void setPosition(int x, int y); /**jsdoc + * Gets the size of the window interior. * @function OverlayWindow.getSize - * @returns {Vec2} + * @returns {Vec2} The size of the window interior, in pixels. */ glm::vec2 getSize(); /**jsdoc + * Sets the size of the window interior, from a {@link Vec2}. * @function OverlayWindow.setSize - * @param {Vec2} size + * @param {Vec2} size - The size of the window interior, in pixels. */ void setSize(const glm::vec2& size); /**jsdoc + * Sets the size of the window interior, from a pair of numbers. * @function OverlayWindow.setSize - * @param {number} width - * @param {number} height + * @param {number} width - The width of the window interior, in pixels. + * @param {number} height - The height of the window interior, in pixels. */ void setSize(int width, int height); /**jsdoc + * Sets the window title. * @function OverlayWindow.setTitle - * @param {string} title + * @param {string} title - The window title. */ void setTitle(const QString& title); - /**jsdoc + * Raises the window to the top. * @function OverlayWindow.raise */ Q_INVOKABLE void raise(); /**jsdoc + * Closes the window. + *

Note: The window also closes when the script ends.

* @function OverlayWindow.close */ Q_INVOKABLE void close(); /**jsdoc * @function OverlayWindow.getEventBridge - * @returns {object} + * @returns {object} Object. + * @deprecated This method is deprecated and will be removed. */ + // Shouldn't be in the API: It returns the OverlayWindow object, not the even bridge. Q_INVOKABLE QObject* getEventBridge() { return this; }; /**jsdoc + * Sends a message to the QML. To receive the message, the QML must implement a function: + *
function fromScript(message) {
+     *   ...
+     * }
* @function OverlayWindow.sendToQml - * @param {object} message + * @param {string | object} message - The message to send to the QML. + * @example Send and receive messages with a QML window. + * // JavaScript file. + * + * var overlayWindow = new OverlayWindow({ + * title: "Overlay Window", + * source: Script.resolvePath("OverlayWindow.qml"), + * width: 400, + * height: 300 + * }); + * + * overlayWindow.fromQml.connect(function (message) { + * print("Message received: " + message); + * }); + * + * Script.setTimeout(function () { + * overlayWindow.sendToQml("Hello world!"); + * }, 2000); + * + * @example + * // QML file, "OverlayWindow.qml". + * + * import QtQuick 2.5 + * import QtQuick.Controls 1.4 + * + * Rectangle { + * width: parent.width + * height: parent.height + * + * function fromScript(message) { + * text.text = message; + * sendToScript("Hello back!"); + * } + * + * Label{ + * id: text + * anchors.centerIn : parent + * text : "..." + * } + * } */ // Scripts can use this to send a message to the QML object void sendToQml(const QVariant& message); /**jsdoc + * Calls a clearWindow() function if present in the QML. * @function OverlayWindow.clearDebugWindow */ void clearDebugWindow(); - /**jsdoc + * Sends a message to an embedded HTML web page. To receive the message, the HTML page's script must connect to the + * EventBridge that is automatically provided for the script: + *
EventBridge.scriptEventReceived.connect(function(message) {
+     *     ...
+     * });
* @function OverlayWindow.emitScriptEvent - * @param {object} message + * @param {string|object} message - The message to send to the embedded HTML page. */ // QmlWindow content may include WebView requiring EventBridge. void emitScriptEvent(const QVariant& scriptMessage); /**jsdoc * @function OverlayWindow.emitWebEvent - * @param {object} message + * @param {object|string} message - The message. + * @deprecated This function is deprecated and will be removed. */ void emitWebEvent(const QVariant& webMessage); signals: /**jsdoc + * Triggered when the window is hidden or shown. * @function OverlayWindow.visibleChanged * @returns {Signal} */ void visibleChanged(); /**jsdoc + * Triggered when the window changes position. * @function OverlayWindow.positionChanged * @returns {Signal} */ void positionChanged(); /**jsdoc + * Triggered when the window changes size. * @function OverlayWindow.sizeChanged * @returns {Signal} */ void sizeChanged(); /**jsdoc + * Triggered when the window changes position. * @function OverlayWindow.moved - * @param {Vec2} position + * @param {Vec2} position - The position of the window, in pixels. * @returns {Signal} */ void moved(glm::vec2 position); /**jsdoc + * Triggered when the window changes size. * @function OverlayWindow.resized - * @param {Size} size + * @param {Size} size - The size of the window interior, in pixels. * @returns {Signal} */ void resized(QSizeF size); /**jsdoc + * Triggered when the window is closed. * @function OverlayWindow.closed * @returns {Signal} */ void closed(); /**jsdoc + * Triggered when a message from the QML page is received. The QML page can send a message (string or object) by calling: + *
sendToScript(message);
* @function OverlayWindow.fromQml - * @param {object} message + * @param {object} message - The message received. * @returns {Signal} */ // Scripts can connect to this signal to receive messages from the QML object @@ -218,15 +303,18 @@ signals: /**jsdoc * @function OverlayWindow.scriptEventReceived - * @param {object} message + * @param {object} message - The message. * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. */ // QmlWindow content may include WebView requiring EventBridge. void scriptEventReceived(const QVariant& message); /**jsdoc + * Triggered when a message from an embedded HTML page is received. The HTML page can send a message by calling: + *
EventBridge.emitWebEvent(message);
* @function OverlayWindow.webEventReceived - * @param {object} message + * @param {string|object} message - The message received. * @returns {Signal} */ void webEventReceived(const QVariant& message); @@ -235,22 +323,25 @@ protected slots: /**jsdoc * @function OverlayWindow.hasMoved - * @param {Vec2} position - * @returns {Signal} + * @param {Vec2} position - Position. + * @deprecated This method is deprecated and will be removed. */ + // Shouldn't be in the API: it is just connected to in order to emit a signal. void hasMoved(QVector2D); /**jsdoc * @function OverlayWindow.hasClosed - * @returns {Signal} + * @deprecated This method is deprecated and will be removed. */ + // Shouldn't be in the API: it is just connected to in order to emit a signal. void hasClosed(); /**jsdoc * @function OverlayWindow.qmlToScript - * @param {object} message - * @returns {Signal} + * @param {object} message - Message. + * @deprecated This method is deprecated and will be removed. */ + // Shouldn't be in the API: it is just connected to in order to emit a signal. void qmlToScript(const QVariant& message); protected: diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index dd5ec2a845..5468ee18c3 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -103,9 +103,9 @@ public: * @function Tablet.getTablet * @param {string} name - A unique name that identifies the tablet. * @returns {TabletProxy} The tablet instance. - * @example Display the High Fidelity home page on the system tablet. + * @example Display the Vircadia home page on the system tablet. * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - * tablet.gotoWebScreen("https://projectathena.io/"); + * tablet.gotoWebScreen("https://vircadia.com/"); */ Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId); @@ -217,6 +217,7 @@ Q_DECLARE_METATYPE(TabletButtonsProxyModel*); *

Retrieve an existing tablet or create a new tablet using {@link Tablet.getTablet}.

* * @class TabletProxy + * @hideconstructor * * @hifi-interface * @hifi-client-entity @@ -422,6 +423,12 @@ public: *
EventBridge.scriptEventReceived.connect(function(message) {
      *     ...
      * });
+ *

Warning: The EventBridge object is not necessarily set up immediately ready for the web + * page's script to use. A simple workaround that normally works is to add a delay before calling + * EventBridge.scriptEventReceived.connect(...). A better solution is to periodically call + * EventBridge.scriptEventReceived.connect(...) and then EventBridge.emitWebEvent(...) to send a + * message to the Interface script, and have that send a message back using emitScriptEvent(...); when the + * return message is received, the EventBridge is ready for use.

* @function TabletProxy#emitScriptEvent * @param {string|object} message - The message to send to the web page. */ @@ -586,6 +593,7 @@ Q_DECLARE_METATYPE(TabletProxy*); *

Create a new button using {@link TabletProxy#addButton}.

* * @class TabletButtonProxy + * @hideconstructor * * @hifi-interface * @hifi-client-entity diff --git a/libraries/ui/src/ui/types/FileTypeProfile.cpp b/libraries/ui/src/ui/types/FileTypeProfile.cpp index 615e80b85c..9fca1be436 100644 --- a/libraries/ui/src/ui/types/FileTypeProfile.cpp +++ b/libraries/ui/src/ui/types/FileTypeProfile.cpp @@ -11,21 +11,37 @@ #include "FileTypeProfile.h" +#include +#include #include #include "RequestFilters.h" +#include "NetworkingConstants.h" #if !defined(Q_OS_ANDROID) static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine"; +static std::set FileTypeProfile_instances; +static std::mutex FileTypeProfile_mutex; + FileTypeProfile::FileTypeProfile(QQmlContext* parent) : ContextAwareProfile(parent) { - static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)"; - setHttpUserAgent(WEB_ENGINE_USER_AGENT); + setHttpUserAgent(NetworkingConstants::WEB_ENGINE_USER_AGENT); + + setStorageName(QML_WEB_ENGINE_STORAGE_NAME); + setOffTheRecord(false); auto requestInterceptor = new RequestInterceptor(this); setRequestInterceptor(requestInterceptor); + + std::lock_guard lock(FileTypeProfile_mutex); + FileTypeProfile_instances.insert(this); +} + +FileTypeProfile::~FileTypeProfile() { + std::lock_guard lock(FileTypeProfile_mutex); + FileTypeProfile_instances.erase(this); } void FileTypeProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { @@ -37,5 +53,11 @@ void FileTypeProfile::registerWithContext(QQmlContext* context) { context->setContextProperty("FileTypeProfile", new FileTypeProfile(context)); } +void FileTypeProfile::clearCache() { + std::lock_guard lock(FileTypeProfile_mutex); + foreach (auto instance, FileTypeProfile_instances) { + instance->clearHttpCache(); + } +} #endif \ No newline at end of file diff --git a/libraries/ui/src/ui/types/FileTypeProfile.h b/libraries/ui/src/ui/types/FileTypeProfile.h index 7ddfdd0aed..8d8b921846 100644 --- a/libraries/ui/src/ui/types/FileTypeProfile.h +++ b/libraries/ui/src/ui/types/FileTypeProfile.h @@ -25,8 +25,12 @@ class FileTypeProfile : public ContextAwareProfile { public: static void registerWithContext(QQmlContext* parent); + static void clearCache(); + protected: FileTypeProfile(QQmlContext* parent); + virtual ~FileTypeProfile(); + class RequestInterceptor : public Parent::RequestInterceptor { public: RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {} diff --git a/libraries/ui/src/ui/types/HFWebEngineProfile.cpp b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp index 3c5701cc52..3f2d50e8fa 100644 --- a/libraries/ui/src/ui/types/HFWebEngineProfile.cpp +++ b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp @@ -11,6 +11,8 @@ #include "HFWebEngineProfile.h" +#include +#include #include #include "RequestFilters.h" @@ -19,12 +21,24 @@ static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine"; +static std::set HFWebEngineProfile_instances; +static std::mutex HFWebEngineProfile_mutex; + HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent) { setStorageName(QML_WEB_ENGINE_STORAGE_NAME); + setOffTheRecord(false); // we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user setRequestInterceptor(new RequestInterceptor(this)); + + std::lock_guard lock(HFWebEngineProfile_mutex); + HFWebEngineProfile_instances.insert(this); +} + +HFWebEngineProfile::~HFWebEngineProfile() { + std::lock_guard lock(HFWebEngineProfile_mutex); + HFWebEngineProfile_instances.erase(this); } void HFWebEngineProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { @@ -35,4 +49,11 @@ void HFWebEngineProfile::registerWithContext(QQmlContext* context) { context->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(context)); } +void HFWebEngineProfile::clearCache() { + std::lock_guard lock(HFWebEngineProfile_mutex); + foreach (auto instance, HFWebEngineProfile_instances) { + instance->clearHttpCache(); + } +} + #endif diff --git a/libraries/ui/src/ui/types/HFWebEngineProfile.h b/libraries/ui/src/ui/types/HFWebEngineProfile.h index 6b84ad6f80..872cb31901 100644 --- a/libraries/ui/src/ui/types/HFWebEngineProfile.h +++ b/libraries/ui/src/ui/types/HFWebEngineProfile.h @@ -23,8 +23,12 @@ class HFWebEngineProfile : public ContextAwareProfile { public: static void registerWithContext(QQmlContext* parent); + static void clearCache(); + protected: HFWebEngineProfile(QQmlContext* parent); + virtual ~HFWebEngineProfile(); + class RequestInterceptor : public Parent::RequestInterceptor { public: RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {} diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 943dd02c29..0eea5230ca 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -27,10 +27,10 @@ namespace { bool isAuthableHighFidelityURL(const QUrl& url) { auto metaverseServerURL = MetaverseAPI::getCurrentMetaverseServerURL(); - static const QStringList HF_HOSTS = { - "highfidelity.com", "highfidelity.io", - metaverseServerURL.toString(), "metaverse.highfidelity.io" + static QStringList HF_HOSTS = { + metaverseServerURL.toString() }; + HF_HOSTS << NetworkingConstants::IS_AUTHABLE_HOSTNAME; const auto& scheme = url.scheme(); const auto& host = url.host(); @@ -83,9 +83,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, } } static const QString USER_AGENT = "User-Agent"; - const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" }; - const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" }; - const QString tokenStringLimitedCommerce{ "Chrome/48.0 (HighFidelityInterface limitedCommerce)" }; + const QString tokenStringMobile{ NetworkingConstants::MOBILE_USER_AGENT }; + const QString tokenStringMetaverse{ NetworkingConstants::METAVERSE_USER_AGENT }; + const QString tokenStringLimitedCommerce{ "Chrome/48.0 (VircadiaInterface limitedCommerce)" }; const QString tokenString = !isAuthable ? tokenStringMobile : (accountManager->getLimitedCommerce() ? tokenStringLimitedCommerce : tokenStringMetaverse); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); diff --git a/libraries/workload/src/workload/Space.cpp b/libraries/workload/src/workload/Space.cpp index f045c8311f..5704ba8c4d 100644 --- a/libraries/workload/src/workload/Space.cpp +++ b/libraries/workload/src/workload/Space.cpp @@ -127,6 +127,18 @@ uint32_t Space::copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const { return numCopied; } +uint32_t Space::copySelectedProxyValues(Proxy::Vector& proxies, const workload::indexed_container::Indices& indices) const { + std::unique_lock lock(_proxiesMutex); + uint32_t numCopied = 0; + for (auto index : indices) { + if (isAllocatedID(index) && (index < (Index)_proxies.size())) { + proxies.push_back(_proxies[index]); + ++numCopied; + } + } + return numCopied; +} + const Owner Space::getOwner(int32_t proxyID) const { std::unique_lock lock(_proxiesMutex); if (isAllocatedID(proxyID) && (proxyID < (Index)_proxies.size())) { diff --git a/libraries/workload/src/workload/Space.h b/libraries/workload/src/workload/Space.h index 7dcb2217f7..d189d48156 100644 --- a/libraries/workload/src/workload/Space.h +++ b/libraries/workload/src/workload/Space.h @@ -47,6 +47,7 @@ public: void categorizeAndGetChanges(std::vector& changes); uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const; + uint32_t copySelectedProxyValues(Proxy::Vector& proxies, const workload::indexed_container::Indices& indices) const; const Owner getOwner(int32_t proxyID) const; uint8_t getRegion(int32_t proxyID) const; diff --git a/pkg-scripts/Dockerfile.templ b/pkg-scripts/Dockerfile.templ index 76d27e8c78..e340024bce 100644 --- a/pkg-scripts/Dockerfile.templ +++ b/pkg-scripts/Dockerfile.templ @@ -19,25 +19,25 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ apt-get install -y tzdata supervisor ${DEPENDS} && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ - mkdir -p /var/lib/athena -RUN groupadd -r athena ; \ - useradd -Nr athena -d /var/lib/athena ; \ - usermod -aG athena athena ; \ - chown athena.athena /var/lib/athena ; \ + mkdir -p /var/lib/vircadia +RUN groupadd -r vircadia ; \ + useradd -Nr vircadia -d /var/lib/vircadia ; \ + usermod -aG vircadia vircadia ; \ + chown vircadia.vircadia /var/lib/vircadia ; \ exit 0 -VOLUME /var/lib/athena +VOLUME /var/lib/vircadia RUN mkdir -p /var/run ; chmod 777 /var/run -COPY athena.conf /etc/supervisor/conf.d/athena.conf +COPY vircadia.conf /etc/supervisor/conf.d/vircadia.conf COPY entrypoint.sh / -COPY opt /opt/athena -COPY lib /opt/athena/lib +COPY opt /opt/vircadia +COPY lib /opt/vircadia/lib ENTRYPOINT ["/entrypoint.sh"] -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/athena.conf"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/vircadia.conf"] LABEL \ - net.projectathena.gitsrc="${GITSRC}" \ - net.projectathena.gitdate="${GITDATE}" \ - net.projectathena.gitcommit="${GITCOMMIT}" + net.vircadia.gitsrc="${GITSRC}" \ + net.vircadia.gitdate="${GITDATE}" \ + net.vircadia.gitcommit="${GITCOMMIT}" diff --git a/pkg-scripts/README b/pkg-scripts/README index f4ef24eed4..92a9bf00d3 100644 --- a/pkg-scripts/README +++ b/pkg-scripts/README @@ -1,5 +1,5 @@ Collection of scripts to create server distribution packages. Most of these scripts assume -use of the build script at https://github.com/daleglass/athena-builder, specifically that +use of the build script at https://github.com/kasenvr/vircadia-builder, specifically that the following directory structure exists base folder/ @@ -9,7 +9,7 @@ base folder/ These scripts assume that the current directory is the pkg-scripts folder inside of the source directory and that the base folder can be reached by going to "../..". This may not work if pkg-scripts is a symlink; -adding an ATHENA=~/Athena to the beginning of the commandline will override where it looks for the base folder +adding an VIRCADIA=~/Vircadia to the beginning of the commandline will override where it looks for the base folder Ubuntu: DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server @@ -30,24 +30,24 @@ Docker: Results: The following directory structure is created for binaries: - /opt/athena - executables - /opt/athena/lib - private shared libraries required for executables - /opt/athena/resources - files required by domain-server administrative website - /opt/athena/plugins - files required by assignment-client, mainly for audio codecs + /opt/vircadia - executables + /opt/vircadia/lib - private shared libraries required for executables + /opt/vircadia/resources - files required by domain-server administrative website + /opt/vircadia/plugins - files required by assignment-client, mainly for audio codecs The following systemd services are installed in /usr/lib/systemd/system: - athena-assignment-client.service - athena-domain-server.service - athena-server.target - used to launch/shutdown the two prior services - athena-assignment-client@.service - athena-domain-server@.service - athena-server@.target - used to launch/shutdown the two prior services + vircadia-assignment-client.service + vircadia-domain-server.service + vircadia-server.target - used to launch/shutdown the two prior services + vircadia-assignment-client@.service + vircadia-domain-server@.service + vircadia-server@.target - used to launch/shutdown the two prior services - The top three services in this list are the "normal" services that launch Athena + The top three services in this list are the "normal" services that launch Vircadia in the typical fashion. The bottom three services are "template" services designed to permit multiple services to be installed and running on a single machine. - The script "/opt/athena/new-server serverName basePort" will do the necessary + The script "/opt/vircadia/new-server serverName basePort" will do the necessary setup for a new domain with the specified server name and port. Upon installation the package will create and launch a domain named "default" at base port 40100. The domain name here has nothing to do with the name people will use to find your @@ -55,6 +55,6 @@ Results: used to configure and run the domain on your server. The server stores its files in the following locations: - /var/lib/athena/.local - "unnamed" services (the default location for Athena servers) - /var/lib/athena/serverName - "named" (template) domains - /etc/opt/athena - environment variables when launching named domains + /var/lib/vircadia/.local - "unnamed" services (the default location for Vircadia servers) + /var/lib/vircadia/serverName - "named" (template) domains + /etc/opt/vircadia - environment variables when launching named domains diff --git a/pkg-scripts/athena-assignment-client.service b/pkg-scripts/athena-assignment-client.service deleted file mode 100644 index a6407294dc..0000000000 --- a/pkg-scripts/athena-assignment-client.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Assignment client service for Athena server -After=network.target -PartOf=athena-server.target - -[Service] -Restart=always - -WorkingDirectory=/opt/athena -Environment="LD_LIBRARY_PATH=/opt/athena/lib" -User=athena -Group=athena -#LimitCORE=infinity -#ExecStart=/opt/athena/assignment-client -n 6 -ExecStart=/opt/athena/assignment-client --min 6 --max 20 - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-assignment-client@.service b/pkg-scripts/athena-assignment-client@.service deleted file mode 100644 index 7f60ed4d28..0000000000 --- a/pkg-scripts/athena-assignment-client@.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=Assignment client service for Athena server -After=network.target -PartOf=athena-server@%i.target - -[Service] -Restart=always - -WorkingDirectory=/opt/athena -EnvironmentFile=/etc/opt/athena/%i.conf -Environment="LD_LIBRARY_PATH=/opt/athena/lib" "HOME=/var/lib/athena/%i" -PrivateTmp=true -User=athena -Group=athena -#LimitCORE=infinity -#ExecStart=/opt/athena/assignment-client -n 6 -ExecStart=/opt/athena/assignment-client --min 6 --max 20 --server-port $HIFI_DOMAIN_SERVER_PORT - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-domain-server.service b/pkg-scripts/athena-domain-server.service deleted file mode 100644 index a5db9dc269..0000000000 --- a/pkg-scripts/athena-domain-server.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Domain Server service for Athena -After=network.target -PartOf=athena-server.target - -[Service] -Restart=on-failure - -WorkingDirectory=/opt/athena -Environment="LD_LIBRARY_PATH=/opt/athena/lib" -User=athena -Group=athena -#LimitCORE=infinity -#ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l domain-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/domain-server/domain-server; fi' -ExecStart=/opt/athena/domain-server - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-domain-server@.service b/pkg-scripts/athena-domain-server@.service deleted file mode 100644 index 330d8ac81d..0000000000 --- a/pkg-scripts/athena-domain-server@.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=Domain Server service for Athena -After=network.target -PartOf=athena-server@%i.target - -[Service] -Restart=on-failure - -WorkingDirectory=/opt/athena -EnvironmentFile=/etc/opt/athena/%i.conf -Environment="LD_LIBRARY_PATH=/opt/athena/lib" "HOME=/var/lib/athena/%i" -PrivateTmp=true -User=athena -Group=athena -#LimitCORE=infinity -#ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l domain-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/domain-server/domain-server; fi' -ExecStart=/opt/athena/domain-server - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-server.spec b/pkg-scripts/athena-server.spec deleted file mode 100644 index 7910c8114b..0000000000 --- a/pkg-scripts/athena-server.spec +++ /dev/null @@ -1,128 +0,0 @@ -#ATHENA=~/Athena rpmbuild --target x86_64 -bb athena-server.spec -%define version %{lua:print(os.getenv("VERSION"))} -%define depends %{lua:print(os.getenv("DEPENDS"))} - -Name: athena-server -Version: %{version} -Release: 1%{?dist} -Summary: Project Athena metaverse platform, based on the High Fidelity Engine. - -License: ASL 2.0 -URL: https://projectathena.io -Source0: https://github.com/daleglass/athena-builder/blob/master/athena_builder - -#BuildRequires: systemd-rpm-macros -BuildRequires: chrpath -Requires: %{depends} -BuildArch: x86_64 -AutoReq: no -AutoProv: no - -%description -Project Athena allows creation and sharing of VR experiences. - The Project Athena metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. - - -%prep - - -%build - - -%install -rm -rf $RPM_BUILD_ROOT -install -d $RPM_BUILD_ROOT/opt/athena -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/assignment-client/assignment-client -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/domain-server/domain-server -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/tools/oven/oven -#install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/ice-server/ice-server -strip --strip-all $RPM_BUILD_ROOT/opt/athena/* -chrpath -d $RPM_BUILD_ROOT/opt/athena/* -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/source/pkg-scripts/new-server -install -d $RPM_BUILD_ROOT/opt/athena/lib -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/build/libraries/*/*.so -strip --strip-all $RPM_BUILD_ROOT/opt/athena/lib/* -chrpath -d $RPM_BUILD_ROOT/opt/athena/lib/* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* -install -d $RPM_BUILD_ROOT/usr/lib/systemd/system -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-assignment-client.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-assignment-client@.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-domain-server.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-domain-server@.service -#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-ice-server.service -#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-ice-server@.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-server.target -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-server@.target -cp -a $ATHENA/source/domain-server/resources $RPM_BUILD_ROOT/opt/athena -cp -a $ATHENA/build/assignment-client/plugins $RPM_BUILD_ROOT/opt/athena -chrpath -d $RPM_BUILD_ROOT/opt/athena/plugins/*.so -chrpath -d $RPM_BUILD_ROOT/opt/athena/plugins/*/*.so -strip --strip-all $RPM_BUILD_ROOT/opt/athena/plugins/*.so -strip --strip-all $RPM_BUILD_ROOT/opt/athena/plugins/*/*.so -find $RPM_BUILD_ROOT/opt/athena/resources -name ".gitignore" -delete - - -%files -%license $ATHENA/source/LICENSE -/opt/athena -/usr/lib/systemd/system - - -%changelog - - -%post -# create users -getent passwd athena >/dev/numm 2>&1 || useradd -r -c "Project Athena" -d /var/lib/athena -U -M athena -#getent group athena >/dev/null 2>&1 || groupadd -r athena - -# create data folder -mkdir -p /etc/opt/athena -mkdir -p /var/lib/athena && chown athena:athena /var/lib/athena && chmod 775 /var/lib/athena - -ldconfig -n /opt/athena/lib -if [ ! -d "/var/lib/athena/default" ]; then - /opt/athena/new-server default 40100 - systemctl enable athena-server@default.target - systemctl start athena-server@default.target -fi - -%systemd_post athena-assignment-client.service -%systemd_post athena-assignment-client@.service -%systemd_post athena-domain-server.service -%systemd_post athena-domain-server@.service -#%systemd_post athena-ice-server.service -#%systemd_post athena-ice-server@.service -%systemd_post athena-server.target -%systemd_post athena-server@.target - - -%preun -%systemd_preun athena-server.target -%systemd_preun athena-server@.target -%systemd_preun athena-assignment-client.service -%systemd_preun athena-assignment-client@.service -%systemd_preun athena-domain-server.service -%systemd_preun athena-domain-server@.service -#%systemd_preun athena-ice-server.service -#%systemd_preun athena-ice-server@.service - - -%postun -%systemd_postun_with_restart athena-server.target -%systemd_postun_with_restart athena-server@.target -%systemd_postun_with_restart athena-assignment-client.service -%systemd_postun_with_restart athena-assignment-client@.service -%systemd_postun_with_restart athena-domain-server.service -%systemd_postun_with_restart athena-domain-server@.service -#%systemd_postun_with_restart athena-ice-server.service -#%systemd_postun_with_restart athena-ice-server@.service diff --git a/pkg-scripts/athena-server.target b/pkg-scripts/athena-server.target deleted file mode 100644 index e3929b401b..0000000000 --- a/pkg-scripts/athena-server.target +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Athena virtual domain -Wants=athena-assignment-client.service -Wants=athena-domain-server.service -#Wants=athena-ice-server.service -After=athena-assignment-client.service -After=athena-domain-server.service -#After=athena-ice-server.service - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-server@.target b/pkg-scripts/athena-server@.target deleted file mode 100644 index 8ac451ca80..0000000000 --- a/pkg-scripts/athena-server@.target +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Athena virtual domain -Wants=athena-assignment-client@%i.service -Wants=athena-domain-server@%i.service -#Wants=athena-ice-server@%i.service -After=athena-assignment-client@%i.service -After=athena-domain-server@%i.service -#After=athena-ice-server@%i.service - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/docker-entrypoint.sh b/pkg-scripts/docker-entrypoint.sh index 4a4a7506dc..b519647edb 100755 --- a/pkg-scripts/docker-entrypoint.sh +++ b/pkg-scripts/docker-entrypoint.sh @@ -6,23 +6,23 @@ set -x # allowing the container to be run directly as Jenkins. In Dev, or on unknown # environments, run the container as root to automatically correct docker # group in container to match the docker.sock GID mounted from the host. -if [ -f /var/lib/athena/.local -a "$(id -u)" = "0" ]; then +if [ -f /var/lib/vircadia/.local -a "$(id -u)" = "0" ]; then # realign gid - THIS_ATHENA_GID=`ls -ngd /var/lib/athena/.local | cut -f3 -d' '` - CUR_ATHENA_GID=`getent group athena | cut -f3 -d: || true` - if [ ! -z "$THIS_ATHENA_GID" -a "$THIS_ATHENA_GID" != "$CUR_ATHENA_GID" ]; then - groupmod -g ${THIS_ATHENA_GID} -o athena + THIS_VIRCADIA_GID=`ls -ngd /var/lib/vircadia/.local | cut -f3 -d' '` + CUR_VIRCADIA_GID=`getent group vircadia | cut -f3 -d: || true` + if [ ! -z "$THIS_VIRCADIA_GID" -a "$THIS_VIRCADIA_GID" != "$CUR_VIRCADIA_GID" ]; then + groupmod -g ${THIS_VIRCADIA_GID} -o vircadia fi # realign pid - THIS_ATHENA_PID=`ls -nd /var/lib/athena/.local | cut -f3 -d' '` - CUR_ATHENA_PID=`getent passwd athena | cut -f3 -d: || true` - if [ ! -z "$THIS_ATHENA_PID" -a "$THIS_ATHENA_PID" != "$CUR_ATHENA_PID" ]; then - usermod -u ${THIS_ATHENA_PID} -o athena + THIS_VIRCADIA_PID=`ls -nd /var/lib/vircadia/.local | cut -f3 -d' '` + CUR_VIRCADIA_PID=`getent passwd vircadia | cut -f3 -d: || true` + if [ ! -z "$THIS_VIRCADIA_PID" -a "$THIS_VIRCADIA_PID" != "$CUR_VIRCADIA_PID" ]; then + usermod -u ${THIS_VIRCADIA_PID} -o vircadia fi - if ! groups athena | grep -q athena; then - usermod -aG athena athena + if ! groups vircadia | grep -q vircadia; then + usermod -aG vircadia vircadia fi fi diff --git a/pkg-scripts/docker-athena-supervisor.conf b/pkg-scripts/docker-vircadia-supervisor.conf similarity index 60% rename from pkg-scripts/docker-athena-supervisor.conf rename to pkg-scripts/docker-vircadia-supervisor.conf index d6e53996b6..3ee1961c80 100644 --- a/pkg-scripts/docker-athena-supervisor.conf +++ b/pkg-scripts/docker-vircadia-supervisor.conf @@ -1,74 +1,74 @@ [supervisord] -user=athena +user=vircadia nodaemon=true -environment=HOME="/var/lib/athena",USER="athena",LD_LIBRARY_PATH="/opt/athena/lib" +environment=HOME="/var/lib/vircadia",USER="vircadia",LD_LIBRARY_PATH="/opt/vircadia/lib" logfile=/dev/stdout logfile_maxbytes=0 pidfile=/var/run/supervisord.pid [program:domain-server] -command=/opt/athena/domain-server +command=/opt/vircadia/domain-server autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia [program:audio-mixer] -command=/opt/athena/assignment-client -t 0 -a localhost -p 48000 +command=/opt/vircadia/assignment-client -t 0 -a localhost -p 48000 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:avatar-mixer] -command=/opt/athena/assignment-client -t 1 -a localhost -p 48001 +command=/opt/vircadia/assignment-client -t 1 -a localhost -p 48001 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:entities-server] -command=/opt/athena/assignment-client -t 6 -a localhost -p 48006 +command=/opt/vircadia/assignment-client -t 6 -a localhost -p 48006 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:asset-server] -command=/opt/athena/assignment-client -t 3 -a localhost -p 48003 +command=/opt/vircadia/assignment-client -t 3 -a localhost -p 48003 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:entity-script-server] -command=/opt/athena/assignment-client -t 5 -a localhost -p 48005 +command=/opt/vircadia/assignment-client -t 5 -a localhost -p 48005 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:messages-mixer] -command=/opt/athena/assignment-client -t 4 -a localhost -p 48004 +command=/opt/vircadia/assignment-client -t 4 -a localhost -p 48004 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:scripted-agent] -command=/opt/athena/assignment-client -t 2 -a localhost --max 100 +command=/opt/vircadia/assignment-client -t 2 -a localhost --max 100 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr diff --git a/pkg-scripts/make-deb-server b/pkg-scripts/make-deb-server index 72304ae414..6a7bff26c3 100755 --- a/pkg-scripts/make-deb-server +++ b/pkg-scripts/make-deb-server @@ -1,107 +1,99 @@ #!/bin/sh -if [ "$ATHENA" = "" ]; then - ATHENA=`realpath ../..` +if [ "$VIRCADIA" = "" ]; then + VIRCADIA=`realpath ../..` fi -GITDATE=`git -C $ATHENA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` -GITCOMMIT=`git -C $ATHENA/source rev-parse HEAD | cut -c 1-7` -VERSION=0.86.0-k2-$GITDATE-$GITCOMMIT +GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` +GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` +VERSION=2020.2.0-asteria-$GITDATE-$GITCOMMIT sudo apt-get install chrpath binutils dh-make -DEB_BUILD_ROOT=temp-make-deb/athena-server-$VERSION-0ubuntu1 +DEB_BUILD_ROOT=temp-make-deb/vircadia-server-$VERSION-0ubuntu1 rm -r temp-make-deb mkdir -p $DEB_BUILD_ROOT # copy the files over -cp $ATHENA/build/assignment-client/assignment-client $DEB_BUILD_ROOT -cp $ATHENA/build/domain-server/domain-server $DEB_BUILD_ROOT -cp $ATHENA/build/tools/oven/oven $DEB_BUILD_ROOT -cp $ATHENA/build/libraries/*/*.so $DEB_BUILD_ROOT -#cp $ATHENA/build/ice-server/ice-server $DEB_BUILD_ROOT +cp $VIRCADIA/build/assignment-client/assignment-client $DEB_BUILD_ROOT +cp $VIRCADIA/build/domain-server/domain-server $DEB_BUILD_ROOT +cp $VIRCADIA/build/tools/oven/oven $DEB_BUILD_ROOT +cp $VIRCADIA/build/libraries/*/*.so $DEB_BUILD_ROOT +#cp $VIRCADIA/build/ice-server/ice-server $DEB_BUILD_ROOT chrpath -d $DEB_BUILD_ROOT/* -cp $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DEB_BUILD_ROOT chmod +x $DEB_BUILD_ROOT/*.so.*.*.* strip --strip-all $DEB_BUILD_ROOT/* -cp $ATHENA/source/pkg-scripts/new-server $DEB_BUILD_ROOT -cp -a $ATHENA/source/domain-server/resources $DEB_BUILD_ROOT +cp $VIRCADIA/source/pkg-scripts/new-server $DEB_BUILD_ROOT +cp -a $VIRCADIA/source/domain-server/resources $DEB_BUILD_ROOT find $DEB_BUILD_ROOT/resources -name ".gitignore" -delete find $DEB_BUILD_ROOT/resources -type f -executable -exec sh -c 'chmod -x {}' \; -cp $ATHENA/source/README.md $DEB_BUILD_ROOT -cp $ATHENA/source/README_hifi.md $DEB_BUILD_ROOT -cp -a $ATHENA/build/assignment-client/plugins $DEB_BUILD_ROOT +cp $VIRCADIA/source/README.md $DEB_BUILD_ROOT +cp $VIRCADIA/source/README_hifi.md $DEB_BUILD_ROOT +cp -a $VIRCADIA/build/assignment-client/plugins $DEB_BUILD_ROOT strip --strip-all $DEB_BUILD_ROOT/plugins/*.so strip --strip-all $DEB_BUILD_ROOT/plugins/*/*.so #begin the debian package construction cd $DEB_BUILD_ROOT -dh_make -p athena-server_$VERSION-0ubuntu1 -c apache -s --createorig -y +dh_make -p vircadia-server_$VERSION-0ubuntu1 -c apache -s --createorig -y -cp $ATHENA/source/pkg-scripts/athena-assignment-client.service debian -cp $ATHENA/source/pkg-scripts/athena-assignment-client@.service debian -cp $ATHENA/source/pkg-scripts/athena-domain-server.service debian -cp $ATHENA/source/pkg-scripts/athena-domain-server@.service debian -#cp $ATHENA/source/pkg-scripts/athena-ice-server.service debian -#cp $ATHENA/source/pkg-scripts/athena-ice-server@.service debian -cp $ATHENA/source/pkg-scripts/athena-server.target debian -cp $ATHENA/source/pkg-scripts/athena-server@.target debian +cp $VIRCADIA/source/pkg-scripts/vircadia-assignment-client.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-assignment-client@.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-domain-server.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-domain-server@.service debian +#cp $VIRCADIA/source/pkg-scripts/vircadia-ice-server.service debian +#cp $VIRCADIA/source/pkg-scripts/vircadia-ice-server@.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-server.target debian +cp $VIRCADIA/source/pkg-scripts/vircadia-server@.target debian -cp $ATHENA/source/pkg-scripts/server-control debian/control -cp $ATHENA/source/pkg-scripts/server-prerm debian/prerm -cp $ATHENA/source/pkg-scripts/server-postinst debian/postinst -cp $ATHENA/source/LICENSE debian/copyright +cp $VIRCADIA/source/pkg-scripts/server-control debian/control +cp $VIRCADIA/source/pkg-scripts/server-prerm debian/prerm +cp $VIRCADIA/source/pkg-scripts/server-postinst debian/postinst +cp $VIRCADIA/source/pkg-scripts/server-postrm debian/postrm +cp $VIRCADIA/source/LICENSE debian/copyright -echo /etc/opt/athena > debian/dirs -echo /var/lib/athena >> debian/dirs +echo /etc/opt/vircadia > debian/dirs +echo /var/lib/vircadia >> debian/dirs echo README.md > debian/docs echo README_hifi.md >> debian/docs -echo assignment-client opt/athena > debian/install -echo domain-server opt/athena >> debian/install -echo oven opt/athena >> debian/install -#echo ice-server opt/athena >> debian/install -echo new-server opt/athena >> debian/install +echo assignment-client opt/vircadia > debian/install +echo domain-server opt/vircadia >> debian/install +echo oven opt/vircadia >> debian/install +#echo ice-server opt/vircadia >> debian/install +echo new-server opt/vircadia >> debian/install for so in *.so.*.*.*; do - echo $so opt/athena/lib >> debian/install + echo $so opt/vircadia/lib >> debian/install done for so in *.so; do - echo $so opt/athena/lib >> debian/install + echo $so opt/vircadia/lib >> debian/install done #for service in *.service; do -# echo $service opt/athena/systemd >> debian/install +# echo $service opt/vircadia/systemd >> debian/install #done #for target in *.target; do -# echo $target opt/athena/systemd >> debian/install +# echo $target opt/vircadia/systemd >> debian/install #done -find resources -type f -exec sh -c 'echo {} opt/athena/$(dirname "{}") >> debian/install' \; -find plugins -type f -exec sh -c 'echo {} opt/athena/$(dirname "{}") >> debian/install' \; +find resources -type f -exec sh -c 'echo {} opt/vircadia/$(dirname "{}") >> debian/install' \; +find plugins -type f -exec sh -c 'echo {} opt/vircadia/$(dirname "{}") >> debian/install' \; -#echo usr/lib/systemd/system/athena-assignment-client.service opt/athena/systemd/athena-assignment-client.service > debian/athena-server.links -#echo usr/lib/systemd/system/athena-assignment-client@.service opt/athena/systemd/athena-assignment-client@.service >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-domain-server.service opt/athena/systemd/athena-domain-server.service >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-domain-server@.service opt/athena/systemd/athena-domain-server@.service >> debian/athena-server.links -##echo usr/lib/systemd/system/athena-ice-server.service opt/athena/systemd/athena-ice-server.service >> debian/athena-server.links -##echo usr/lib/systemd/system/athena-ice-server@.service opt/athena/systemd/athena-ice-server@.service >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-server.target opt/athena/systemd/athena-server.target >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-server@.target opt/athena/systemd/athena-server@.target >> debian/athena-server.links +SOFILES=`ls *.so *.so.*.*.* | grep -Po '^(.+\.so(\.\d+)?)' | sed 's/\./\\\./g' | paste -d'|' -s` -SOFILES=`ls *.so *.so.*.*.* | sed 's/\./\\\./g' | paste -d'|' -s` +DEPENDS=`find * -path debian -prune -o -type f -executable -exec sh -c 'objdump -p {} | grep NEEDED' \; \ + | awk '{print $2}' | sort | uniq | egrep -v "^($SOFILES)$" \ + | xargs -n 1 -I {} sh -c 'dpkg -S {} | head -n 1' | cut -d ':' -f 1 | sort | uniq | paste -d',' -s` -DEPENDS=`find * -type f -executable -exec sh -c 'objdump -p {} | grep NEEDED' \; | awk '{print $2}' | sort | uniq | egrep -v "^($SOFILES)$" | xargs -n 1 -I {} sh -c 'dpkg -S {} | head -n 1' | cut -d ':' -f 1 | sort | uniq | paste -d',' -s` - -cp $ATHENA/source/pkg-scripts/server-rules debian/rules -sed "s/{DEPENDS}/$DEPENDS/" $ATHENA/source/pkg-scripts/server-control > debian/control +cp $VIRCADIA/source/pkg-scripts/server-rules debian/rules +sed "s/{DEPENDS}/$DEPENDS/" $VIRCADIA/source/pkg-scripts/server-control > debian/control dpkg-buildpackage -us -uc diff --git a/pkg-scripts/make-docker-server b/pkg-scripts/make-docker-server index 3ad37bb0e2..68b8ca7cd6 100755 --- a/pkg-scripts/make-docker-server +++ b/pkg-scripts/make-docker-server @@ -1,53 +1,51 @@ #!/bin/sh -if [ "$ATHENA" = "" ]; then - ATHENA=`realpath ../..` +if [ "$VIRCADIA" = "" ]; then + VIRCADIA=`realpath ../..` fi -GITSRC=`git -C $ATHENA/source config --get remote.origin.url | cut -d':' -f 2` -GITDATE=`git -C $ATHENA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` -GITCOMMIT=`git -C $ATHENA/source rev-parse HEAD | cut -c 1-7` +GITSRC=`git -C $VIRCADIA/source config --get remote.origin.url | cut -d':' -f 2` +GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` +GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` sudo apt-get install chrpath binutils DOCK_BUILD_ROOT=temp-make-dock rm -r temp-make-dock mkdir -p $DOCK_BUILD_ROOT -cp $ATHENA/source/pkg-scripts/Dockerfile.templ $DOCK_BUILD_ROOT/Dockerfile -cp $ATHENA/source/pkg-scripts/docker-entrypoint.sh $DOCK_BUILD_ROOT/entrypoint.sh -cp $ATHENA/source/pkg-scripts/docker-athena-supervisor.conf $DOCK_BUILD_ROOT/athena.conf +cp $VIRCADIA/source/pkg-scripts/Dockerfile.templ $DOCK_BUILD_ROOT/Dockerfile +cp $VIRCADIA/source/pkg-scripts/docker-entrypoint.sh $DOCK_BUILD_ROOT/entrypoint.sh +cp $VIRCADIA/source/pkg-scripts/docker-vircadia-supervisor.conf $DOCK_BUILD_ROOT/vircadia.conf # copy the files over mkdir -p $DOCK_BUILD_ROOT/opt -cp $ATHENA/build/assignment-client/assignment-client $DOCK_BUILD_ROOT/opt -cp $ATHENA/build/domain-server/domain-server $DOCK_BUILD_ROOT/opt -cp $ATHENA/build/tools/oven/oven $DOCK_BUILD_ROOT/opt -#cp $ATHENA/build/ice-server/ice-server $DOCK_BUILD_ROOT/opt +cp $VIRCADIA/build/assignment-client/assignment-client $DOCK_BUILD_ROOT/opt +cp $VIRCADIA/build/domain-server/domain-server $DOCK_BUILD_ROOT/opt +cp $VIRCADIA/build/tools/oven/oven $DOCK_BUILD_ROOT/opt +#cp $VIRCADIA/build/ice-server/ice-server $DOCK_BUILD_ROOT/opt strip --strip-all $DOCK_BUILD_ROOT/opt/* chrpath -d $DOCK_BUILD_ROOT/opt/* -cp -a $ATHENA/build/assignment-client/plugins $DOCK_BUILD_ROOT/opt +cp -a $VIRCADIA/build/assignment-client/plugins $DOCK_BUILD_ROOT/opt strip --strip-all $DOCK_BUILD_ROOT/opt/plugins/*.so chrpath -d $DOCK_BUILD_ROOT/opt/plugins/*.so strip --strip-all $DOCK_BUILD_ROOT/opt/plugins/*/*.so chrpath -d $DOCK_BUILD_ROOT/opt/plugins/*/*.so -cp -a $ATHENA/source/domain-server/resources $DOCK_BUILD_ROOT/opt +cp -a $VIRCADIA/source/domain-server/resources $DOCK_BUILD_ROOT/opt find $DOCK_BUILD_ROOT/opt/resources -name ".gitignore" -delete find $DOCK_BUILD_ROOT/opt/resources -type f -executable -exec sh -c 'chmod -x {}' \; mkdir -p $DOCK_BUILD_ROOT/lib -cp $ATHENA/build/libraries/*/*.so $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/build/libraries/*/*.so $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DOCK_BUILD_ROOT/lib chmod +x $DOCK_BUILD_ROOT/lib/* strip --strip-all $DOCK_BUILD_ROOT/lib/* chrpath -d $DOCK_BUILD_ROOT/lib/* @@ -57,4 +55,4 @@ SOFILES=`ls $DOCK_BUILD_ROOT/lib | sed 's/\./\\\./g' | paste -d'|' -s` DEPENDS=`find $DOCK_BUILD_ROOT/opt $DOCK_BUILD_ROOT/lib -type f -executable -exec sh -c 'objdump -p {} | grep NEEDED' \; | awk '{print $2}' | sort | uniq | egrep -v "^($SOFILES)$" | xargs -n 1 -I {} sh -c 'dpkg -S {} | head -n 1' | cut -d ':' -f 1 | sort | uniq | paste -d' ' -s` cd $DOCK_BUILD_ROOT -docker build -t odysseus654/athena-server --build-arg "DEPENDS=$DEPENDS" --build-arg "GITSRC=$GITSRC" --build-arg "GITDATE=$GITDATE" --build-arg "GITCOMMIT=$GITCOMMIT" . +docker build -t odysseus654/vircadia-server --build-arg "DEPENDS=$DEPENDS" --build-arg "GITSRC=$GITSRC" --build-arg "GITDATE=$GITDATE" --build-arg "GITCOMMIT=$GITCOMMIT" . diff --git a/pkg-scripts/make-rpm-server b/pkg-scripts/make-rpm-server index 9863faa0ab..20ac76d4b0 100755 --- a/pkg-scripts/make-rpm-server +++ b/pkg-scripts/make-rpm-server @@ -1,44 +1,42 @@ #!/bin/sh -if [ "$ATHENA" = "" ]; then - ATHENA=`realpath ../..` +if [ "$VIRCADIA" = "" ]; then + VIRCADIA=`realpath ../..` fi -GITDATE=`git -C $ATHENA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` -GITCOMMIT=`git -C $ATHENA/source rev-parse HEAD | cut -c 1-7` -VERSION=0.86.0_K2_${GITDATE}_${GITCOMMIT} +GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` +GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` +VERSION=2020.2.0_ASTERIA_${GITDATE}_${GITCOMMIT} SOFILES=`ls \ - $ATHENA/build/libraries/*/*.so \ - $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ - $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* \ + $VIRCADIA/build/libraries/*/*.so \ + $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Quick.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ | sed 's/\./\\\./g' \ | paste -d'|' -s` DEPENDS=mesa-libGL,`ls \ - $ATHENA/build/assignment-client/assignment-client \ - $ATHENA/build/domain-server/domain-server \ - $ATHENA/build/tools/oven/oven \ - $ATHENA/build/libraries/*/*.so \ - $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ - $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* \ - $ATHENA/build/assignment-client/plugins/*.so \ - $ATHENA/build/assignment-client/plugins/*/*.so \ + $VIRCADIA/build/assignment-client/assignment-client \ + $VIRCADIA/build/domain-server/domain-server \ + $VIRCADIA/build/tools/oven/oven \ + $VIRCADIA/build/libraries/*/*.so \ + $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Quick.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ + $VIRCADIA/build/assignment-client/plugins/*.so \ + $VIRCADIA/build/assignment-client/plugins/*/*.so \ | xargs -I {} sh -c 'objdump -p {} | grep NEEDED' \ | awk '{print $2}' \ | sort | uniq \ @@ -51,6 +49,6 @@ DEPENDS=mesa-libGL,`ls \ sudo yum install chrpath -export VERSION DEPENDS ATHENA -rpmbuild --target x86_64 -bb ./athena-server.spec +export VERSION DEPENDS VIRCADIA +rpmbuild --target x86_64 -bb ./vircadia-server.spec mv ~/rpmbuild/RPMS/x86_64/*.rpm . diff --git a/pkg-scripts/new-server b/pkg-scripts/new-server index fd9ba95761..a2ce9b330d 100755 --- a/pkg-scripts/new-server +++ b/pkg-scripts/new-server @@ -2,13 +2,13 @@ if [ -z "$1" ] || [ -z "$2" ]; then echo "new-server {name} {base-port}" echo - echo "Sets up a new athena server with the specified name and base port number" + echo "Sets up a new vircadia server with the specified name and base port number" echo " {name} - a simple name used to identify the server to scripts (not used in the server configuration)" echo " {base-port} - the base port number (default server is 40100). The metaverse port will be {base-port}+2" echo " Four contiguous port numbers are allocated, these must not overlap with other running services on this machine" echo echo "Launching a server created by this script is done with:" - echo " sudo systemctl start athena-server@{name}.target" + echo " sudo systemctl start vircadia-server@{name}.target" echo exit 1 fi @@ -20,30 +20,30 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi -if [ -d "/var/lib/athena/$1" ]; then - echo "Path /var/lib/athena/$1 already exists" +if [ -d "/var/lib/vircadia/$1" ]; then + echo "Path /var/lib/vircadia/$1 already exists" echo echo "Please remove this path first if you wish to recreate this server" exit 2 fi -mkdir -p /var/lib/athena/$1/.local/share -ln -s ../.. /var/lib/athena/$1/.local/share/Project\ Athena\ -\ dev -ln -s ../.. /var/lib/athena/$1/.local/share/Project\ Athena -mkdir -p /var/lib/athena/$1/domain-server -echo "{\"metaverse\": {\"local_port\": $(($2 + 2))},\"version\": 2.4}" > /var/lib/athena/$1/domain-server/config.json -chown -R athena.athena /var/lib/athena/$1 +mkdir -p /var/lib/vircadia/$1/.local/share +ln -s ../.. /var/lib/vircadia/$1/.local/share/Vircadia\ -\ dev +ln -s ../.. /var/lib/vircadia/$1/.local/share/Vircadia +mkdir -p /var/lib/vircadia/$1/domain-server +echo "{\"metaverse\": {\"local_port\": $(($2 + 2))},\"version\": 2.4}" > /var/lib/vircadia/$1/domain-server/config.json +chown -R vircadia.vircadia /var/lib/vircadia/$1 -echo HIFI_DOMAIN_SERVER_HTTP_PORT=$2 > /etc/opt/athena/$1.conf -echo HIFI_DOMAIN_SERVER_HTTPS_PORT=$(($2 + 1)) >> /etc/opt/athena/$1.conf -echo HIFI_DOMAIN_SERVER_PORT=$(($2 + 2)) >> /etc/opt/athena/$1.conf -echo HIFI_DOMAIN_SERVER_DTLS_PORT=$(($2 + 3)) >> /etc/opt/athena/$1.conf +echo HIFI_DOMAIN_SERVER_HTTP_PORT=$2 > /etc/opt/vircadia/$1.conf +echo HIFI_DOMAIN_SERVER_HTTPS_PORT=$(($2 + 1)) >> /etc/opt/vircadia/$1.conf +echo HIFI_DOMAIN_SERVER_PORT=$(($2 + 2)) >> /etc/opt/vircadia/$1.conf +echo HIFI_DOMAIN_SERVER_DTLS_PORT=$(($2 + 3)) >> /etc/opt/vircadia/$1.conf -echo "A new athena server has been created with the name of '$1'" +echo "A new vircadia server has been created with the name of '$1'" echo echo "To launch it:" -echo " sudo systemctl start athena-server@$1.target" +echo " sudo systemctl start vircadia-server@$1.target" echo "To have it launch at system start:" -echo " sudo systemctl enable athena-server@$1.target" +echo " sudo systemctl enable vircadia-server@$1.target" echo "The server configuration console is available at:" echo " http://localhost:$2" diff --git a/pkg-scripts/server-control b/pkg-scripts/server-control index 70383891bd..c80b8da724 100644 --- a/pkg-scripts/server-control +++ b/pkg-scripts/server-control @@ -1,15 +1,15 @@ -Source: athena-server +Source: vircadia-server Section: comm Priority: optional Maintainer: Heather Anderson Build-Depends: debhelper (>= 10) Standards-Version: 4.1.2 -Homepage: https://www.projectathena.dev +Homepage: https://vircadia.com Vcs-Git: https://github.com/kasenvr/project-athena.git Vcs-Browser: https://github.com/kasenvr/project-athena -Package: athena-server +Package: vircadia-server Architecture: any Depends: adduser, {DEPENDS} -Description: Project Athena allows creation and sharing of VR experiences. - The Project Athena metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. +Description: Vircadia allows creation and sharing of VR experiences. + The Vircadia metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. diff --git a/pkg-scripts/server-postinst b/pkg-scripts/server-postinst index fa14e8175f..cb06adbae0 100755 --- a/pkg-scripts/server-postinst +++ b/pkg-scripts/server-postinst @@ -1,5 +1,5 @@ #!/bin/sh -# postinst script for athena-server +# postinst script for vircadia-server # # see: dh_installdeb(1) @@ -17,23 +17,55 @@ set -e # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - case "$1" in configure) - ldconfig -n /opt/athena/lib - adduser --system --quiet --gecos "Project Athena" --home /var/lib/athena --group --no-create-home athena - mkdir -p /var/lib/athena - chown athena:athena /var/lib/athena - chmod 775 /var/lib/athena - if [ ! -d "/var/lib/athena/default" ]; then - /opt/athena/new-server default 40100 - systemctl enable athena-server@default.target - systemctl start athena-server@default.target + ldconfig -n /opt/vircadia/lib + adduser --system --quiet --gecos "Vircadia" --home /var/lib/vircadia --group --no-create-home vircadia + mkdir -p /var/lib/vircadia + chown vircadia:vircadia /var/lib/vircadia + chmod 775 /var/lib/vircadia + if [ ! -d "/var/lib/vircadia/default" ]; then + if [ -d "/var/lib/athena" ]; then + ATHENA_ACTIVE=`systemctl list-units \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | paste -s -d'|' \ + | head -c -1` + ATHENA_ENABLED=`systemctl list-units --state=loaded \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | xargs -I {} sh -c 'if systemctl is-enabled {} >/dev/null ; then echo {} ; fi' \ + | paste -s -d'|' \ + | head -c -1` + + # shutdown athena servers + echo -n $ATHENA_ACTIVE | xargs -d'|' systemctl stop + + # copy the server files over + cp /etc/opt/athena/* /etc/opt/vircadia + cp -R /var/lib/athena/* /var/lib/vircadia + chown -R vircadia:vircadia /var/lib/vircadia/* + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. "Vircadia - dev"' ';' + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. Vircadia' ';' + + VIRCADIA_ACTIVE=`echo -n $ATHENA_ACTIVE | sed 's/athena/vircadia/g'` + VIRCADIA_ENABLED=`echo -n $ATHENA_ENABLED | sed 's/athena/vircadia/g'` + + echo -n $ATHENA_ENABLED | xargs -d'|' systemctl disable + echo -n $VIRCADIA_ENABLED | xargs -d'|' systemctl enable + echo -n $VIRCADIA_ACTIVE | xargs -d'|' systemctl start + else + /opt/vircadia/new-server default 40100 + systemctl enable vircadia-server@default.target + systemctl start vircadia-server@default.target + fi + else + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl restart fi ;; abort-remove|abort-deconfigure) - ldconfig -n /opt/athena/lib + ldconfig -n /opt/vircadia/lib ;; abort-upgrade) diff --git a/pkg-scripts/server-postrm b/pkg-scripts/server-postrm new file mode 100755 index 0000000000..944d5e60f4 --- /dev/null +++ b/pkg-scripts/server-postrm @@ -0,0 +1,41 @@ +#!/bin/sh +# postrm script for vircadia-server +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `disappear' +# * `abort-install; +# * `abort-install' +# * `abort-upgrade' +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + rm -r /etc/opt/vircadia + rm -r /var/lib/vircadia + ;; + + remove|upgrade|failed-upgrade|disappear|abort-install|abort-upgrade) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/pkg-scripts/server-prerm b/pkg-scripts/server-prerm index cdeddd308e..de15128778 100755 --- a/pkg-scripts/server-prerm +++ b/pkg-scripts/server-prerm @@ -1,5 +1,5 @@ #!/bin/sh -# prerm script for athena-server +# prerm script for vircadia-server # # see: dh_installdeb(1) @@ -19,7 +19,11 @@ set -e case "$1" in remove) - find -P /opt/athena/lib -type l -delete + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl stop + + find -P /opt/vircadia/lib -type l -delete ;; upgrade|deconfigure) diff --git a/pkg-scripts/server-rules b/pkg-scripts/server-rules index 4585591b15..24f557159b 100755 --- a/pkg-scripts/server-rules +++ b/pkg-scripts/server-rules @@ -18,26 +18,26 @@ dh $@ --with=systemd override_dh_systemd_enable: - dh_systemd_enable --no-enable --name athena-assignment-client athena-assignment-client.service - dh_systemd_enable --no-enable --name athena-assignment-client@ athena-assignment-client@.service - dh_systemd_enable --no-enable --name athena-domain-server athena-domain-server.service - dh_systemd_enable --no-enable --name athena-domain-server@ athena-domain-server@.service - #dh_systemd_enable --no-enable --name athena-ice-server athena-ice-server.service - #dh_systemd_enable --no-enable --name athena-ice-server@ athena-ice-server@.service - dh_systemd_enable --no-enable --name athena-server athena-server.target - dh_systemd_enable --no-enable --name athena-server@ athena-server@.target - #dh_systemd_enable --name athena-server@default athena-server@default.target + dh_systemd_enable --no-enable --name vircadia-assignment-client vircadia-assignment-client.service + dh_systemd_enable --no-enable --name vircadia-assignment-client@ vircadia-assignment-client@.service + dh_systemd_enable --no-enable --name vircadia-domain-server vircadia-domain-server.service + dh_systemd_enable --no-enable --name vircadia-domain-server@ vircadia-domain-server@.service + #dh_systemd_enable --no-enable --name vircadia-ice-server vircadia-ice-server.service + #dh_systemd_enable --no-enable --name vircadia-ice-server@ vircadia-ice-server@.service + dh_systemd_enable --no-enable --name vircadia-server vircadia-server.target + dh_systemd_enable --no-enable --name vircadia-server@ vircadia-server@.target + #dh_systemd_enable --name vircadia-server@default vircadia-server@default.target override_dh_systemd_start: - dh_systemd_start --restart-after-upgrade --no-start athena-assignment-client.service - dh_systemd_start --restart-after-upgrade --no-start athena-assignment-client@.service - dh_systemd_start --restart-after-upgrade --no-start athena-domain-server.service - dh_systemd_start --restart-after-upgrade --no-start athena-domain-server@.service - #dh_systemd_start --restart-after-upgrade --no-start athena-ice-server.service - #dh_systemd_start --restart-after-upgrade --no-start athena-ice-server@.service - dh_systemd_start --restart-after-upgrade --no-start athena-server.target - dh_systemd_start --restart-after-upgrade --no-start athena-server@.target - #dh_systemd_start --restart-after-upgrade athena-server@default.target + dh_systemd_start --restart-after-upgrade --no-start vircadia-assignment-client.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-assignment-client@.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-domain-server.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-domain-server@.service + #dh_systemd_start --restart-after-upgrade --no-start vircadia-ice-server.service + #dh_systemd_start --restart-after-upgrade --no-start vircadia-ice-server@.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-server.target + dh_systemd_start --restart-after-upgrade --no-start vircadia-server@.target + #dh_systemd_start --restart-after-upgrade vircadia-server@default.target override_dh_installinit: dh_installinit --noscripts diff --git a/pkg-scripts/vircadia-assignment-client.service b/pkg-scripts/vircadia-assignment-client.service new file mode 100644 index 0000000000..9c86e4e874 --- /dev/null +++ b/pkg-scripts/vircadia-assignment-client.service @@ -0,0 +1,18 @@ +[Unit] +Description=Assignment client service for Vircadia server +After=network.target +PartOf=vircadia-server.target + +[Service] +Restart=always + +WorkingDirectory=/opt/vircadia +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" +User=vircadia +Group=vircadia +#LimitCORE=infinity +#ExecStart=/opt/vircadia/assignment-client -n 6 +ExecStart=/opt/vircadia/assignment-client --min 6 --max 20 + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-assignment-client@.service b/pkg-scripts/vircadia-assignment-client@.service new file mode 100644 index 0000000000..4684947426 --- /dev/null +++ b/pkg-scripts/vircadia-assignment-client@.service @@ -0,0 +1,20 @@ +[Unit] +Description=Assignment client service for Vircadia server +After=network.target +PartOf=vircadia-server@%i.target + +[Service] +Restart=always + +WorkingDirectory=/opt/vircadia +EnvironmentFile=/etc/opt/vircadia/%i.conf +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" "HOME=/var/lib/vircadia/%i" +PrivateTmp=true +User=vircadia +Group=vircadia +#LimitCORE=infinity +#ExecStart=/opt/vircadia/assignment-client -n 6 +ExecStart=/opt/vircadia/assignment-client --min 6 --max 20 --server-port $HIFI_DOMAIN_SERVER_PORT + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-domain-server.service b/pkg-scripts/vircadia-domain-server.service new file mode 100644 index 0000000000..8c261bc72f --- /dev/null +++ b/pkg-scripts/vircadia-domain-server.service @@ -0,0 +1,17 @@ +[Unit] +Description=Domain Server service for Vircadia +After=network.target +PartOf=vircadia-server.target + +[Service] +Restart=on-failure + +WorkingDirectory=/opt/vircadia +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" +User=vircadia +Group=vircadia +#LimitCORE=infinity +ExecStart=/opt/vircadia/domain-server + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-domain-server@.service b/pkg-scripts/vircadia-domain-server@.service new file mode 100644 index 0000000000..7ae84dc6f3 --- /dev/null +++ b/pkg-scripts/vircadia-domain-server@.service @@ -0,0 +1,19 @@ +[Unit] +Description=Domain Server service for Vircadia +After=network.target +PartOf=vircadia-server@%i.target + +[Service] +Restart=on-failure + +WorkingDirectory=/opt/vircadia +EnvironmentFile=/etc/opt/vircadia/%i.conf +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" "HOME=/var/lib/vircadia/%i" +PrivateTmp=true +User=vircadia +Group=vircadia +#LimitCORE=infinity +ExecStart=/opt/vircadia/domain-server + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/athena-ice-server.service b/pkg-scripts/vircadia-ice-server.service similarity index 63% rename from pkg-scripts/athena-ice-server.service rename to pkg-scripts/vircadia-ice-server.service index 57db8581b3..d966ffe0c0 100644 --- a/pkg-scripts/athena-ice-server.service +++ b/pkg-scripts/vircadia-ice-server.service @@ -1,18 +1,18 @@ [Unit] -Description=Ice Server service for Athena +Description=Ice Server service for Vircadia After=network.target -PartOf=athena-server.target +PartOf=vircadia-server.target [Service] Restart=on-failure -Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/athena/lib" -WorkingDirectory=/opt/athena -User=athena -Group=athena +Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/vircadia/lib" +WorkingDirectory=/opt/vircadia +User=vircadia +Group=vircadia #ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l ice-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/ice-server/ice-server; fi' #LimitCORE=infinity -ExecStart=/opt/athena/ice-server +ExecStart=/opt/vircadia/ice-server [Install] WantedBy=multi-user.target diff --git a/pkg-scripts/athena-ice-server@.service b/pkg-scripts/vircadia-ice-server@.service similarity index 56% rename from pkg-scripts/athena-ice-server@.service rename to pkg-scripts/vircadia-ice-server@.service index e4993f5436..fc5ab4615d 100644 --- a/pkg-scripts/athena-ice-server@.service +++ b/pkg-scripts/vircadia-ice-server@.service @@ -1,20 +1,20 @@ [Unit] -Description=Ice Server service for Athena +Description=Ice Server service for Vircadia After=network.target -PartOf=athena-server@%i.target +PartOf=vircadia-server@%i.target [Service] Restart=on-failure -EnvironmentFile=/etc/opt/athena/%i.conf -Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/athena/lib" "HOME=/var/lib/athena/%i" +EnvironmentFile=/etc/opt/vircadia/%i.conf +Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/vircadia/lib" "HOME=/var/lib/vircadia/%i" PrivateTmp=true -WorkingDirectory=/opt/athena -User=athena -Group=athena +WorkingDirectory=/opt/vircadia +User=vircadia +Group=vircadia #ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l ice-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/ice-server/ice-server; fi' #LimitCORE=infinity -ExecStart=/opt/athena/ice-server +ExecStart=/opt/vircadia/ice-server [Install] WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-server.spec b/pkg-scripts/vircadia-server.spec new file mode 100644 index 0000000000..3e0eed896e --- /dev/null +++ b/pkg-scripts/vircadia-server.spec @@ -0,0 +1,168 @@ +#VIRCADIA=~/Vircadia rpmbuild --target x86_64 -bb vircadia-server.spec +%define version %{lua:print(os.getenv("VERSION"))} +%define depends %{lua:print(os.getenv("DEPENDS"))} + +Name: vircadia-server +Version: %{version} +Release: 1%{?dist} +Summary: Vircadia metaverse platform, based on the High Fidelity Engine. + +License: ASL 2.0 +URL: https://vircadia.com +Source0: https://github.com/kasenvr/vircadia-builder/blob/master/vircadia-builder + +#BuildRequires: systemd-rpm-macros +BuildRequires: chrpath +Requires: %{depends} +BuildArch: x86_64 +AutoReq: no +AutoProv: no + +%description +Vircadia allows creation and sharing of VR experiences. + The Vircadia metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. + + +%prep + + +%build + + +%install +rm -rf $RPM_BUILD_ROOT +install -d $RPM_BUILD_ROOT/opt/vircadia +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/assignment-client/assignment-client +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/domain-server/domain-server +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/tools/oven/oven +#install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/ice-server/ice-server +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/* +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/* +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/source/pkg-scripts/new-server +install -d $RPM_BUILD_ROOT/opt/vircadia/lib +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/build/libraries/*/*.so +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/lib/* +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/lib/* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Quick.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* +install -d $RPM_BUILD_ROOT/usr/lib/systemd/system +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-assignment-client.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-assignment-client@.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-domain-server.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-domain-server@.service +#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-ice-server.service +#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-ice-server@.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-server.target +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-server@.target +cp -a $VIRCADIA/source/domain-server/resources $RPM_BUILD_ROOT/opt/vircadia +cp -a $VIRCADIA/build/assignment-client/plugins $RPM_BUILD_ROOT/opt/vircadia +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/plugins/*.so +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/plugins/*/*.so +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/plugins/*.so +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/plugins/*/*.so +find $RPM_BUILD_ROOT/opt/vircadia/resources -name ".gitignore" -delete + + +%files +%license $VIRCADIA/source/LICENSE +/opt/vircadia +/usr/lib/systemd/system + + +%changelog + + +%post +# create users +getent passwd vircadia >/dev/null 2>&1 || useradd -r -c "Vircadia" -d /var/lib/vircadia -U -M vircadia +#getent group vircadia >/dev/null 2>&1 || groupadd -r vircadia + +# create data folder +mkdir -p /etc/opt/vircadia +mkdir -p /var/lib/vircadia && chown vircadia:vircadia /var/lib/vircadia && chmod 775 /var/lib/vircadia + +ldconfig -n /opt/vircadia/lib + +%systemd_post vircadia-assignment-client.service +%systemd_post vircadia-assignment-client@.service +%systemd_post vircadia-domain-server.service +%systemd_post vircadia-domain-server@.service +#%systemd_post vircadia-ice-server.service +#%systemd_post vircadia-ice-server@.service +%systemd_post vircadia-server.target +%systemd_post vircadia-server@.target + +if [ ! -d "/var/lib/vircadia/default" ]; then + if [ -d "/var/lib/athena" ]; then + ATHENA_ACTIVE=`systemctl list-units \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | paste -s -d'|' \ + | head -c -1` + ATHENA_ENABLED=`systemctl list-units --state=loaded \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | xargs -I {} sh -c 'if systemctl is-enabled {} >/dev/null ; then echo {} ; fi' \ + | paste -s -d'|' \ + | head -c -1` + + # shutdown athena servers + echo -n $ATHENA_ACTIVE | xargs -d'|' systemctl stop + + # copy the server files over + cp /etc/opt/athena/* /etc/opt/vircadia + cp -R /var/lib/athena/* /var/lib/vircadia + chown -R vircadia:vircadia /var/lib/vircadia/* + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. "Vircadia - dev"' ';' + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. Vircadia' ';' + + VIRCADIA_ACTIVE=`echo -n $ATHENA_ACTIVE | sed 's/athena/vircadia/g'` + VIRCADIA_ENABLED=`echo -n $ATHENA_ENABLED | sed 's/athena/vircadia/g'` + + echo -n $ATHENA_ENABLED | xargs -d'|' systemctl disable + echo -n $VIRCADIA_ENABLED | xargs -d'|' systemctl enable + echo -n $VIRCADIA_ACTIVE | xargs -d'|' systemctl start + else + /opt/vircadia/new-server default 40100 + systemctl enable vircadia-server@default.target + systemctl start vircadia-server@default.target + fi +else + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl restart +fi + + +%preun + +if [ "$1" -eq 0 ]; then + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl stop +fi + +%systemd_preun vircadia-server.target +%systemd_preun vircadia-server@.target +%systemd_preun vircadia-assignment-client.service +%systemd_preun vircadia-assignment-client@.service +%systemd_preun vircadia-domain-server.service +%systemd_preun vircadia-domain-server@.service +#%systemd_preun vircadia-ice-server.service +#%systemd_preun vircadia-ice-server@.service + + +%postun +%systemd_postun_with_restart vircadia-server.target +%systemd_postun_with_restart vircadia-server@.target +%systemd_postun_with_restart vircadia-assignment-client.service +%systemd_postun_with_restart vircadia-assignment-client@.service +%systemd_postun_with_restart vircadia-domain-server.service +%systemd_postun_with_restart vircadia-domain-server@.service +#%systemd_postun_with_restart vircadia-ice-server.service +#%systemd_postun_with_restart vircadia-ice-server@.service diff --git a/pkg-scripts/vircadia-server.target b/pkg-scripts/vircadia-server.target new file mode 100644 index 0000000000..27225aade3 --- /dev/null +++ b/pkg-scripts/vircadia-server.target @@ -0,0 +1,11 @@ +[Unit] +Description=Vircadia virtual domain +Wants=vircadia-assignment-client.service +Wants=vircadia-domain-server.service +#Wants=vircadia-ice-server.service +After=vircadia-assignment-client.service +After=vircadia-domain-server.service +#After=vircadia-ice-server.service + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-server@.target b/pkg-scripts/vircadia-server@.target new file mode 100644 index 0000000000..158a48bedc --- /dev/null +++ b/pkg-scripts/vircadia-server@.target @@ -0,0 +1,11 @@ +[Unit] +Description=Vircadia virtual domain +Wants=vircadia-assignment-client@%i.service +Wants=vircadia-domain-server@%i.service +#Wants=vircadia-ice-server@%i.service +After=vircadia-assignment-client@%i.service +After=vircadia-domain-server@%i.service +#After=vircadia-ice-server@%i.service + +[Install] +WantedBy=multi-user.target diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index c88bb8a00d..1448e14c72 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -36,6 +36,12 @@ if (NOT SERVER_ONLY AND NOT ANDROID) add_subdirectory(${DIR}) set(DIR "hifiLeapMotion") add_subdirectory(${DIR}) + + if (WIN32) + set(DIR "hifiOsc") + add_subdirectory(${DIR}) + endif() + endif() # server-side plugins diff --git a/plugins/KasenAPIExample/src/KasenAPIExample.cpp b/plugins/KasenAPIExample/src/KasenAPIExample.cpp index d566d11376..9f81793037 100644 --- a/plugins/KasenAPIExample/src/KasenAPIExample.cpp +++ b/plugins/KasenAPIExample/src/KasenAPIExample.cpp @@ -113,7 +113,7 @@ private: } struct _HeadHelper : public HeadData { QMap getBlendshapeMap() const { - return _blendshapeLookupMap; + return BLENDSHAPE_LOOKUP_MAP; } struct States { QVector base, summed, transient; }; States getBlendshapeStates() const { diff --git a/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp b/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp index 5c5b975676..0989d28d23 100644 --- a/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp +++ b/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp @@ -281,6 +281,7 @@ void LeapMotionPlugin::InputDevice::update(float deltaTime, const controller::In glm::vec3 pos; glm::quat rot; + glm::quat prevRot; if (_isLeapOnHMD) { auto jointPosition = joints[i].position; const glm::vec3 HMD_EYE_TO_LEAP_OFFSET = glm::vec3(0.0f, 0.0f, -0.09f); // Eyes to surface of Leap Motion. @@ -291,17 +292,24 @@ void LeapMotionPlugin::InputDevice::update(float deltaTime, const controller::In glm::quat jointOrientation = joints[i].orientation; jointOrientation = glm::quat(jointOrientation.w, -jointOrientation.x, -jointOrientation.z, -jointOrientation.y); rot = controllerToAvatarRotation * hmdSensorOrientation * jointOrientation; + + glm::quat prevJointOrientation = prevJoints[i].orientation; + prevJointOrientation = + glm::quat(prevJointOrientation.w, -prevJointOrientation.x, -prevJointOrientation.z, -prevJointOrientation.y); + prevRot = controllerToAvatarRotation * hmdSensorOrientation * prevJointOrientation; + } else { pos = controllerToAvatarRotation * (joints[i].position - leapMotionOffset); const glm::quat ZERO_HAND_ORIENTATION = glm::quat(glm::vec3(PI_OVER_TWO, PI, 0.0f)); rot = controllerToAvatarRotation * joints[i].orientation * ZERO_HAND_ORIENTATION; + prevRot = controllerToAvatarRotation * prevJoints[i].orientation * ZERO_HAND_ORIENTATION; } glm::vec3 linearVelocity, angularVelocity; if (i < prevJoints.size()) { linearVelocity = (pos - (prevJoints[i].position * METERS_PER_CENTIMETER)) / deltaTime; // m/s // quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation. - glm::quat d = glm::log(rot * glm::inverse(prevJoints[i].orientation)); + glm::quat d = glm::log(rot * glm::inverse(prevRot)); angularVelocity = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s } diff --git a/plugins/hifiNeuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt index 7d2616e4d6..ad4f78698c 100644 --- a/plugins/hifiNeuron/CMakeLists.txt +++ b/plugins/hifiNeuron/CMakeLists.txt @@ -6,7 +6,7 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (APPLE OR WIN32) +if (WIN32) set(TARGET_NAME hifiNeuron) setup_hifi_plugin(Qml) diff --git a/plugins/hifiOsc/CMakeLists.txt b/plugins/hifiOsc/CMakeLists.txt new file mode 100644 index 0000000000..cb8b437ab6 --- /dev/null +++ b/plugins/hifiOsc/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Created by Anthony Thibault on 2019/8/24 +# Copyright 2019 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html +# + +set(TARGET_NAME hifiOsc) +setup_hifi_plugin(Qml) +link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins) +target_liblo() + + diff --git a/plugins/hifiOsc/src/OscPlugin.cpp b/plugins/hifiOsc/src/OscPlugin.cpp new file mode 100644 index 0000000000..788342971d --- /dev/null +++ b/plugins/hifiOsc/src/OscPlugin.cpp @@ -0,0 +1,636 @@ +// +// OscPlugin.cpp +// +// Created by Anthony Thibault on 2019/8/24 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "OscPlugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + +const char* OscPlugin::NAME = "Open Sound Control (OSC)"; +const char* OscPlugin::OSC_ID_STRING = "Open Sound Control (OSC)"; +const bool DEFAULT_ENABLED = false; + +enum class FaceCap { + BrowsU_C = 0, + BrowsD_L, + BrowsD_R, + BrowsU_L, + BrowsU_R, + EyeUp_L, + EyeUp_R, + EyeDown_L, + EyeDown_R, + EyeIn_L, + EyeIn_R, + EyeOut_L, + EyeOut_R, + EyeBlink_L, + EyeBlink_R, + EyeSquint_L, + EyeSquint_R, + EyeOpen_L, + EyeOpen_R, + Puff, + CheekSquint_L, + CheekSquint_R, + NoseSneer_L, + NoseSneer_R, + JawOpen, + JawFwd, + JawLeft, + JawRight, + LipsFunnel, + LipsPucker, + MouthLeft, + MouthRight, + LipsUpperClose, + LipsLowerClose, + MouthShrugUpper, + MouthShrugLower, + MouthClose, + MouthSmile_L, + MouthSmile_R, + MouthFrown_L, + MouthFrown_R, + MouthDimple_L, + MouthDimple_R, + MouthUpperUp_L, + MouthUpperUp_R, + MouthLowerDown_L, + MouthLowerDown_R, + MouthPress_L, + MouthPress_R, + LipsStretch_L, + LipsStretch_R, + TongueOut, + BlendshapeCount +}; + +// used to mirror left/right shapes from FaceCap. +// i.e. right and left shapes are swapped. +FaceCap faceMirrorMap[(int)FaceCap::BlendshapeCount] = { + FaceCap::BrowsU_C, + FaceCap::BrowsD_R, + FaceCap::BrowsD_L, + FaceCap::BrowsU_R, + FaceCap::BrowsU_L, + FaceCap::EyeUp_R, + FaceCap::EyeUp_L, + FaceCap::EyeDown_R, + FaceCap::EyeDown_L, + FaceCap::EyeIn_R, + FaceCap::EyeIn_L, + FaceCap::EyeOut_R, + FaceCap::EyeOut_L, + FaceCap::EyeBlink_R, + FaceCap::EyeBlink_L, + FaceCap::EyeSquint_R, + FaceCap::EyeSquint_L, + FaceCap::EyeOpen_R, + FaceCap::EyeOpen_L, + FaceCap::Puff, + FaceCap::CheekSquint_R, + FaceCap::CheekSquint_L, + FaceCap::NoseSneer_R, + FaceCap::NoseSneer_L, + FaceCap::JawOpen, + FaceCap::JawFwd, + FaceCap::JawRight, + FaceCap::JawLeft, + FaceCap::LipsFunnel, + FaceCap::LipsPucker, + FaceCap::MouthRight, + FaceCap::MouthLeft, + FaceCap::LipsUpperClose, + FaceCap::LipsLowerClose, + FaceCap::MouthShrugUpper, + FaceCap::MouthShrugLower, + FaceCap::MouthClose, + FaceCap::MouthSmile_R, + FaceCap::MouthSmile_L, + FaceCap::MouthFrown_R, + FaceCap::MouthFrown_L, + FaceCap::MouthDimple_R, + FaceCap::MouthDimple_L, + FaceCap::MouthUpperUp_R, + FaceCap::MouthUpperUp_L, + FaceCap::MouthLowerDown_R, + FaceCap::MouthLowerDown_L, + FaceCap::MouthPress_R, + FaceCap::MouthPress_L, + FaceCap::LipsStretch_R, + FaceCap::LipsStretch_L, + FaceCap::TongueOut +}; + +static const char* STRINGS[FaceCap::BlendshapeCount] = { + "BrowsU_C", + "BrowsD_L", + "BrowsD_R", + "BrowsU_L", + "BrowsU_R", + "EyeUp_L", + "EyeUp_R", + "EyeDown_L", + "EyeDown_R", + "EyeIn_L", + "EyeIn_R", + "EyeOut_L", + "EyeOut_R", + "EyeBlink_L", + "EyeBlink_R", + "EyeSquint_L", + "EyeSquint_R", + "EyeOpen_L", + "EyeOpen_R", + "Puff", + "CheekSquint_L", + "CheekSquint_R", + "NoseSneer_L", + "NoseSneer_R", + "JawOpen", + "JawFwd", + "JawLeft", + "JawRight", + "LipsFunnel", + "LipsPucker", + "MouthLeft", + "MouthRight", + "LipsUpperClose", + "LipsLowerClose", + "MouthShrugUpper", + "MouthShrugLower", + "MouthClose", + "MouthSmile_L", + "MouthSmile_R", + "MouthFrown_L", + "MouthFrown_R", + "MouthDimple_L", + "MouthDimple_R", + "MouthUpperUp_L", + "MouthUpperUp_R", + "MouthLowerDown_L", + "MouthLowerDown_R", + "MouthPress_L", + "MouthPress_R", + "LipsStretch_L", + "LipsStretch_R", + "TongueOut" +}; + +static enum controller::StandardAxisChannel CHANNELS[FaceCap::BlendshapeCount] = { + controller::BROWSU_C, + controller::BROWSD_L, + controller::BROWSD_R, + controller::BROWSU_L, + controller::BROWSU_R, + controller::EYEUP_L, + controller::EYEUP_R, + controller::EYEDOWN_L, + controller::EYEDOWN_R, + controller::EYEIN_L, + controller::EYEIN_R, + controller::EYEOUT_L, + controller::EYEOUT_R, + controller::EYEBLINK_L, + controller::EYEBLINK_R, + controller::EYESQUINT_L, + controller::EYESQUINT_R, + controller::EYEOPEN_L, + controller::EYEOPEN_R, + controller::PUFF, + controller::CHEEKSQUINT_L, + controller::CHEEKSQUINT_R, + controller::NOSESNEER_L, + controller::NOSESNEER_R, + controller::JAWOPEN, + controller::JAWFWD, + controller::JAWLEFT, + controller::JAWRIGHT, + controller::LIPSFUNNEL, + controller::LIPSPUCKER, + controller::MOUTHLEFT, + controller::MOUTHRIGHT, + controller::LIPSUPPERCLOSE, + controller::LIPSLOWERCLOSE, + controller::MOUTHSHRUGUPPER, + controller::MOUTHSHRUGLOWER, + controller::MOUTHCLOSE, + controller::MOUTHSMILE_L, + controller::MOUTHSMILE_R, + controller::MOUTHFROWN_L, + controller::MOUTHFROWN_R, + controller::MOUTHDIMPLE_L, + controller::MOUTHDIMPLE_R, + controller::MOUTHUPPERUP_L, + controller::MOUTHUPPERUP_R, + controller::MOUTHLOWERDOWN_L, + controller::MOUTHLOWERDOWN_R, + controller::MOUTHPRESS_L, + controller::MOUTHPRESS_R, + controller::LIPSSTRETCH_L, + controller::LIPSSTRETCH_R, + controller::TONGUEOUT +}; + + +void OscPlugin::init() { + + _inputDevice = std::make_shared(); + _inputDevice->setContainer(this); + + { + std::lock_guard guard(_dataMutex); + _blendshapeValues.assign((int)FaceCap::BlendshapeCount, 0.0f); + _blendshapeValidFlags.assign((int)FaceCap::BlendshapeCount, false); + _headRot = glm::quat(); + _headRotValid = false; + _headTransTarget = extractTranslation(_lastInputCalibrationData.defaultHeadMat); + _headTransSmoothed = extractTranslation(_lastInputCalibrationData.defaultHeadMat); + _headTransValid = false; + _eyeLeftRot = glm::quat(); + _eyeLeftRotValid = false; + _eyeRightRot = glm::quat(); + _eyeRightRotValid = false; + } + + loadSettings(); + + auto preferences = DependencyManager::get(); + static const QString OSC_PLUGIN { OscPlugin::NAME }; + { + auto getter = [this]()->bool { return _enabled; }; + auto setter = [this](bool value) { + _enabled = value; + saveSettings(); + if (!_enabled) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->clearState(); + restartServer(); + }); + } + }; + auto preference = new CheckPreference(OSC_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } + { + auto debugGetter = [this]()->bool { return _debug; }; + auto debugSetter = [this](bool value) { + _debug = value; + saveSettings(); + }; + auto preference = new CheckPreference(OSC_PLUGIN, "Extra Debugging", debugGetter, debugSetter); + preferences->addPreference(preference); + } + + { + static const int MIN_PORT_NUMBER { 0 }; + static const int MAX_PORT_NUMBER { 65535 }; + + auto getter = [this]()->float { return (float)_serverPort; }; + auto setter = [this](float value) { + _serverPort = (int)value; + saveSettings(); + restartServer(); + }; + auto preference = new SpinnerPreference(OSC_PLUGIN, "Server Port", getter, setter); + + preference->setMin(MIN_PORT_NUMBER); + preference->setMax(MAX_PORT_NUMBER); + preference->setStep(1); + preferences->addPreference(preference); + } +} + +bool OscPlugin::isSupported() const { + // networking/UDP is pretty much always available... + return true; +} + +static void errorHandlerFunc(int num, const char* msg, const char* path) { + qDebug(inputplugins) << "OscPlugin: server error" << num << "in path" << path << ":" << msg; +} + +static int genericHandlerFunc(const char* path, const char* types, lo_arg** argv, + int argc, void* data, void* user_data) { + + OscPlugin* container = reinterpret_cast(user_data); + assert(container); + + QString key(path); + std::lock_guard guard(container->_dataMutex); + + // Special case: decode blendshapes from face-cap iPhone app. + // http://www.bannaflak.com/face-cap/livemode.html + if (path[0] == '/' && path[1] == 'W' && argc == 2 && types[0] == 'i' && types[1] == 'f') { + int index = argv[0]->i; + if (index >= 0 && index < (int)FaceCap::BlendshapeCount) { + int mirroredIndex = (int)faceMirrorMap[index]; + container->_blendshapeValues[mirroredIndex] = argv[1]->f; + container->_blendshapeValidFlags[mirroredIndex] = true; + } + } + + // map /HT to head translation + if (path[0] == '/' && path[1] == 'H' && path[2] == 'T' && + types[0] == 'f' && types[1] == 'f' && types[2] == 'f') { + glm::vec3 trans(-argv[0]->f, -argv[1]->f, argv[2]->f); // in cm + + // convert trans into a delta (in meters) from the sweet spot of the iphone camera. + const float CM_TO_METERS = 0.01f; + const glm::vec3 FACE_CAP_HEAD_SWEET_SPOT(0.0f, 0.0f, -45.0f); + glm::vec3 delta = (trans - FACE_CAP_HEAD_SWEET_SPOT) * CM_TO_METERS; + + container->_headTransTarget = extractTranslation(container->_lastInputCalibrationData.defaultHeadMat) + delta; + container->_headTransValid = true; + } + + // map /HR to head rotation + if (path[0] == '/' && path[1] == 'H' && path[2] == 'R' && path[3] == 0 && + types[0] == 'f' && types[1] == 'f' && types[2] == 'f') { + glm::vec3 euler(-argv[0]->f, -argv[1]->f, argv[2]->f); + container->_headRot = glm::quat(glm::radians(euler)) * Quaternions::Y_180; + container->_headRotValid = true; + } + + // map /ELR to left eye rot + if (path[0] == '/' && path[1] == 'E' && path[2] == 'L' && path[3] == 'R' && + types[0] == 'f' && types[1] == 'f') { + glm::vec3 euler(argv[0]->f, -argv[1]->f, 0.0f); + container->_eyeLeftRot = container->_headRot * glm::quat(glm::radians(euler)); + container->_eyeLeftRotValid = true; + } + + // map /ERR to right eye rot + if (path[0] == '/' && path[1] == 'E' && path[2] == 'R' && path[3] == 'R' && + types[0] == 'f' && types[1] == 'f') { + glm::vec3 euler((float)argv[0]->f, (float)-argv[1]->f, 0.0f); + container->_eyeRightRot = container->_headRot * glm::quat(glm::radians(euler)); + container->_eyeRightRotValid = true; + } + + // AJT: TODO map /STRINGS[i] to _blendshapeValues[i] + + if (container->_debug) { + for (int i = 0; i < argc; i++) { + switch (types[i]) { + case 'i': + // int32 + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] =" << argv[i]->i; + break; + case 'f': + // float32 + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] =" << argv[i]->f32; + break; + case 's': + // OSC-string + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'b': + // OSC-blob + break; + case 'h': + // 64 bit big-endian two's complement integer + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] =" << argv[i]->h; + break; + case 't': + // OSC-timetag + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'd': + // 64 bit ("double") IEEE 754 floating point number + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] =" << argv[i]->d; + break; + case 'S': + // Alternate type represented as an OSC-string (for example, for systems that differentiate "symbols" from "strings") + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'c': + // an ascii character, sent as 32 bits + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] =" << argv[i]->c; + break; + case 'r': + // 32 bit RGBA color + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'm': + // 4 byte MIDI message. Bytes from MSB to LSB are: port id, status byte, data1, data2 + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'T': + // true + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'F': + // false + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'N': + // nil + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case 'I': + // inf + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case '[': + // Indicates the beginning of an array. The tags following are for data in the Array until a close brace tag is reached. + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + case ']': + // Indicates the end of an array. + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = "; + break; + default: + qDebug(inputplugins) << "OscPlugin: " << path << "[" << i << "] = " << types[i]; + break; + } + } + } + + return 1; +} + + +bool OscPlugin::activate() { + InputPlugin::activate(); + + loadSettings(); + + if (_enabled) { + + qDebug(inputplugins) << "OscPlugin: activated"; + + // register with userInputMapper + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(_inputDevice); + + return startServer(); + } + return false; +} + +void OscPlugin::deactivate() { + qDebug(inputplugins) << "OscPlugin: deactivated, _oscServerThread =" << _oscServerThread; + + if (_oscServerThread) { + stopServer(); + } +} + +void OscPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + if (!_enabled) { + return; + } + + _lastInputCalibrationData = inputCalibrationData; + + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->update(deltaTime, inputCalibrationData); + }); +} + +void OscPlugin::saveSettings() const { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + settings.setValue(QString("enabled"), _enabled); + settings.setValue(QString("extraDebug"), _debug); + settings.setValue(QString("serverPort"), _serverPort); + } + settings.endGroup(); +} + +void OscPlugin::loadSettings() { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + _enabled = settings.value("enabled", QVariant(DEFAULT_ENABLED)).toBool(); + _debug = settings.value("extraDebug", QVariant(DEFAULT_ENABLED)).toBool(); + _serverPort = settings.value("serverPort", QVariant(DEFAULT_OSC_SERVER_PORT)).toInt(); + } + settings.endGroup(); +} + +bool OscPlugin::startServer() { + if (_oscServerThread) { + qWarning(inputplugins) << "OscPlugin: (startServer) server is already running, _oscServerThread =" << _oscServerThread; + return true; + } + + // start a new server on specified port + const size_t BUFFER_SIZE = 64; + char serverPortString[BUFFER_SIZE]; + snprintf(serverPortString, BUFFER_SIZE, "%d", _serverPort); + _oscServerThread = lo_server_thread_new(serverPortString, errorHandlerFunc); + + qDebug(inputplugins) << "OscPlugin: server started on port" << serverPortString << ", _oscServerThread =" << _oscServerThread; + + // add method that will match any path and args + // NOTE: callback function will be called on the OSC thread, not the appliation thread. + lo_server_thread_add_method(_oscServerThread, NULL, NULL, genericHandlerFunc, (void*)this); + + lo_server_thread_start(_oscServerThread); + + return true; +} + +void OscPlugin::stopServer() { + if (!_oscServerThread) { + qWarning(inputplugins) << "OscPlugin: (stopServer) server is already shutdown."; + } + + // stop and free server + lo_server_thread_stop(_oscServerThread); + lo_server_thread_free(_oscServerThread); + _oscServerThread = nullptr; +} + +void OscPlugin::restartServer() { + if (_oscServerThread) { + stopServer(); + } + startServer(); +} + +// +// InputDevice +// + +controller::Input::NamedVector OscPlugin::InputDevice::getAvailableInputs() const { + static controller::Input::NamedVector availableInputs; + if (availableInputs.size() == 0) { + for (int i = 0; i < (int)FaceCap::BlendshapeCount; i++) { + availableInputs.push_back(makePair(CHANNELS[i], STRINGS[i])); + } + } + availableInputs.push_back(makePair(controller::HEAD, "Head")); + availableInputs.push_back(makePair(controller::LEFT_EYE, "LeftEye")); + availableInputs.push_back(makePair(controller::RIGHT_EYE, "RightEye")); + return availableInputs; +} + +QString OscPlugin::InputDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/osc.json"; + return MAPPING_JSON; +} + +void OscPlugin::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; + std::lock_guard guard(_container->_dataMutex); + for (int i = 0; i < (int)FaceCap::BlendshapeCount; i++) { + if (_container->_blendshapeValidFlags[i]) { + _axisStateMap[CHANNELS[i]] = controller::AxisValue(_container->_blendshapeValues[i], 0, true); + } + } + if (_container->_headRotValid && _container->_headTransValid) { + const float SMOOTH_TIMESCALE = 2.0f; + float tau = deltaTime / SMOOTH_TIMESCALE; + _container->_headTransSmoothed = lerp(_container->_headTransSmoothed, _container->_headTransTarget, tau); + glm::vec3 delta = _container->_headTransSmoothed - _container->_headTransTarget; + glm::vec3 trans = extractTranslation(inputCalibrationData.defaultHeadMat) + delta; + + controller::Pose sensorSpacePose(trans, _container->_headRot); + _poseStateMap[controller::HEAD] = sensorSpacePose.transform(sensorToAvatarMat); + } + if (_container->_eyeLeftRotValid) { + controller::Pose sensorSpacePose(vec3(0.0f), _container->_eyeLeftRot); + _poseStateMap[controller::LEFT_EYE] = sensorSpacePose.transform(sensorToAvatarMat); + } + if (_container->_eyeRightRotValid) { + controller::Pose sensorSpacePose(vec3(0.0f), _container->_eyeRightRot); + _poseStateMap[controller::RIGHT_EYE] = sensorSpacePose.transform(sensorToAvatarMat); + } +} + +void OscPlugin::InputDevice::clearState() { + std::lock_guard guard(_container->_dataMutex); + for (int i = 0; i < (int)FaceCap::BlendshapeCount; i++) { + _axisStateMap[CHANNELS[i]] = controller::AxisValue(0.0f, 0, false); + } + _poseStateMap[controller::HEAD] = controller::Pose(); + _poseStateMap[controller::LEFT_EYE] = controller::Pose(); + _poseStateMap[controller::RIGHT_EYE] = controller::Pose(); +} + diff --git a/plugins/hifiOsc/src/OscPlugin.h b/plugins/hifiOsc/src/OscPlugin.h new file mode 100644 index 0000000000..3ce198b625 --- /dev/null +++ b/plugins/hifiOsc/src/OscPlugin.h @@ -0,0 +1,95 @@ +// +// OscPlugin.h +// +// Created by Anthony Thibault on 2019/8/24 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OscPlugin_h +#define hifi_OscPlugin_h + +#include +#include +#include + +#include "lo/lo.h" + +// OSC (Open Sound Control) input plugin. +class OscPlugin : public InputPlugin { + Q_OBJECT +public: + + // Plugin functions + virtual void init() override; + virtual bool isSupported() const override; + virtual const QString getName() const override { return NAME; } + const QString getID() const override { return OSC_ID_STRING; } + + virtual bool activate() override; + virtual void deactivate() override; + + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + + virtual void saveSettings() const override; + virtual void loadSettings() override; + + bool startServer(); + void stopServer(); + void restartServer(); + +protected: + + class InputDevice : public controller::InputDevice { + public: + friend class OscPlugin; + + InputDevice() : controller::InputDevice("OSC") {} + + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + virtual void focusOutEvent() override {}; + + void clearState(); + void setContainer(OscPlugin* container) { _container = container; } + + OscPlugin* _container { nullptr }; + }; + + std::shared_ptr _inputDevice; + + static const char* NAME; + static const char* OSC_ID_STRING; + + bool _enabled { false }; + mutable bool _initialized { false }; + + lo_server_thread _oscServerThread { nullptr }; + +public: + bool _debug { false }; + enum Constants { DEFAULT_OSC_SERVER_PORT = 7700 }; + int _serverPort { DEFAULT_OSC_SERVER_PORT }; + controller::InputCalibrationData _lastInputCalibrationData; + + std::vector _blendshapeValues; + std::vector _blendshapeValidFlags; + glm::quat _headRot; + bool _headRotValid { false }; + glm::vec3 _headTransTarget; + glm::vec3 _headTransSmoothed; + bool _headTransValid { false }; + glm::quat _eyeLeftRot; + bool _eyeLeftRotValid { false }; + glm::quat _eyeRightRot; + bool _eyeRightRotValid { false }; + std::mutex _dataMutex; +}; + +#endif // hifi_OscPlugin_h + diff --git a/plugins/hifiOsc/src/OscProvider.cpp b/plugins/hifiOsc/src/OscProvider.cpp new file mode 100644 index 0000000000..0d4c582d16 --- /dev/null +++ b/plugins/hifiOsc/src/OscProvider.cpp @@ -0,0 +1,49 @@ +// +// Created by Anthony Thibault on 2019/8/25 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include +#include +#include + +#include +#include + +#include "OscPlugin.h" + +class OscProvider : public QObject, public InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + OscProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~OscProvider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new OscPlugin()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + + virtual void destroyInputPlugins() override { + _inputPlugins.clear(); + } + +private: + InputPluginList _inputPlugins; +}; + +#include "OscProvider.moc" diff --git a/plugins/hifiOsc/src/plugin.json b/plugins/hifiOsc/src/plugin.json new file mode 100644 index 0000000000..d977e34a1c --- /dev/null +++ b/plugins/hifiOsc/src/plugin.json @@ -0,0 +1,4 @@ +{ + "name":"Osc", + "version": 1 +} diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt index 8b2bb114a0..c68723a10a 100644 --- a/plugins/hifiSdl2/CMakeLists.txt +++ b/plugins/hifiSdl2/CMakeLists.txt @@ -6,7 +6,9 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -set(TARGET_NAME hifiSdl2) -setup_hifi_plugin(Qml) -link_hifi_libraries(shared controllers ui plugins input-plugins script-engine) -target_sdl2() +if (NOT APPLE) + set(TARGET_NAME hifiSdl2) + setup_hifi_plugin(Qml) + link_hifi_libraries(shared controllers ui plugins input-plugins script-engine) + target_sdl2() +endif() \ No newline at end of file diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index 33d27c4e9d..bd3fbb439f 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -9,7 +9,7 @@ # Windows doesn't need this, and building it currently make Linux unstable. # if (NOT WIN32) -if (APPLE) +if (FALSE AND APPLE) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index dcb2e39e1b..9690ee3fb5 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -1,12 +1,13 @@ # # Created by Bradley Austin Davis on 2015/11/18 # Copyright 2015 High Fidelity, Inc. +# Copyright 2020 Vircadia contributors. # # Distributed under the Apache License, Version 2.0. # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (WIN32 AND (NOT USE_GLES)) +if ((WIN32 OR UNIX AND NOT APPLE) AND NOT USE_GLES) set(TARGET_NAME openvr) setup_hifi_plugin(Gui Qml Multimedia) link_hifi_libraries(shared task gl qml networking controllers ui @@ -15,5 +16,9 @@ if (WIN32 AND (NOT USE_GLES)) include_hifi_library_headers(octree) target_openvr() - target_link_libraries(${TARGET_NAME} Winmm.lib) + if (WIN32) + target_sranipal() + target_aristo() + target_link_libraries(${TARGET_NAME} Winmm.lib) + endif() endif() diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 78b462369b..c5eb740325 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/05/12 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -738,28 +739,36 @@ int OpenVrDisplayPlugin::getRequiredThreadCount() const { } QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const { - QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String); + QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_RecordingDeviceOverride_String); + // FIXME: Address Linux. +#ifdef Q_OS_WIN if (!device.isEmpty()) { static const WCHAR INIT = 0; size_t size = device.size() + 1; std::vector deviceW; deviceW.assign(size, INIT); device.toWCharArray(deviceW.data()); + // FIXME: This may not be necessary if vr::k_pch_audio_RecordingDeviceOverride_StringName is used above. device = AudioClient::getWinDeviceName(deviceW.data()); } +#endif return device; } QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const { - QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String); + QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_PlaybackDeviceOverride_String); + // FIXME: Address Linux. +#ifdef Q_OS_WIN if (!device.isEmpty()) { static const WCHAR INIT = 0; size_t size = device.size() + 1; std::vector deviceW; deviceW.assign(size, INIT); device.toWCharArray(deviceW.data()); + // FIXME: This may not be necessary if vr::k_pch_audio_PlaybackDeviceOverride_StringName is used above. device = AudioClient::getWinDeviceName(deviceW.data()); } +#endif return device; } diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index ce60719d67..ce7625eedb 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/11/01 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -18,8 +19,6 @@ #include #include -#include -#include #include #include #include @@ -51,7 +50,7 @@ static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; bool isOculusPresent() { bool result = false; -#if defined(Q_OS_WIN32) +#ifdef Q_OS_WIN HANDLE oculusServiceEvent = ::OpenEventW(SYNCHRONIZE, FALSE, L"OculusHMDConnected"); // The existence of the service indicates a running Oculus runtime if (oculusServiceEvent) { @@ -100,7 +99,7 @@ QString getVrSettingString(const char* section, const char* setting) { vr::IVRSettings * vrSettings = vr::VRSettings(); if (vrSettings) { vr::EVRSettingsError error = vr::VRSettingsError_None; - vrSettings->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, BUFFER, BUFFER_SIZE, &error); + vrSettings->GetString(vr::k_pch_audio_Section, setting, BUFFER, BUFFER_SIZE, &error); if (error == vr::VRSettingsError_None) { result = BUFFER; } @@ -208,8 +207,10 @@ void finishOpenVrKeyboardInput() { updateFromOpenVrKeyboardInput(); // Simulate an enter press on the top level window to trigger the action if (0 == (_currentHints & Qt::ImhMultiLine)) { - qApp->sendEvent(offscreenUi->getWindow(), &QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::KeyboardModifiers(), QString("\n"))); - qApp->sendEvent(offscreenUi->getWindow(), &QKeyEvent(QEvent::KeyRelease, Qt::Key_Return, Qt::KeyboardModifiers())); + auto keyPress = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::KeyboardModifiers(), QString("\n")); + auto keyRelease = QKeyEvent(QEvent::KeyRelease, Qt::Key_Return, Qt::KeyboardModifiers()); + qApp->sendEvent(offscreenUi->getWindow(), &keyPress); + qApp->sendEvent(offscreenUi->getWindow(), &keyRelease); } } @@ -293,11 +294,20 @@ void handleOpenVrEvents() { ulong promitySensorFlag = (1UL << ((int)vr::k_EButton_ProximitySensor)); _headInHeadset = (controllerState.ulButtonPressed & promitySensorFlag) == promitySensorFlag; } - } #if DEV_BUILD - qDebug() << "OpenVR: Event " << activeHmd->GetEventTypeNameFromEnum((vr::EVREventType)event.eventType) << "(" << event.eventType << ")"; + //qDebug() << "OpenVR: Event " << activeHmd->GetEventTypeNameFromEnum((vr::EVREventType)event.eventType) << "(" << event.eventType << ")"; + // FIXME: Reinstate the line above and remove the following lines once the problem with excessive occurrences of + // VREvent_ActionBindingReloaded events is fixed in SteamVR for Linux. + // https://github.com/ValveSoftware/SteamVR-for-Linux/issues/307 + #ifdef Q_OS_LINUX + if (event.eventType != vr::VREvent_ActionBindingReloaded) { + qDebug() << "OpenVR: Event " << activeHmd->GetEventTypeNameFromEnum((vr::EVREventType)event.eventType) << "(" << event.eventType << ")"; + }; + #else + qDebug() << "OpenVR: Event " << activeHmd->GetEventTypeNameFromEnum((vr::EVREventType)event.eventType) << "(" << event.eventType << ")"; + #endif #endif } @@ -407,13 +417,35 @@ void showMinSpecWarning() { qFatal("Unable to create overlay"); } - // Needed here for PathUtils +#ifdef Q_OS_LINUX + QFile cmdlineFile("/proc/self/cmdline"); + if (!cmdlineFile.open(QIODevice::ReadOnly)) { + qFatal("Unable to open /proc/self/cmdline"); + } + + auto contents = cmdlineFile.readAll(); + auto arguments = contents.split('\0'); + arguments.pop_back(); // Last element is empty. + cmdlineFile.close(); + + int __argc = arguments.count(); + char** __argv = new char* [__argc]; + for (int i = 0; i < __argc; i++) { + __argv[i] = arguments[i].data(); + } +#endif + QCoreApplication miniApp(__argc, __argv); +#ifdef Q_OS_LINUX + QObject::connect(&miniApp, &QCoreApplication::destroyed, [=] { + delete[] __argv; + }); +#endif + vrSystem->ResetSeatedZeroPose(); QString imagePath = PathUtils::resourcesPath() + "/images/steam-min-spec-failed.png"; vrOverlay->SetOverlayFromFile(minSpecFailedOverlay, imagePath.toLocal8Bit().toStdString().c_str()); - vrOverlay->SetHighQualityOverlay(minSpecFailedOverlay); vrOverlay->SetOverlayWidthInMeters(minSpecFailedOverlay, 1.4f); vrOverlay->SetOverlayInputMethod(minSpecFailedOverlay, vr::VROverlayInputMethod_Mouse); vrOverlay->ShowOverlay(minSpecFailedOverlay); @@ -488,7 +520,12 @@ bool checkMinSpecImpl() { } extern "C" { +#if defined(Q_OS_WIN32) __declspec(dllexport) int __stdcall CheckMinSpec() { +#else + __attribute__((visibility("default"))) int CheckMinSpec() { +#endif return checkMinSpecImpl() ? 1 : 0; } + } diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index b0960a03eb..23bd9f39de 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/06/12 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -76,7 +77,7 @@ struct PoseData { } void update(const glm::mat4& resetMat) { - for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { + for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { if (!vrPoses[i].bPoseIsValid) { continue; } @@ -87,7 +88,7 @@ struct PoseData { } void resetToInvalid() { - for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { + for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { vrPoses[i].bPoseIsValid = false; } } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 8aa7311de4..3629698e11 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -1,9 +1,9 @@ // // ViveControllerManager.cpp -// input-plugins/src/input-plugins // // Created by Sam Gondelman on 6/29/15. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -13,6 +13,23 @@ #include #include +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4091 ) +#pragma warning( disable : 4334 ) +#endif + +#ifdef VIVE_PRO_EYE +#include +#include +#include +#include +#endif + +#ifdef _WIN32 +#pragma warning( pop ) +#endif + #include #include #include @@ -34,22 +51,19 @@ #include #include -#include +#include #include +#include "OpenVrDisplayPlugin.h" + extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); static const QString OPENVR_LAYOUT = QString("OpenVrConfiguration.qml"); -static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND; -static const char* MENU_PARENT = "Avatar"; -static const char* MENU_NAME = "Vive Controllers"; -static const char* MENU_PATH = "Avatar" ">" "Vive Controllers"; - static const int MIN_HEAD = 1; static const int MIN_PUCK_COUNT = 2; static const int MIN_FEET_AND_HIPS = 3; @@ -62,6 +76,34 @@ static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; +#ifdef VIVE_PRO_EYE +enum ViveHandJointIndex { + HAND = 0, + THUMB_1, + THUMB_2, + THUMB_3, + THUMB_4, + INDEX_1, + INDEX_2, + INDEX_3, + INDEX_4, + MIDDLE_1, + MIDDLE_2, + MIDDLE_3, + MIDDLE_4, + RING_1, + RING_2, + RING_3, + RING_4, + PINKY_1, + PINKY_2, + PINKY_3, + PINKY_4, + + Size +}; +#endif + const char* ViveControllerManager::NAME { "OpenVR" }; const std::map TRACKING_RESULT_TO_STRING = { @@ -114,21 +156,52 @@ static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) return result; } -static glm::mat4 calculateResetMat() { - auto chaperone = vr::VRChaperone(); - if (chaperone) { - float const UI_RADIUS = 1.0f; - float const UI_HEIGHT = 1.6f; - float const UI_Z_OFFSET = 0.5; - - float xSize, zSize; - chaperone->GetPlayAreaSize(&xSize, &zSize); - glm::vec3 uiPos(0.0f, UI_HEIGHT, UI_RADIUS - (0.5f * zSize) - UI_Z_OFFSET); - - return glm::inverse(createMatFromQuatAndPos(glm::quat(), uiPos)); +#ifdef VIVE_PRO_EYE +class ViveProEyeReadThread : public QThread { +public: + ViveProEyeReadThread() { + setObjectName("OpenVR ViveProEye Read Thread"); } - return glm::mat4(); -} + void run() override { + while (!quit) { + ViveSR::anipal::Eye::EyeData eyeData; + int result = ViveSR::anipal::Eye::GetEyeData(&eyeData); + { + QMutexLocker locker(&eyeDataMutex); + eyeDataBuffer.getEyeDataResult = result; + if (result == ViveSR::Error::WORK) { + uint64_t leftValids = eyeData.verbose_data.left.eye_data_validata_bit_mask; + uint64_t rightValids = eyeData.verbose_data.right.eye_data_validata_bit_mask; + + eyeDataBuffer.leftDirectionValid = + (leftValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY) > (uint64_t)0; + eyeDataBuffer.rightDirectionValid = + (rightValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY) > (uint64_t)0; + eyeDataBuffer.leftOpennessValid = + (leftValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY) > (uint64_t)0; + eyeDataBuffer.rightOpennessValid = + (rightValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY) > (uint64_t)0; + + float *leftGaze = eyeData.verbose_data.left.gaze_direction_normalized.elem_; + float *rightGaze = eyeData.verbose_data.right.gaze_direction_normalized.elem_; + eyeDataBuffer.leftEyeGaze = glm::vec3(leftGaze[0], leftGaze[1], leftGaze[2]); + eyeDataBuffer.rightEyeGaze = glm::vec3(rightGaze[0], rightGaze[1], rightGaze[2]); + + eyeDataBuffer.leftEyeOpenness = eyeData.verbose_data.left.eye_openness; + eyeDataBuffer.rightEyeOpenness = eyeData.verbose_data.right.eye_openness; + } + } + } + } + + bool quit { false }; + + // mutex and buffer for moving data from this thread to the other one + QMutex eyeDataMutex; + EyeDataBuffer eyeDataBuffer; +}; +#endif + static QString outOfRangeDataStrategyToString(ViveControllerManager::OutOfRangeDataStrategy strategy) { switch (strategy) { @@ -191,6 +264,10 @@ void ViveControllerManager::setConfigurationSettings(const QJsonObject configura _hmdDesktopTracking = configurationSettings["hmdDesktopTracking"].toBool(); } + if (configurationSettings.contains("eyeTrackingEnabled")) { + _eyeTrackingEnabled = configurationSettings["eyeTrackingEnabled"].toBool(); + } + _inputDevice->configureCalibrationSettings(configurationSettings); saveSettings(); } @@ -201,6 +278,7 @@ QJsonObject ViveControllerManager::configurationSettings() { QJsonObject configurationSettings = _inputDevice->configurationSettings(); configurationSettings["desktopMode"] = _desktopMode; configurationSettings["hmdDesktopTracking"] = _hmdDesktopTracking; + configurationSettings["eyeTrackingEnabled"] = _eyeTrackingEnabled; return configurationSettings; } @@ -211,6 +289,82 @@ QString ViveControllerManager::configurationLayout() { return OPENVR_LAYOUT; } +bool isDeviceIndexActive(vr::IVRSystem*& system, uint32_t deviceIndex) { + if (!system) { + return false; + } + if (deviceIndex != vr::k_unTrackedDeviceIndexInvalid && + system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && + system->IsTrackedDeviceConnected(deviceIndex)) { + vr::EDeviceActivityLevel activityLevel = system->GetTrackedDeviceActivityLevel(deviceIndex); + if (activityLevel == vr::k_EDeviceActivityLevel_UserInteraction) { + return true; + } + } + return false; +} + +bool isHandControllerActive(vr::IVRSystem*& system, vr::ETrackedControllerRole deviceRole) { + if (!system) { + return false; + } + auto deviceIndex = system->GetTrackedDeviceIndexForControllerRole(deviceRole); + return isDeviceIndexActive(system, deviceIndex); +} + +bool areBothHandControllersActive(vr::IVRSystem*& system) { + return + isHandControllerActive(system, vr::TrackedControllerRole_LeftHand) && + isHandControllerActive(system, vr::TrackedControllerRole_RightHand); +} + +#ifdef VIVE_PRO_EYE +void ViveControllerManager::enableGestureDetection() { + if (_viveCameraHandTracker) { + return; + } + if (!ViveSR::anipal::Eye::IsViveProEye()) { + return; + } + +// #define HAND_TRACKER_USE_EXTERNAL_TRANSFORM 1 + +#ifdef HAND_TRACKER_USE_EXTERNAL_TRANSFORM + UseExternalTransform(true); // camera hand tracker results are in HMD frame +#else + UseExternalTransform(false); // camera hand tracker results are in sensor frame +#endif + GestureOption options; // defaults are GestureBackendAuto and GestureModeSkeleton + GestureFailure gestureFailure = StartGestureDetection(&options); + switch (gestureFailure) { + case GestureFailureNone: + qDebug() << "StartGestureDetection success"; + _viveCameraHandTracker = true; + break; + case GestureFailureOpenCL: + qDebug() << "StartGestureDetection (Only on Windows) OpenCL is not supported on the machine"; + break; + case GestureFailureCamera: + qDebug() << "StartGestureDetection Start camera failed"; + break; + case GestureFailureInternal: + qDebug() << "StartGestureDetection Internal errors"; + break; + case GestureFailureCPUOnPC: + qDebug() << "StartGestureDetection CPU backend is not supported on Windows"; + break; + } +} + +void ViveControllerManager::disableGestureDetection() { + if (!_viveCameraHandTracker) { + return; + } + StopGestureDetection(); + _viveCameraHandTracker = false; +} +#endif + bool ViveControllerManager::activate() { InputPlugin::activate(); @@ -230,6 +384,30 @@ bool ViveControllerManager::activate() { auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; + +#ifdef VIVE_PRO_EYE + if (ViveSR::anipal::Eye::IsViveProEye()) { + qDebug() << "Vive Pro eye-tracking detected"; + + int error = ViveSR::anipal::Initial(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE, NULL); + if (error == ViveSR::Error::WORK) { + _viveProEye = true; + qDebug() << "Successfully initialize Eye engine."; + } else if (error == ViveSR::Error::RUNTIME_NOT_FOUND) { + _viveProEye = false; + qDebug() << "please follows SRanipal SDK guide to install SR_Runtime first"; + } else { + _viveProEye = false; + qDebug() << "Failed to initialize Eye engine. please refer to ViveSR error code:" << error; + } + + if (_viveProEye) { + _viveProEyeReadThread = std::make_shared(); + _viveProEyeReadThread->start(QThread::HighPriority); + } + } +#endif + return true; } @@ -251,6 +429,15 @@ void ViveControllerManager::deactivate() { userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; +#ifdef VIVE_PRO_EYE + if (_viveProEyeReadThread) { + _viveProEyeReadThread->quit = true; + _viveProEyeReadThread->wait(); + _viveProEyeReadThread = nullptr; + ViveSR::anipal::Release(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE); + } +#endif + saveSettings(); } @@ -262,6 +449,318 @@ bool ViveControllerManager::isHeadControllerMounted() const { return activityLevel == vr::k_EDeviceActivityLevel_UserInteraction; } +#ifdef VIVE_PRO_EYE +void ViveControllerManager::invalidateEyeInputs() { + _inputDevice->_poseStateMap[controller::LEFT_EYE].valid = false; + _inputDevice->_poseStateMap[controller::RIGHT_EYE].valid = false; + _inputDevice->_axisStateMap[controller::EYEBLINK_L].valid = false; + _inputDevice->_axisStateMap[controller::EYEBLINK_R].valid = false; +} + +void ViveControllerManager::updateEyeTracker(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + if (!isHeadControllerMounted()) { + invalidateEyeInputs(); + return; + } + + EyeDataBuffer eyeDataBuffer; + { + // GetEyeData takes around 4ms to finish, so we run it on a thread. + QMutexLocker locker(&_viveProEyeReadThread->eyeDataMutex); + memcpy(&eyeDataBuffer, &_viveProEyeReadThread->eyeDataBuffer, sizeof(eyeDataBuffer)); + } + + if (eyeDataBuffer.getEyeDataResult != ViveSR::Error::WORK) { + invalidateEyeInputs(); + return; + } + + // only update from buffer values if the new data is "valid" + if (!eyeDataBuffer.leftDirectionValid) { + eyeDataBuffer.leftEyeGaze = _prevEyeData.leftEyeGaze; + eyeDataBuffer.leftDirectionValid = _prevEyeData.leftDirectionValid; + } + if (!eyeDataBuffer.rightDirectionValid) { + eyeDataBuffer.rightEyeGaze = _prevEyeData.rightEyeGaze; + eyeDataBuffer.rightDirectionValid = _prevEyeData.rightDirectionValid; + } + if (!eyeDataBuffer.leftOpennessValid) { + eyeDataBuffer.leftEyeOpenness = _prevEyeData.leftEyeOpenness; + eyeDataBuffer.leftOpennessValid = _prevEyeData.leftOpennessValid; + } + if (!eyeDataBuffer.rightOpennessValid) { + eyeDataBuffer.rightEyeOpenness = _prevEyeData.rightEyeOpenness; + eyeDataBuffer.rightOpennessValid = _prevEyeData.rightOpennessValid; + } + _prevEyeData = eyeDataBuffer; + + // transform data into what the controller system expects. + + // in the data from sranipal, left=+x, up=+y, forward=+z + mat4 localLeftEyeMat = glm::lookAt(vec3(0.0f, 0.0f, 0.0f), + glm::vec3(eyeDataBuffer.leftEyeGaze[0], + eyeDataBuffer.leftEyeGaze[1], + -eyeDataBuffer.leftEyeGaze[2]), + vec3(0.0f, 1.0f, 0.0f)); + quat localLeftEyeRot = glm::quat_cast(localLeftEyeMat); + quat avatarLeftEyeRot = _inputDevice->_poseStateMap[controller::HEAD].rotation * localLeftEyeRot; + + mat4 localRightEyeMat = glm::lookAt(vec3(0.0f, 0.0f, 0.0f), + glm::vec3(eyeDataBuffer.rightEyeGaze[0], + eyeDataBuffer.rightEyeGaze[1], + -eyeDataBuffer.rightEyeGaze[2]), + vec3(0.0f, 1.0f, 0.0f)); + quat localRightEyeRot = glm::quat_cast(localRightEyeMat); + quat avatarRightEyeRot = _inputDevice->_poseStateMap[controller::HEAD].rotation * localRightEyeRot; + + // TODO -- figure out translations for eyes + if (eyeDataBuffer.leftDirectionValid) { + _inputDevice->_poseStateMap[controller::LEFT_EYE] = controller::Pose(glm::vec3(), avatarLeftEyeRot); + _inputDevice->_poseStateMap[controller::LEFT_EYE].valid = true; + } else { + _inputDevice->_poseStateMap[controller::LEFT_EYE].valid = false; + } + if (eyeDataBuffer.rightDirectionValid) { + _inputDevice->_poseStateMap[controller::RIGHT_EYE] = controller::Pose(glm::vec3(), avatarRightEyeRot); + _inputDevice->_poseStateMap[controller::RIGHT_EYE].valid = true; + } else { + _inputDevice->_poseStateMap[controller::RIGHT_EYE].valid = false; + } + + quint64 now = usecTimestampNow(); + + // in hifi, 0 is open 1 is closed. in SRanipal 1 is open, 0 is closed. + if (eyeDataBuffer.leftOpennessValid) { + _inputDevice->_axisStateMap[controller::EYEBLINK_L] = + controller::AxisValue(1.0f - eyeDataBuffer.leftEyeOpenness, now); + } else { + _inputDevice->_poseStateMap[controller::EYEBLINK_L].valid = false; + } + if (eyeDataBuffer.rightOpennessValid) { + _inputDevice->_axisStateMap[controller::EYEBLINK_R] = + controller::AxisValue(1.0f - eyeDataBuffer.rightEyeOpenness, now); + } else { + _inputDevice->_poseStateMap[controller::EYEBLINK_R].valid = false; + } +} + +glm::vec3 ViveControllerManager::getRollingAverageHandPoint(int handIndex, int pointIndex) const { +#if 0 + return _handPoints[0][handIndex][pointIndex]; +#else + glm::vec3 result; + for (int s = 0; s < NUMBER_OF_HAND_TRACKER_SMOOTHING_FRAMES; s++) { + result += _handPoints[s][handIndex][pointIndex]; + } + return result / NUMBER_OF_HAND_TRACKER_SMOOTHING_FRAMES; +#endif +} + + +controller::Pose ViveControllerManager::trackedHandDataToPose(int hand, const glm::vec3& palmFacing, + int nearHandPositionIndex, int farHandPositionIndex) { + glm::vec3 nearPoint = getRollingAverageHandPoint(hand, nearHandPositionIndex); + + glm::quat poseRot; + if (nearHandPositionIndex != farHandPositionIndex) { + glm::vec3 farPoint = getRollingAverageHandPoint(hand, farHandPositionIndex); + + glm::vec3 pointingDir = farPoint - nearPoint; // y axis + glm::vec3 otherAxis = glm::cross(pointingDir, palmFacing); + + glm::mat4 rotMat; + rotMat = glm::mat4(glm::vec4(otherAxis, 0.0f), + glm::vec4(pointingDir, 0.0f), + glm::vec4(palmFacing * (hand == 0 ? 1.0f : -1.0f), 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + poseRot = glm::normalize(glmExtractRotation(rotMat)); + } + + if (!isNaN(poseRot)) { + controller::Pose pose(nearPoint, poseRot); + return pose; + } else { + controller::Pose pose; + pose.valid = false; + return pose; + } +} + + +void ViveControllerManager::trackFinger(int hand, int jointIndex1, int jointIndex2, int jointIndex3, int jointIndex4, + controller::StandardPoseChannel joint1, controller::StandardPoseChannel joint2, + controller::StandardPoseChannel joint3, controller::StandardPoseChannel joint4) { + + glm::vec3 point1 = getRollingAverageHandPoint(hand, jointIndex1); + glm::vec3 point2 = getRollingAverageHandPoint(hand, jointIndex2); + glm::vec3 point3 = getRollingAverageHandPoint(hand, jointIndex3); + glm::vec3 point4 = getRollingAverageHandPoint(hand, jointIndex4); + + glm::vec3 wristPos = getRollingAverageHandPoint(hand, ViveHandJointIndex::HAND); + glm::vec3 thumb2 = getRollingAverageHandPoint(hand, ViveHandJointIndex::THUMB_2); + glm::vec3 pinkie1 = getRollingAverageHandPoint(hand, ViveHandJointIndex::PINKY_1); + + // 1st + glm::vec3 palmFacing = glm::normalize(glm::cross(pinkie1 - wristPos, thumb2 - wristPos)); + glm::vec3 handForward = glm::normalize(point1 - wristPos); + glm::vec3 x = glm::normalize(glm::cross(palmFacing, handForward)); + glm::vec3 y = glm::normalize(point2 - point1); + glm::vec3 z = (hand == 0) ? glm::cross(y, x) : glm::cross(x, y); + glm::mat4 rotMat1 = glm::mat4(glm::vec4(x, 0.0f), + glm::vec4(y, 0.0f), + glm::vec4(z, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + glm::quat rot1 = glm::normalize(glmExtractRotation(rotMat1)); + if (!isNaN(rot1)) { + _inputDevice->_poseStateMap[joint1] = controller::Pose(point1, rot1); + } + + + // 2nd + glm::vec3 x2 = x; // glm::normalize(glm::cross(point3 - point2, point2 - point1)); + glm::vec3 y2 = glm::normalize(point3 - point2); + glm::vec3 z2 = (hand == 0) ? glm::cross(y2, x2) : glm::cross(x2, y2); + + glm::mat4 rotMat2 = glm::mat4(glm::vec4(x2, 0.0f), + glm::vec4(y2, 0.0f), + glm::vec4(z2, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + glm::quat rot2 = glm::normalize(glmExtractRotation(rotMat2)); + if (!isNaN(rot2)) { + _inputDevice->_poseStateMap[joint2] = controller::Pose(point2, rot2); + } + + + // 3rd + glm::vec3 x3 = x; // glm::normalize(glm::cross(point4 - point3, point3 - point1)); + glm::vec3 y3 = glm::normalize(point4 - point3); + glm::vec3 z3 = (hand == 0) ? glm::cross(y3, x3) : glm::cross(x3, y3); + + glm::mat4 rotMat3 = glm::mat4(glm::vec4(x3, 0.0f), + glm::vec4(y3, 0.0f), + glm::vec4(z3, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + glm::quat rot3 = glm::normalize(glmExtractRotation(rotMat3)); + if (!isNaN(rot3)) { + _inputDevice->_poseStateMap[joint3] = controller::Pose(point3, rot3); + } + + + // 4th + glm::quat rot4 = rot3; + if (!isNaN(rot4)) { + _inputDevice->_poseStateMap[joint4] = controller::Pose(point4, rot4); + } +} + + +void ViveControllerManager::updateCameraHandTracker(float deltaTime, + const controller::InputCalibrationData& inputCalibrationData) { + + if (areBothHandControllersActive(_system)) { + // if both hand-controllers are in use, don't do camera hand tracking + disableGestureDetection(); + } else { + enableGestureDetection(); + } + + if (!_viveCameraHandTracker) { + return; + } + + const GestureResult* results = NULL; + int handTrackerFrameIndex { -1 }; + int resultsHandCount = GetGestureResult(&results, &handTrackerFrameIndex); + + // FIXME: Why the commented-out condition? + if (handTrackerFrameIndex >= 0 /* && handTrackerFrameIndex != _lastHandTrackerFrameIndex */) { +#ifdef HAND_TRACKER_USE_EXTERNAL_TRANSFORM + glm::mat4 trackedHandToAvatar = + glm::inverse(inputCalibrationData.avatarMat) * + inputCalibrationData.sensorToWorldMat * + inputCalibrationData.hmdSensorMat; + // glm::mat4 trackedHandToAvatar = _inputDevice->_poseStateMap[controller::HEAD].getMatrix() * Matrices::Y_180; +#else + DisplayPluginPointer displayPlugin = _container->getActiveDisplayPlugin(); + std::shared_ptr openVRDisplayPlugin = + std::dynamic_pointer_cast(displayPlugin); + glm::mat4 sensorResetMatrix; + if (openVRDisplayPlugin) { + sensorResetMatrix = openVRDisplayPlugin->getSensorResetMatrix(); + } + + glm::mat4 trackedHandToAvatar = + glm::inverse(inputCalibrationData.avatarMat) * + inputCalibrationData.sensorToWorldMat * + sensorResetMatrix; +#endif + + // roll all the old points in the rolling average + memmove(&(_handPoints[1]), + &(_handPoints[0]), + sizeof(_handPoints[0]) * (NUMBER_OF_HAND_TRACKER_SMOOTHING_FRAMES - 1)); + + for (int handIndex = 0; handIndex < resultsHandCount; handIndex++) { + bool isLeftHand = results[handIndex].isLeft; + + vr::ETrackedControllerRole controllerRole = + isLeftHand ? vr::TrackedControllerRole_LeftHand : vr::TrackedControllerRole_RightHand; + if (isHandControllerActive(_system, controllerRole)) { + continue; // if the controller for this hand is tracked, ignore camera hand tracking + } + + int hand = isLeftHand ? 0 : 1; + for (int pointIndex = 0; pointIndex < NUMBER_OF_HAND_POINTS; pointIndex++) { + glm::vec3 pos(results[handIndex].points[3 * pointIndex], + results[handIndex].points[3 * pointIndex + 1], + -results[handIndex].points[3 * pointIndex + 2]); + _handPoints[0][hand][pointIndex] = transformPoint(trackedHandToAvatar, pos); + } + + glm::vec3 wristPos = getRollingAverageHandPoint(hand, ViveHandJointIndex::HAND); + glm::vec3 thumb2 = getRollingAverageHandPoint(hand, ViveHandJointIndex::THUMB_2); + glm::vec3 pinkie1 = getRollingAverageHandPoint(hand, ViveHandJointIndex::PINKY_1); + glm::vec3 palmFacing = glm::cross(pinkie1 - wristPos, thumb2 - wristPos); // z axis + + _inputDevice->_poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = + trackedHandDataToPose(hand, palmFacing, ViveHandJointIndex::HAND, ViveHandJointIndex::MIDDLE_1); + trackFinger(hand, ViveHandJointIndex::THUMB_1, ViveHandJointIndex::THUMB_2, ViveHandJointIndex::THUMB_3, + ViveHandJointIndex::THUMB_4, + isLeftHand ? controller::LEFT_HAND_THUMB1 : controller::RIGHT_HAND_THUMB1, + isLeftHand ? controller::LEFT_HAND_THUMB2 : controller::RIGHT_HAND_THUMB2, + isLeftHand ? controller::LEFT_HAND_THUMB3 : controller::RIGHT_HAND_THUMB3, + isLeftHand ? controller::LEFT_HAND_THUMB4 : controller::RIGHT_HAND_THUMB4); + trackFinger(hand, ViveHandJointIndex::INDEX_1, ViveHandJointIndex::INDEX_2, ViveHandJointIndex::INDEX_3, + ViveHandJointIndex::INDEX_4, + isLeftHand ? controller::LEFT_HAND_INDEX1 : controller::RIGHT_HAND_INDEX1, + isLeftHand ? controller::LEFT_HAND_INDEX2 : controller::RIGHT_HAND_INDEX2, + isLeftHand ? controller::LEFT_HAND_INDEX3 : controller::RIGHT_HAND_INDEX3, + isLeftHand ? controller::LEFT_HAND_INDEX4 : controller::RIGHT_HAND_INDEX4); + trackFinger(hand, ViveHandJointIndex::MIDDLE_1, ViveHandJointIndex::MIDDLE_2, ViveHandJointIndex::MIDDLE_3, + ViveHandJointIndex::MIDDLE_4, + isLeftHand ? controller::LEFT_HAND_MIDDLE1 : controller::RIGHT_HAND_MIDDLE1, + isLeftHand ? controller::LEFT_HAND_MIDDLE2 : controller::RIGHT_HAND_MIDDLE2, + isLeftHand ? controller::LEFT_HAND_MIDDLE3 : controller::RIGHT_HAND_MIDDLE3, + isLeftHand ? controller::LEFT_HAND_MIDDLE4 : controller::RIGHT_HAND_MIDDLE4); + trackFinger(hand, ViveHandJointIndex::RING_1, ViveHandJointIndex::RING_2, ViveHandJointIndex::RING_3, + ViveHandJointIndex::RING_4, + isLeftHand ? controller::LEFT_HAND_RING1 : controller::RIGHT_HAND_RING1, + isLeftHand ? controller::LEFT_HAND_RING2 : controller::RIGHT_HAND_RING2, + isLeftHand ? controller::LEFT_HAND_RING3 : controller::RIGHT_HAND_RING3, + isLeftHand ? controller::LEFT_HAND_RING4 : controller::RIGHT_HAND_RING4); + trackFinger(hand, ViveHandJointIndex::PINKY_1, ViveHandJointIndex::PINKY_2, ViveHandJointIndex::PINKY_3, + ViveHandJointIndex::PINKY_4, + isLeftHand ? controller::LEFT_HAND_PINKY1 : controller::RIGHT_HAND_PINKY1, + isLeftHand ? controller::LEFT_HAND_PINKY2 : controller::RIGHT_HAND_PINKY2, + isLeftHand ? controller::LEFT_HAND_PINKY3 : controller::RIGHT_HAND_PINKY3, + isLeftHand ? controller::LEFT_HAND_PINKY4 : controller::RIGHT_HAND_PINKY4); + } + } + _lastHandTrackerFrameIndex = handTrackerFrameIndex; +} +#endif + + void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!_system) { @@ -297,6 +796,15 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; } + +#ifdef VIVE_PRO_EYE + if (_viveProEye && _eyeTrackingEnabled) { + updateEyeTracker(deltaTime, inputCalibrationData); + } + + updateCameraHandTracker(deltaTime, inputCalibrationData); +#endif + } void ViveControllerManager::loadSettings() { @@ -312,6 +820,9 @@ void ViveControllerManager::loadSettings() { _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); _inputDevice->_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(settings.value("outOfRangeDataStrategy", QVariant(DEFAULT_OUT_OF_RANGE_STRATEGY)).toString()); } + + const bool DEFAULT_EYE_TRACKING_ENABLED = false; + _eyeTrackingEnabled = settings.value("eyeTrackingEnabled", QVariant(DEFAULT_EYE_TRACKING_ENABLED)).toBool(); } settings.endGroup(); } @@ -326,6 +837,8 @@ void ViveControllerManager::saveSettings() const { settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); settings.setValue(QString("outOfRangeDataStrategy"), outOfRangeDataStrategyToString(_inputDevice->_outOfRangeDataStrategy)); } + + settings.setValue(QString("eyeTrackingEnabled"), _eyeTrackingEnabled); } settings.endGroup(); } @@ -354,6 +867,9 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle if (_headsetName == "HTC") { _headsetName += " Vive"; } + if (oculusViaOpenVR()) { + _headsetName = "OpenVR"; // Enables calibration dialog to function when debugging using Oculus. + } } // While the keyboard is open, we defer strictly to the keyboard values if (isOpenVrKeyboardShown()) { @@ -370,7 +886,7 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); // collect poses for all generic trackers - for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { + for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { handleTrackedObject(i, inputCalibrationData); handleHmd(i, inputCalibrationData); } @@ -445,8 +961,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHead = headObject["override"].toBool(); if (overrideHead) { _headConfig = HeadConfig::Puck; - _headPuckYOffset = headObject["Y"].toDouble() * CM_TO_M; - _headPuckZOffset = headObject["Z"].toDouble() * CM_TO_M; + _headPuckYOffset = (float)headObject["Y"].toDouble() * CM_TO_M; + _headPuckZOffset = (float)headObject["Z"].toDouble() * CM_TO_M; } else { _headConfig = HeadConfig::HMD; } @@ -455,8 +971,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHands = handsObject["override"].toBool(); if (overrideHands) { _handConfig = HandConfig::Pucks; - _handPuckYOffset = handsObject["Y"].toDouble() * CM_TO_M; - _handPuckZOffset = handsObject["Z"].toDouble() * CM_TO_M; + _handPuckYOffset = (float)handsObject["Y"].toDouble() * CM_TO_M; + _handPuckZOffset = (float)handsObject["Z"].toDouble() * CM_TO_M; } else { _handConfig = HandConfig::HandController; } @@ -490,8 +1006,8 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() { configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD); configurationSettings["handController"] = (_handConfig == HandConfig::HandController); configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); - configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; - configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; + configurationSettings["armCircumference"] = (double)(_armCircumference * M_TO_CM); + configurationSettings["shoulderWidth"] = (double)(_shoulderWidth * M_TO_CM); configurationSettings["outOfRangeDataStrategy"] = outOfRangeDataStrategyToString(_outOfRangeDataStrategy); return configurationSettings; } @@ -708,8 +1224,6 @@ bool ViveControllerManager::InputDevice::configureHead(const glm::mat4& defaultT bool ViveControllerManager::InputDevice::configureBody(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition); int puckCount = (int)_validTrackedObjects.size(); - glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat); - glm::vec3 headPosition = getReferenceHeadPosition(defaultToReferenceMat, inputCalibration.defaultHeadMat); if (_config == Config::None) { return true; } else if (_config == Config::Feet && puckCount >= MIN_PUCK_COUNT) { @@ -807,8 +1321,6 @@ controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(const c } void ViveControllerManager::InputDevice::handleHmd(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) { - uint32_t poseIndex = controller::TRACKED_OBJECT_00 + deviceIndex; - if (_system->IsTrackedDeviceConnected(deviceIndex) && _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_HMD && _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid) { @@ -830,9 +1342,7 @@ void ViveControllerManager::InputDevice::handleHmd(uint32_t deviceIndex, const c void ViveControllerManager::InputDevice::handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { - if (_system->IsTrackedDeviceConnected(deviceIndex) && - _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && - _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid) { + if (isDeviceIndexActive(_system, deviceIndex) && _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid) { // process pose const mat4& mat = _nextSimPoseData.poses[deviceIndex]; @@ -984,11 +1494,11 @@ void ViveControllerManager::InputDevice::printDeviceTrackingResultChange(uint32_ } bool ViveControllerManager::InputDevice::checkForCalibrationEvent() { - auto& endOfMap = _buttonPressedMap.end(); - auto& leftTrigger = _buttonPressedMap.find(controller::LT); - auto& rightTrigger = _buttonPressedMap.find(controller::RT); - auto& leftAppButton = _buttonPressedMap.find(LEFT_APP_MENU); - auto& rightAppButton = _buttonPressedMap.find(RIGHT_APP_MENU); + auto endOfMap = _buttonPressedMap.end(); + auto leftTrigger = _buttonPressedMap.find(controller::LT); + auto rightTrigger = _buttonPressedMap.find(controller::RT); + auto leftAppButton = _buttonPressedMap.find(LEFT_APP_MENU); + auto rightAppButton = _buttonPressedMap.find(RIGHT_APP_MENU); return ((leftTrigger != endOfMap && leftAppButton != endOfMap) && (rightTrigger != endOfMap && rightAppButton != endOfMap)); } @@ -1339,10 +1849,74 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu * Hipsnumber{@link Pose}Hips pose. * Spine2number{@link Pose}Spine2 pose. * Headnumber{@link Pose}Head pose. + * LeftEyenumber{@link Pose}Left eye pose. + * RightEyenumber{@link Pose}Right eye pose. + * EyeBlink_LnumbernumberLeft eyelid blink. + * EyeBlink_RnumbernumberRight eyelid blink. * LeftArmnumber{@link Pose}Left arm pose. * RightArmnumber{@link Pose}Right arm pose * LeftHandnumber{@link Pose}Left hand pose. + * LeftHandThumb1number{@link Pose}Left thumb 1 finger joint pose. + * LeftHandThumb2number{@link Pose}Left thumb 2 finger joint pose. + * LeftHandThumb3number{@link Pose}Left thumb 3 finger joint pose. + * LeftHandThumb4number{@link Pose}Left thumb 4 finger joint pose. + * LeftHandIndex1number{@link Pose}Left index 1 finger joint pose. + * LeftHandIndex2number{@link Pose}Left index 2 finger joint pose. + * LeftHandIndex3number{@link Pose}Left index 3 finger joint pose. + * LeftHandIndex4number{@link Pose}Left index 4 finger joint pose. + * LeftHandMiddle1number{@link Pose}Left middle 1 finger joint pose. + * + * LeftHandMiddle2number{@link Pose}Left middle 2 finger joint pose. + * + * LeftHandMiddle3number{@link Pose}Left middle 3 finger joint pose. + * + * LeftHandMiddle4number{@link Pose}Left middle 4 finger joint pose. + * + * LeftHandRing1number{@link Pose}Left ring 1 finger joint pose. + * LeftHandRing2number{@link Pose}Left ring 2 finger joint pose. + * LeftHandRing3number{@link Pose}Left ring 3 finger joint pose. + * LeftHandRing4number{@link Pose}Left ring 4 finger joint pose. + * LeftHandPinky1number{@link Pose}Left pinky 1 finger joint pose. + * LeftHandPinky2number{@link Pose}Left pinky 2 finger joint pose. + * LeftHandPinky3number{@link Pose}Left pinky 3 finger joint pose. + * LeftHandPinky4number{@link Pose}Left pinky 4 finger joint pose. * RightHandnumber{@link Pose}Right hand pose. + * RightHandThumb1number{@link Pose}Right thumb 1 finger joint pose. + * + * RightHandThumb2number{@link Pose}Right thumb 2 finger joint pose. + * + * RightHandThumb3number{@link Pose}Right thumb 3 finger joint pose. + * + * RightHandThumb4number{@link Pose}Right thumb 4 finger joint pose. + * + * RightHandIndex1number{@link Pose}Right index 1 finger joint pose. + * + * RightHandIndex2number{@link Pose}Right index 2 finger joint pose. + * + * RightHandIndex3number{@link Pose}Right index 3 finger joint pose. + * + * RightHandIndex4number{@link Pose}Right index 4 finger joint pose. + * + * RightHandMiddle1number{@link Pose}Right middle 1 finger joint pose. + * + * RightHandMiddle2number{@link Pose}Right middle 2 finger joint pose. + * + * RightHandMiddle3number{@link Pose}Right middle 3 finger joint pose. + * + * RightHandMiddle4number{@link Pose}Right middle 4 finger joint pose. + * + * RightHandRing1number{@link Pose}Right ring 1 finger joint pose. + * RightHandRing2number{@link Pose}Right ring 2 finger joint pose. + * RightHandRing3number{@link Pose}Right ring 3 finger joint pose. + * RightHandRing4number{@link Pose}Right ring 4 finger joint pose. + * RightHandPinky1number{@link Pose}Right pinky 1 finger joint pose. + * + * RightHandPinky2number{@link Pose}Right pinky 2 finger joint pose. + * + * RightHandPinky3number{@link Pose}Right pinky 3 finger joint pose. + * + * RightHandPinky4number{@link Pose}Right pinky 4 finger joint pose. + * * Trackers * TrackedObject00number{@link Pose}Tracker 0 pose. * TrackedObject01number{@link Pose}Tracker 1 pose. @@ -1401,9 +1975,52 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI makePair(LEFT_GRIP, "LeftGrip"), makePair(RIGHT_GRIP, "RightGrip"), - // 3d location of controller + // 3d location of left controller and fingers makePair(LEFT_HAND, "LeftHand"), + makePair(LEFT_HAND_THUMB1, "LeftHandThumb1"), + makePair(LEFT_HAND_THUMB2, "LeftHandThumb2"), + makePair(LEFT_HAND_THUMB3, "LeftHandThumb3"), + makePair(LEFT_HAND_THUMB4, "LeftHandThumb4"), + makePair(LEFT_HAND_INDEX1, "LeftHandIndex1"), + makePair(LEFT_HAND_INDEX2, "LeftHandIndex2"), + makePair(LEFT_HAND_INDEX3, "LeftHandIndex3"), + makePair(LEFT_HAND_INDEX4, "LeftHandIndex4"), + makePair(LEFT_HAND_MIDDLE1, "LeftHandMiddle1"), + makePair(LEFT_HAND_MIDDLE2, "LeftHandMiddle2"), + makePair(LEFT_HAND_MIDDLE3, "LeftHandMiddle3"), + makePair(LEFT_HAND_MIDDLE4, "LeftHandMiddle4"), + makePair(LEFT_HAND_RING1, "LeftHandRing1"), + makePair(LEFT_HAND_RING2, "LeftHandRing2"), + makePair(LEFT_HAND_RING3, "LeftHandRing3"), + makePair(LEFT_HAND_RING4, "LeftHandRing4"), + makePair(LEFT_HAND_PINKY1, "LeftHandPinky1"), + makePair(LEFT_HAND_PINKY2, "LeftHandPinky2"), + makePair(LEFT_HAND_PINKY3, "LeftHandPinky3"), + makePair(LEFT_HAND_PINKY4, "LeftHandPinky4"), + + // 3d location of right controller and fingers makePair(RIGHT_HAND, "RightHand"), + makePair(RIGHT_HAND_THUMB1, "RightHandThumb1"), + makePair(RIGHT_HAND_THUMB2, "RightHandThumb2"), + makePair(RIGHT_HAND_THUMB3, "RightHandThumb3"), + makePair(RIGHT_HAND_THUMB4, "RightHandThumb4"), + makePair(RIGHT_HAND_INDEX1, "RightHandIndex1"), + makePair(RIGHT_HAND_INDEX2, "RightHandIndex2"), + makePair(RIGHT_HAND_INDEX3, "RightHandIndex3"), + makePair(RIGHT_HAND_INDEX4, "RightHandIndex4"), + makePair(RIGHT_HAND_MIDDLE1, "RightHandMiddle1"), + makePair(RIGHT_HAND_MIDDLE2, "RightHandMiddle2"), + makePair(RIGHT_HAND_MIDDLE3, "RightHandMiddle3"), + makePair(RIGHT_HAND_MIDDLE4, "RightHandMiddle4"), + makePair(RIGHT_HAND_RING1, "RightHandRing1"), + makePair(RIGHT_HAND_RING2, "RightHandRing2"), + makePair(RIGHT_HAND_RING3, "RightHandRing3"), + makePair(RIGHT_HAND_RING4, "RightHandRing4"), + makePair(RIGHT_HAND_PINKY1, "RightHandPinky1"), + makePair(RIGHT_HAND_PINKY2, "RightHandPinky2"), + makePair(RIGHT_HAND_PINKY3, "RightHandPinky3"), + makePair(RIGHT_HAND_PINKY4, "RightHandPinky4"), + makePair(LEFT_FOOT, "LeftFoot"), makePair(RIGHT_FOOT, "RightFoot"), makePair(HIPS, "Hips"), @@ -1411,6 +2028,10 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI makePair(HEAD, "Head"), makePair(LEFT_ARM, "LeftArm"), makePair(RIGHT_ARM, "RightArm"), + makePair(LEFT_EYE, "LeftEye"), + makePair(RIGHT_EYE, "RightEye"), + makePair(EYEBLINK_L, "EyeBlink_L"), + makePair(EYEBLINK_R, "EyeBlink_R"), // 16 tracked poses makePair(TRACKED_OBJECT_00, "TrackedObject00"), diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index dbd248dc53..956b2b5eaf 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -1,9 +1,9 @@ // // ViveControllerManager.h -// input-plugins/src/input-plugins // // Created by Sam Gondelman on 6/29/15. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -25,12 +25,35 @@ #include #include "OpenVrHelpers.h" +#ifdef Q_OS_WIN +#define VIVE_PRO_EYE +#endif + using PuckPosePair = std::pair; namespace vr { class IVRSystem; } + +#ifdef VIVE_PRO_EYE +class ViveProEyeReadThread; + +class EyeDataBuffer { +public: + int getEyeDataResult { 0 }; + bool leftDirectionValid { false }; + bool rightDirectionValid { false }; + bool leftOpennessValid { false }; + bool rightOpennessValid { false }; + glm::vec3 leftEyeGaze; + glm::vec3 rightEyeGaze; + float leftEyeOpenness { 0.0f }; + float rightEyeOpenness { 0.0f }; +}; +#endif + + class ViveControllerManager : public InputPlugin { Q_OBJECT public: @@ -49,12 +72,22 @@ public: bool isHeadController() const override { return true; } bool isHeadControllerMounted() const; +#ifdef VIVE_PRO_EYE + void enableGestureDetection(); + void disableGestureDetection(); +#endif + bool activate() override; void deactivate() override; - QString getDeviceName() { return QString::fromStdString(_inputDevice->_headsetName); } + QString getDeviceName() override { return QString::fromStdString(_inputDevice->_headsetName); } void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } +#ifdef VIVE_PRO_EYE + void invalidateEyeInputs(); + void updateEyeTracker(float deltaTime, const controller::InputCalibrationData& inputCalibrationData); + void updateCameraHandTracker(float deltaTime, const controller::InputCalibrationData& inputCalibrationData); +#endif void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void saveSettings() const override; @@ -229,6 +262,27 @@ private: vr::IVRSystem* _system { nullptr }; std::shared_ptr _inputDevice { std::make_shared(_system) }; + bool _eyeTrackingEnabled{ false }; + +#ifdef VIVE_PRO_EYE + bool _viveProEye { false }; + std::shared_ptr _viveProEyeReadThread; + EyeDataBuffer _prevEyeData; + + bool _viveCameraHandTracker { false }; + int _lastHandTrackerFrameIndex { -1 }; + + const static int NUMBER_OF_HAND_TRACKER_SMOOTHING_FRAMES { 6 }; + const static int NUMBER_OF_HAND_POINTS { 21 }; + glm::vec3 _handPoints[NUMBER_OF_HAND_TRACKER_SMOOTHING_FRAMES][2][NUMBER_OF_HAND_POINTS]; // 2 for number of hands + glm::vec3 getRollingAverageHandPoint(int handIndex, int pointIndex) const; + controller::Pose trackedHandDataToPose(int hand, const glm::vec3& palmFacing, + int nearHandPositionIndex, int farHandPositionIndex); + void trackFinger(int hand, int jointIndex1, int jointIndex2, int jointIndex3, int jointIndex4, + controller::StandardPoseChannel joint1, controller::StandardPoseChannel joint2, + controller::StandardPoseChannel joint3, controller::StandardPoseChannel joint4); +#endif + static const char* NAME; }; diff --git a/prebuild.py b/prebuild.py index bd4c74bd64..cc315a49a4 100644 --- a/prebuild.py +++ b/prebuild.py @@ -20,6 +20,7 @@ import hifi_singleton import hifi_utils import hifi_android import hifi_vcpkg +import hifi_qt import argparse import concurrent @@ -93,6 +94,8 @@ def parse_args(): parser.add_argument('--force-build', action='store_true') parser.add_argument('--release-type', type=str, default="DEV", help="DEV, PR, or PRODUCTION") parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution') + parser.add_argument('--vcpkg-build-type', type=str, help='Could be `release` or `debug`. By default it doesn`t set the build-type') + parser.add_argument('--vcpkg-skip-clean', action='store_true', help='Skip the cleanup of vcpkg downloads and packages folders after vcpkg build completition.') parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build') parser.add_argument('--ports-path', type=str, default=defaultPortsPath) parser.add_argument('--ci-build', action='store_true', default=os.getenv('CI_BUILD') is not None) @@ -112,6 +115,7 @@ def main(): del os.environ[var] args = parse_args() + assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS') if args.ci_build: logging.basicConfig(datefmt='%H:%M:%S', format='%(asctime)s %(guid)s %(message)s', level=logging.INFO) @@ -119,9 +123,35 @@ def main(): logger.info('sha=%s' % headSha()) logger.info('start') - # Only allow one instance of the program to run at a time + # OS dependent information + system = platform.system() + if 'Windows' == system and 'CI_BUILD' in os.environ and os.environ["CI_BUILD"] == "Github": + logger.info("Downloading NSIS") + with timer('NSIS'): + hifi_utils.downloadAndExtract(assets_url + '/dependencies/NSIS-hifi-plugins-1.0.tgz', "C:/Program Files (x86)") + + qtInstallPath = '' + # If not android, install our Qt build + if not args.android: + qt = hifi_qt.QtDownloader(args) + qtInstallPath = qt.cmakePath + with hifi_singleton.Singleton(qt.lockFile) as lock: + with timer('Qt'): + qt.installQt() + qt.writeConfig() + pm = hifi_vcpkg.VcpkgRepo(args) + if qtInstallPath != '': + pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath) + + # Only allow one instance of the program to run at a time + + if qtInstallPath != '': + pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath) + + # Only allow one instance of the program to run at a time with hifi_singleton.Singleton(pm.lockFile) as lock: + with timer('Bootstraping'): if not pm.upToDate(): pm.bootstrap() @@ -135,10 +165,10 @@ def main(): # * build host tools, like spirv-cross and scribe # * build client dependencies like openssl and nvtt with timer('Setting up dependencies'): - pm.setupDependencies() + pm.setupDependencies(qt=qtInstallPath) # wipe out the build directories (after writing the tag, since failure - # here shouldn't invalidte the vcpkg install) + # here shouldn't invalidate the vcpkg install) with timer('Cleaning builds'): pm.cleanBuilds() @@ -153,6 +183,13 @@ def main(): qtPath = os.path.join(pm.androidPackagePath, 'qt') hifi_android.QtPackager(appPath, qtPath).bundle() + # Fixup the vcpkg cmake to not reset VCPKG_TARGET_TRIPLET + pm.fixupCmakeScript() + + if not args.vcpkg_skip_clean: + # Cleanup downloads and packages folders in vcpkg to make it smaller for CI + pm.cleanupDevelopmentFiles() + # Write the vcpkg config to the build directory last with timer('Writing configuration'): pm.writeConfig() diff --git a/screenshare/.gitignore b/screenshare/.gitignore new file mode 100644 index 0000000000..e978d75d04 --- /dev/null +++ b/screenshare/.gitignore @@ -0,0 +1,4 @@ +hifi-screenshare-*/ +hifi-screenshare*.zip +screenshare*.zip +screenshare-*/ diff --git a/screenshare/CMakeLists.txt b/screenshare/CMakeLists.txt new file mode 100644 index 0000000000..d91d8c50b1 --- /dev/null +++ b/screenshare/CMakeLists.txt @@ -0,0 +1,31 @@ +set(TARGET_NAME screenshare) + +add_custom_target(${TARGET_NAME}-npm-install + COMMAND npm install + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) +add_custom_target(${TARGET_NAME} + COMMAND npm run packager -- --out ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${TARGET_NAME}-npm-install +) + +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "screenshare") +set_target_properties(${TARGET_NAME}-npm-install PROPERTIES FOLDER "hidden/screenshare") + +if (WIN32) + set(PACKAGED_SCREENSHARE_FOLDER "hifi-screenshare-win32-x64") + set(SCREENSHARE_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_SCREENSHARE_FOLDER}") + install( + DIRECTORY "${SCREENSHARE_DESTINATION}/" + DESTINATION ${SCREENSHARE_INSTALL_DIR} + ) + + set(EXECUTABLE_PATH "${SCREENSHARE_DESTINATION}/${SCREENSHARE_EXEC_NAME}") + optional_win_executable_signing() +endif() + +# DO build the Screenshare Electron app when building the `ALL_BUILD` target. +# DO build the Screenshare Electron app when a user selects "Build Solution" from within Visual Studio. +set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL FALSE EXCLUDE_FROM_DEFAULT_BUILD FALSE) +set_target_properties(${TARGET_NAME}-npm-install PROPERTIES EXCLUDE_FROM_ALL FALSE EXCLUDE_FROM_DEFAULT_BUILD FALSE) diff --git a/screenshare/README.md b/screenshare/README.md new file mode 100644 index 0000000000..e57f37adc1 --- /dev/null +++ b/screenshare/README.md @@ -0,0 +1,16 @@ +# Screen Sharing within High Fidelity +This Screen Share app, built using Electron, allows for easy desktop screen sharing when used in conjuction with various scripts in the `hifi-content` repository. + +# Screen Sharing Source Files +## `packager.js` +Calling npm run packager will use this file to create the actual Electron `hifi-screenshare` executable. +It will kick out a folder `hifi-screenshare-` which contains an executable. + +## `src/screenshareApp.js` +The main process file to configure the electron app. + +## `src/screenshareMainProcess.js` +The render file to display the app's UI. + +## `screenshareApp.html` +The HTML that displays the screen selection UI and the confirmation screen UI. \ No newline at end of file diff --git a/screenshare/package-lock.json b/screenshare/package-lock.json new file mode 100644 index 0000000000..c7d92d3e17 --- /dev/null +++ b/screenshare/package-lock.json @@ -0,0 +1,2289 @@ +{ + "name": "highfidelity_screenshare", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@electron/get": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.5.0.tgz", + "integrity": "sha512-tafxBz6n08G6SX961F/h8XFtpB/DdwRvJJoDeOH9x78jDSCMQ2G/rRWqSwLFp9oeMFBJf0Pf5Kkw6TKt5w9TWg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "sumchecker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.0.tgz", + "integrity": "sha512-yreseuC/z4iaodVoq07XULEOO9p4jnQazO7mbrnDSvWAU/y2cbyIKs+gWJptfcGu9R+1l27K8Rkj0bfvqnBpgQ==", + "dev": true, + "requires": { + "debug": "^4.1.0" + } + } + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "10.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.21.tgz", + "integrity": "sha512-nuFlRdBiqbF+PJIEVxm2jLFcQWN7q7iWEJGsBV4n7v1dbI9qXB8im2pMMKMCUZe092sQb5SQft2DHfuQGK5hqQ==", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "asar": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/asar/-/asar-2.0.1.tgz", + "integrity": "sha512-Vo9yTuUtyFahkVMFaI6uMuX6N7k5DWa6a/8+7ov0/f8Lq9TVR0tUjzSzxQSxT1Y+RJIZgnP7BVb6Uhi+9cjxqA==", + "dev": true, + "requires": { + "chromium-pickle-js": "^0.2.0", + "commander": "^2.20.0", + "cuint": "^0.2.2", + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "tmp-promise": "^1.0.5" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "author-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", + "integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-zip": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-2.1.6.tgz", + "integrity": "sha512-xLIETNkzRcU6jGRzenJyRFxahbtP4628xEKMTI/Ql0Vu8m4h8M7uRLVi7E5OYHuJ6VQPsG4icJumKAFUvfm0+A==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "defer-to-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "electron": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/electron/-/electron-6.0.12.tgz", + "integrity": "sha512-70ODZa1RP6K0gE9IV9YLCXPSyhLjXksCuYSSPb3MljbfwfHo5uE6X0CGxzm+54YuPdE2e7EPnWZxOOsJYrS5iQ==", + "dev": true, + "requires": { + "@types/node": "^10.12.18", + "electron-download": "^4.1.0", + "extract-zip": "^1.0.3" + } + }, + "electron-download": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", + "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", + "dev": true, + "requires": { + "debug": "^3.0.0", + "env-paths": "^1.0.0", + "fs-extra": "^4.0.1", + "minimist": "^1.2.0", + "nugget": "^2.0.1", + "path-exists": "^3.0.0", + "rc": "^1.2.1", + "semver": "^5.4.1", + "sumchecker": "^2.0.2" + } + }, + "electron-notarize": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.1.1.tgz", + "integrity": "sha512-TpKfJcz4LXl5jiGvZTs5fbEx+wUFXV5u8voeG5WCHWfY/cdgdD8lDZIZRqLVOtR3VO+drgJ9aiSHIO9TYn/fKg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^8.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "electron-osx-sign": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.14.tgz", + "integrity": "sha512-72vtrz9I3dOeFDaNvO5thwIjrimDiXMmYEbN0hEBqnvcSSMOWugjim2wiY9ox3dhuBFUhxp3owmuZCoH3Ij08A==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "electron-packager": { + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-14.0.6.tgz", + "integrity": "sha512-X+ikV+TnnNkIrK93vOjsjPeykCQBFxBS7LXKMTE1s62rXWirGMdjWL+edVkBOMRkH0ROJyFmWM28Dpj6sfEg+A==", + "dev": true, + "requires": { + "@electron/get": "^1.3.0", + "asar": "^2.0.1", + "cross-zip": "^2.1.5", + "debug": "^4.0.1", + "electron-notarize": "^0.1.1", + "electron-osx-sign": "^0.4.11", + "fs-extra": "^8.1.0", + "galactus": "^0.2.1", + "get-package-info": "^1.0.0", + "junk": "^3.1.0", + "parse-author": "^2.0.0", + "plist": "^3.0.0", + "rcedit": "^2.0.0", + "resolve": "^1.1.6", + "sanitize-filename": "^1.6.0", + "semver": "^6.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "flora-colossus": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-1.0.1.tgz", + "integrity": "sha512-d+9na7t9FyH8gBJoNDSi28mE4NgQVGGvxQ4aHtFRetjyh5SXjuus+V5EZaxFmFdXVemSOrx0lsgEl/ZMjnOWJA==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^7.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "galactus": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-0.2.1.tgz", + "integrity": "sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk=", + "dev": true, + "requires": { + "debug": "^3.1.0", + "flora-colossus": "^1.0.0", + "fs-extra": "^4.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-package-info": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", + "integrity": "sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw=", + "dev": true, + "requires": { + "bluebird": "^3.1.1", + "debug": "^2.2.0", + "lodash.get": "^4.0.0", + "read-pkg-up": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "nugget": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", + "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", + "dev": true, + "requires": { + "debug": "^2.1.3", + "minimist": "^1.1.0", + "pretty-bytes": "^1.0.2", + "progress-stream": "^1.1.0", + "request": "^2.45.0", + "single-line-log": "^1.1.2", + "throttleit": "0.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-author": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", + "integrity": "sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=", + "dev": true, + "requires": { + "author-regex": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "plist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz", + "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==", + "dev": true, + "requires": { + "base64-js": "^1.2.3", + "xmlbuilder": "^9.0.7", + "xmldom": "0.1.x" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "pretty-bytes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.1.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", + "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", + "dev": true, + "requires": { + "speedometer": "~0.1.2", + "through2": "~0.2.3" + } + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "rcedit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-2.0.0.tgz", + "integrity": "sha512-XcFGyEBjhWSsud+R8elwQtGBbVkCf7tAiad+nXo5jc6l2rMf46NfGNwjnmBNneBIZDfq+Npf8lwP371JTONfrw==", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "single-line-log": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", + "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", + "dev": true, + "requires": { + "string-width": "^1.0.1" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "speedometer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", + "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "sumchecker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz", + "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", + "dev": true, + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "throttleit": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", + "dev": true + }, + "through2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9", + "xtend": "~2.1.1" + } + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + }, + "tmp-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz", + "integrity": "sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "tmp": "0.1.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=", + "dev": true + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "~0.4.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yargs": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz", + "integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==", + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" + } + } + } +} diff --git a/screenshare/package.json b/screenshare/package.json new file mode 100644 index 0000000000..372679082f --- /dev/null +++ b/screenshare/package.json @@ -0,0 +1,27 @@ +{ + "name": "highfidelity_screenshare", + "version": "1.0.0", + "description": "High Fidelity Screenshare", + "main": "src/screenshareMainProcess.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "packager": "node packager.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/highfidelity/hifi.git" + }, + "author": "High Fidelity", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/highfidelity/hifi/issues" + }, + "homepage": "https://github.com/highfidelity/hifi#readme", + "devDependencies": { + "electron": "^6.0.12", + "electron-packager": "^14.0.6" + }, + "dependencies": { + "yargs": "^14.2.0" + } +} diff --git a/screenshare/packager.js b/screenshare/packager.js new file mode 100644 index 0000000000..05bda9af71 --- /dev/null +++ b/screenshare/packager.js @@ -0,0 +1,49 @@ +var packager = require('electron-packager'); +var osType = require('os').type(); +var argv = require('yargs').argv; + +var platform = null; +if (osType == "Darwin" || osType == "Linux") { + platform = osType.toLowerCase(); +} else if (osType == "Windows_NT") { + platform = "win32" +} + +var NAME = "hifi-screenshare"; +var options = { + dir: __dirname, + name: NAME, + version: "0.1.0", + overwrite: true, + prune: true, + arch: "x64", + platform: platform, + ignore: "electron-packager|README.md|CMakeLists.txt|packager.js|.gitignore" +}; + +// setup per OS options +if (osType == "Darwin") { + options["app-bundle-id"] = "com.highfidelity.hifi-screenshare"; +} else if (osType == "Windows_NT") { + options["version-string"] = { + CompanyName: "Vircadia", + FileDescription: "Vircadia Screenshare", + ProductName: NAME, + OriginalFilename: NAME + ".exe" + } +} + +// check if we were passed a custom out directory, pass it along if so +if (argv.out) { + options.out = argv.out +} + +// call the packager to produce the executable +packager(options) + .then(appPath => { + console.log("Wrote new app to " + appPath); + }) + .catch(error => { + console.error("There was an error writing the packaged console: " + error.message); + process.exit(1); + }); diff --git a/screenshare/src/resources/Graphik-Regular.ttf b/screenshare/src/resources/Graphik-Regular.ttf new file mode 100644 index 0000000000..001faa7f47 Binary files /dev/null and b/screenshare/src/resources/Graphik-Regular.ttf differ diff --git a/screenshare/src/resources/interface.png b/screenshare/src/resources/interface.png new file mode 100644 index 0000000000..f90cbe591c Binary files /dev/null and b/screenshare/src/resources/interface.png differ diff --git a/screenshare/src/screenshareApp.html b/screenshare/src/screenshareApp.html new file mode 100644 index 0000000000..6268b581e4 --- /dev/null +++ b/screenshare/src/screenshareApp.html @@ -0,0 +1,52 @@ + + + + + + + +
+

Share your screen

+

Please select the content you'd like to share.

+
+ +
+
+
+
+
+
+
+
+ + + + + diff --git a/screenshare/src/screenshareApp.js b/screenshare/src/screenshareApp.js new file mode 100644 index 0000000000..6a33b827d4 --- /dev/null +++ b/screenshare/src/screenshareApp.js @@ -0,0 +1,312 @@ +'use strict'; +// screenshareApp.js +// +// Created by Milad Nazeri, Rebecca Stankus, and Zach Fox 2019/11/13 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +const { remote } = require('electron'); + +// Helpers +function handleError(error) { + if (error) { + console.error(error); + } +} + + +// When an application is picked, make sure we clear out the previous pick, toggle the page, +// and add the correct source +let currentScreensharePickID = ""; +function screensharePicked(id) { + currentScreensharePickID = id; + document.getElementById("share_pick").innerHTML = ""; + togglePage(); + addSource(sourceMap[id], "share_pick"); +} + + +// Once we have confirmed that we want to share, prepare the tokbox publishing initiating +// and toggle back to the selects page +function screenConfirmed(isConfirmed) { + document.getElementById("selects").innerHTML = ""; + if (isConfirmed === true){ + onAccessApproved(currentScreensharePickID); + } + togglePage(); +} + + +// Hide/show the select page or the confirmation page +let currentPage = "mainPage"; +function togglePage(){ + if (currentPage === "mainPage") { + currentPage = "confirmationPage"; + document.getElementById("select_screen").style.display = "none"; + document.getElementById("subtitle").innerHTML = "Confirm that you'd like to share this content."; + document.getElementById("confirmation_screen").style.display = "block"; + } else { + showSources(); + currentPage = "mainPage"; + document.getElementById("select_screen").style.display = "block"; + document.getElementById("subtitle").innerHTML = "Please select the content you'd like to share."; + document.getElementById("confirmation_screen").style.display = "none"; + } +} + + +// UI + +// Render the html properly and append that to the correct parent +function addSource(source, type) { + let renderedHTML = renderSourceHTML(source); + if (type === "selects") { + document.getElementById("selects").appendChild(renderedHTML); + } else { + document.getElementById("share_pick").appendChild(renderedHTML); + document.getElementById("content_name").innerHTML = source.name; + } +} + + +// Get the html created from the source. Alter slightly depending on whether this source +// is on the selects screen, or the confirmation screen. Mainly removing highlighting. +// If there is an app Icon, then add it. +function renderSourceHTML(source) { + let type = currentPage === "confirmationPage" ? "share_pick" : "selects"; + let sourceBody = document.createElement('div') + let thumbnail = source.thumbnail.toDataURL(); + sourceBody.classList.add("box") + if (type === "share_pick") { + sourceBody.style.marginLeft = "0px"; + } + + let image = ""; + if (source.appIcon) { + image = ``; + } + sourceBody.innerHTML = ` +
+ ${image} + ${source.name} +
+
+ +
+ ` + return sourceBody; +} + + +// Separate out the screenshares and applications +// Make sure the screens are labeled in order +// Concact the two arrays back together and return +function sortSources() { + let screenSources = []; + let applicationSources = []; + // Difference with Mac selects: + // 1 screen = "Enitre Screen", more than one like PC "Screen 1, Screen 2..." + screenshareSourceArray.forEach((source) => { + if (source.name.match(/(entire )?screen( )?([0-9]?)/i)) { + screenSources.push(source); + } else { + applicationSources.push(source) + } + }); + screenSources.sort((a, b) => { + let aNumber = a.name.replace(/[^\d]/, ""); + let bNumber = b.name.replace(/[^\d]/, ""); + return aNumber - bNumber; + }); + let finalSources = [...screenSources, ...applicationSources]; + return finalSources; +} + + +// Setup sorting the selection array, add individual sources, and update the sourceMap +function addSources() { + screenshareSourceArray = sortSources(); + for (let i = 0; i < screenshareSourceArray.length; i++) { + addSource(screenshareSourceArray[i], "selects"); + sourceMap[screenshareSourceArray[i].id] = screenshareSourceArray[i]; + } +} + + +// 1. Get the screens and window that are available from electron +// 2. Remove the screenshare app itself +// 3. Create a source map to help grab the correct source when picked +// 4. push all the sources for sorting to the source array +// 5. Add thse sources +const electron = require('electron'); +const SCREENSHARE_TITLE = "Screen share"; +const SCREENSHARE_TITLE_REGEX = new RegExp("^" + SCREENSHARE_TITLE + "$"); +const IMAGE_WIDTH = 265; +const IMAGE_HEIGHT = 165; +let screenshareSourceArray = []; +let sourceMap = {}; +function showSources() { + screenshareSourceArray = []; + electron.desktopCapturer.getSources({ + types:['window', 'screen'], + thumbnailSize: { + width: IMAGE_WIDTH, + height: IMAGE_HEIGHT + }, + fetchWindowIcons: true + }, (error, sources) => { + if (error) { + console.log("Error getting sources", error); + } + for (let source of sources) { + if (source.name.match(SCREENSHARE_TITLE_REGEX)){ + continue; + } + sourceMap[source.id] = source; + screenshareSourceArray.push(source); + } + addSources(); + }); +} + + +// Stop the localstream and end the tokrok publishing +let localStream; +let desktopSharing; +function stopSharing() { + desktopSharing = false; + + if (localStream) { + localStream.getTracks()[0].stop(); + localStream = null; + } + + document.getElementById('screenshare').style.display = "none"; + stopTokBoxPublisher(); +} + + +// Callback to start publishing after we have setup the chromium stream +function gotStream(stream) { + if (localStream) { + stopSharing(); + } + + localStream = stream; + startTokboxPublisher(localStream); + + stream.onended = () => { + if (desktopSharing) { + togglePage(); + } + }; +} + + +// After we grant access to electron, create a stream and using the callback +// start the tokbox publisher +function onAccessApproved(desktop_id) { + if (!desktop_id) { + console.log('Desktop Capture access rejected.'); + return; + } + + + + document.getElementById('screenshare').style.visibility = "block"; + desktopSharing = true; + navigator.webkitGetUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: desktop_id, + maxWidth: 1280, + maxHeight: 720, + maxFrameRate: 7 + } + } + }, gotStream, handleError); + remote.getCurrentWindow().minimize(); +} + + +// Tokbox + +// Once we have the connection info, this will create the session which will allow +// us to publish a stream when we are ready +function initializeTokboxSession() { + session = OT.initSession(projectAPIKey, sessionID); + session.on('sessionDisconnected', (event) => { + console.log('You were disconnected from the session.', event.reason); + }); + + // Connect to the session + session.connect(token, (error) => { + if (error) { + handleError(error); + } + }); +} + + +// Init the tokbox publisher with our newly created stream +var publisher; +function startTokboxPublisher(stream) { + publisher = document.createElement("div"); + var publisherOptions = { + audioFallbackEnabled: false, + audioSource: null, + fitMode: 'contain', + frameRate: 7, + height: 720, + insertMode: 'append', + publishAudio: false, + videoSource: stream.getVideoTracks()[0], + width: 1280 + }; + + publisher = OT.initPublisher(publisher, publisherOptions, function(error){ + if (error) { + console.log("ERROR: " + error); + } else { + session.publish(publisher, function(error) { + if (error) { + console.log("ERROR FROM Session.publish: " + error); + return; + } + }) + } + }); +} + + +// Kills the streaming being sent to tokbox +function stopTokBoxPublisher() { + publisher.destroy(); +} + + +// When the app is ready, we get this info from the command line arguments. +const ipcRenderer = electron.ipcRenderer; +let projectAPIKey; +let sessionID; +let token; +let session; +ipcRenderer.on('connectionInfo', function(event, message) { + const connectionInfo = JSON.parse(message); + projectAPIKey = connectionInfo.projectAPIKey; + sessionID = connectionInfo.sessionID; + token = connectionInfo.token; + + initializeTokboxSession(); +}); + + +// Show the initial sources after the dom has loaded +// Sources come from electron.desktopCapturer +document.addEventListener("DOMContentLoaded", () => { + showSources(); +}); diff --git a/screenshare/src/screenshareMainProcess.js b/screenshare/src/screenshareMainProcess.js new file mode 100644 index 0000000000..5dce65a783 --- /dev/null +++ b/screenshare/src/screenshareMainProcess.js @@ -0,0 +1,74 @@ +'use strict'; +// screenshareMainProcess.js +// +// Milad Nazeri and Zach Fox 2019/11/13 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +const {app, BrowserWindow, ipcMain} = require('electron'); +const gotTheLock = app.requestSingleInstanceLock() +const argv = require('yargs').argv; + + +const connectionInfo = { + token: argv.token || "token", + projectAPIKey: argv.projectAPIKey || "projectAPIKey", + sessionID: argv.sessionID || "sessionID" +} + + +// Mac and PC need slightly different width and height sizes. +const osType = require('os').type(); +let width; +let height; +if (osType == "Darwin" || osType == "Linux") { + width = 960; + height = 660; +} else if (osType == "Windows_NT") { + width = 973; + height = 740; +} + + +if (!gotTheLock) { + console.log("Another instance of the screenshare is already running - this instance will quit."); + app.exit(0); + return; +} + +let window; +const zoomFactor = 1.0; +function createWindow(){ + window = new BrowserWindow({ + backgroundColor: "#000000", + width: width, + height: height, + center: true, + frame: true, + useContentSize: true, + zoomFactor: zoomFactor, + resizable: false, + webPreferences: { + nodeIntegration: true + }, + icon: __dirname + `/resources/interface.png`, + skipTaskbar: false, + title: "Screen share" + }); + window.loadURL('file://' + __dirname + '/screenshareApp.html'); + window.setMenu(null); + + window.webContents.on("did-finish-load", () => { + window.webContents.send('connectionInfo', JSON.stringify(connectionInfo)); + }); +} + + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +app.on('ready', function() { + createWindow(); + window.webContents.send('connectionInfo', JSON.stringify(connectionInfo)) +}); diff --git a/screenshare/src/styles.css b/screenshare/src/styles.css new file mode 100644 index 0000000000..78dca032fa --- /dev/null +++ b/screenshare/src/styles.css @@ -0,0 +1,217 @@ +body { + background-color: black; + box-sizing: border-box; + font-family: "Graphik"; + margin: 0px 22px 10px 22px; + } + +#confirmation_screen { + width: 100%; + display: flex; + text-align: center; + justify-content: center; + align-items: center; +} + +#confirmation_text { + margin-top: 65px; + font-size: 25px; + line-height: 25px; +} + +#confirmation_text p { + margin: 0px; +} + +#button_selection { + margin-top: 25px; + width: 100%; + display: flex; + text-align: center; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.button_confirmation { + margin: 4px; + cursor: pointer; + width: 300px; + height: 32px; + line-height: 32px; + text-align: center; + vertical-align: middle; + color: white +} + +.grey_background { + background-color: #191919; +} + +#no { + color: rgba(1, 152, 203,0.7); +} + +#no:hover { + color: rgba(1, 152, 203,1); +} + +#yes { + outline: solid white 2px; +} + +#yes:hover { + background: #0198CB; +} + +yes:hover + #yes_background { + display: block; +} + +#share_pick { + margin-top: 60px; +} + +.text_title { + color: white; +} + +@font-face { + font-family: "Graphik"; + src: url("./resources/Graphik-Regular.ttf"); +} + +#title { + margin-top: 21px; +} + +h1 { + line-height: 48px; + font-size: 48px; + margin: 0px; +} + +h3 { + line-height: 24px; + font-size: 24px; + margin: 9px 0px 0px 0px; +} + +#publisher { + visibility: hidden; + width: 0px; + height: 0px; + bottom: 10px; + left: 10px; + z-index: 100; + border: 3px solid white; + border-radius: 3px; +} + +#selects { + margin-right: 19px; + padding-left: 3px; +} + +.screen_label { + max-width: 220px; + font-size: 25px; + line-height: 25px; + color: white; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.icon { + display: inline-block; + background: #000000; + width: 20px; + height: 20px; + margin-right: 15px; +} + +.box { + height: 165px; + width: 265px; + display: inline-block; + margin-left: 35px; + margin-top: 60px; + cursor: pointer; +} + +.box:nth-child(1) { + margin-top: 0 !important; +} + +.box:nth-child(2) { + margin-top: 0 !important; +} + +.box:nth-child(3) { + margin-top: 0 !important; +} + +.box:nth-child(3n) { + margin-right: 0 !important; +} + +.box:nth-child(3n+1) { + margin-left: 0 !important; +} + +.heading { + height: 35px; + display: flex; + align-items: center; +} + +.image { + width: 265px; + height: 165px; + max-height: 165px; + max-width: 265px; + margin: 0px; +} + +.image:hover { + outline: solid white 3px; +} + +.image_no_hover { + width: 265px; + height: 165px; + max-height: 165px; + max-width: 265px; +} + +img { + width: 265px; + height: 165px; + margin: 0px; +} + +.scrollbar { + float: right; + height: 500px; + width: 100%; + overflow-y: scroll; + margin-top: 40px; +} + +#style-1::-webkit-scrollbar { + width: 9px; + overflow: scroll; + overflow-x: hidden; +} + +#style-1::-webkit-scrollbar-thumb { + background-color: #0198CB; + width: 9px; +} + +#style-1::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); + background-color: #848484; + width: 9px; +} diff --git a/script-archive/afk.js b/script-archive/afk.js index 5977c6384a..46441b8886 100644 --- a/script-archive/afk.js +++ b/script-archive/afk.js @@ -6,7 +6,7 @@ // Copyright 2015 High Fidelity, Inc. // kevintown.net // -// JavaScript for the High Fidelity interface that creates an away from keyboard functionality by providing a UI and keyPressEvent which will mute toggle the connected microphone, face tracking dde and set the avatar to a hand raise pose. +// JavaScript for the Vircadia interface that creates an away from keyboard functionality by providing a UI and keyPressEvent which will mute toggle the connected microphone, face tracking dde and set the avatar to a hand raise pose. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/script-archive/tests/performance/renderableMatrix.js b/script-archive/tests/performance/renderableMatrix.js index 04946328cc..2108e35519 100644 --- a/script-archive/tests/performance/renderableMatrix.js +++ b/script-archive/tests/performance/renderableMatrix.js @@ -114,7 +114,7 @@ Script.setInterval(function () { if (isModel) { properties.modelURL = type; } else if (type === 'Web') { - properties.sourceUrl = 'https://projectathena.io'; + properties.sourceUrl = 'https://vircadia.com'; } else { properties.color = { red: x / ROWS_X * 255, green: y / ROWS_Y * 255, blue: z / ROWS_Z * 255 }; if (type === 'ParticleEffect') { diff --git a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js index 1232bfc843..f518701eb1 100644 --- a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js +++ b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 1 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 1 entity script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js index 31d5e00e00..921c0508ee 100644 --- a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js +++ b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 1 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 1 interface script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js index 66d2e96858..ba16e8af5b 100644 --- a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js +++ b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 2 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 2 entity script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js index 19fd67f6c4..e55e7f9d43 100644 --- a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js +++ b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 2 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 2 interface script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js index f811d95315..faccec5b5c 100644 --- a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js +++ b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 3 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 3 entity script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js index 6b77b2609a..0cde1cbd9c 100644 --- a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js +++ b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 3 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 3 interface script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/scripts/communityModules/chat/FloofChat.html b/scripts/communityScripts/chat/FloofChat.html similarity index 80% rename from scripts/communityModules/chat/FloofChat.html rename to scripts/communityScripts/chat/FloofChat.html index a1e84e132f..b493b05090 100644 --- a/scripts/communityModules/chat/FloofChat.html +++ b/scripts/communityScripts/chat/FloofChat.html @@ -12,6 +12,7 @@ + @@ -28,11 +29,19 @@
+ +
+ +
@@ -46,6 +55,7 @@ - - - - - - - - - - - - - - - -
-
- -
-
-
-
-
-
-
-
+ + Properties + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+
+
+
+ + + + + +
+
+
+
+ +
+
- -
- - + diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index e581fbd194..182dddf817 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1,8 +1,8 @@ // entityProperties.js // // Created by Ryan Huffman on 13 Nov 2014 -// Modified by David Back on 19 Oct 2018 // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -10,6 +10,8 @@ /* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge, JSONEditor, openEventBridge, setTimeout, window, _, $ */ +var currentTab = "base"; + const DEGREES_TO_RADIANS = Math.PI / 180.0; const NO_SELECTION = ","; @@ -24,7 +26,7 @@ const PROPERTY_SELECTION_VISIBILITY = Object.freeze({ SINGLE_SELECTION: 1, MULTIPLE_SELECTIONS: 2, MULTI_DIFF_SELECTIONS: 4, - ANY_SELECTIONS: 7, /* SINGLE_SELECTION | MULTIPLE_SELECTIONS | MULTI_DIFF_SELECTIONS */ + ANY_SELECTIONS: 7 /* SINGLE_SELECTION | MULTIPLE_SELECTIONS | MULTI_DIFF_SELECTIONS */ }); // Multiple-selection behavior @@ -34,12 +36,13 @@ const PROPERTY_MULTI_DISPLAY_MODE = Object.freeze({ * Comma separated values * Limited for properties with type "string" or "textarea" and readOnly enabled */ - COMMA_SEPARATED_VALUES: 1, + COMMA_SEPARATED_VALUES: 1 }); const GROUPS = [ { id: "base", + label: "ENTITY", properties: [ { label: NO_SELECTION, @@ -113,11 +116,16 @@ const GROUPS = [ }, propertyID: "primitiveMode", }, + { + label: "Render With Zones", + type: "multipleZonesSelection", + propertyID: "renderWithZones", + } ] }, { id: "shape", - addToGroup: "base", + label: "SHAPE", properties: [ { label: "Shape", @@ -133,11 +141,21 @@ const GROUPS = [ type: "color", propertyID: "color", }, + { + label: "Alpha", + type: "number-draggable", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "shapeAlpha", + propertyName: "alpha", + }, ] }, { id: "text", - addToGroup: "base", + label: "TEXT", properties: [ { label: "Text", @@ -181,6 +199,36 @@ const GROUPS = [ unit: "m", propertyID: "lineHeight", }, + { + label: "Font", + type: "string", + propertyID: "font", + }, + { + label: "Effect", + type: "dropdown", + options: { + none: "None", + outline: "Outline", + "outline fill": "Outline with fill", + shadow: "Shadow" + }, + propertyID: "textEffect", + }, + { + label: "Effect Color", + type: "color", + propertyID: "textEffectColor", + }, + { + label: "Effect Thickness", + type: "number-draggable", + min: 0.0, + max: 0.5, + step: 0.01, + decimals: 2, + propertyID: "textEffectThickness", + }, { label: "Billboard Mode", type: "dropdown", @@ -220,12 +268,12 @@ const GROUPS = [ label: "Unlit", type: "bool", propertyID: "unlit", - }, + } ] }, { id: "zone", - addToGroup: "base", + label: "ZONE", properties: [ { label: "Shape Type", @@ -255,7 +303,13 @@ const GROUPS = [ label: "Filter", type: "string", propertyID: "filterURL", - }, + } + ] + }, + { + id: "zone_key_light", + label: "ZONE KEY LIGHT", + properties: [ { label: "Key Light", type: "dropdown", @@ -272,7 +326,7 @@ const GROUPS = [ { label: "Light Intensity", type: "number-draggable", - min: 0, + min: -40, max: 40, step: 0.01, decimals: 2, @@ -324,7 +378,13 @@ const GROUPS = [ decimals: 2, propertyID: "keyLight.shadowMaxDistance", showPropertyRule: { "keyLightMode": "enabled" }, - }, + } + ] + }, + { + id: "zone_skybox", + label: "ZONE SKYBOX", + properties: [ { label: "Skybox", type: "dropdown", @@ -342,7 +402,13 @@ const GROUPS = [ type: "string", propertyID: "skybox.url", showPropertyRule: { "skyboxMode": "enabled" }, - }, + } + ] + }, + { + id: "zone_ambient_light", + label: "ZONE AMBIENT LIGHT", + properties: [ { label: "Ambient Light", type: "dropdown", @@ -352,7 +418,7 @@ const GROUPS = [ { label: "Ambient Intensity", type: "number-draggable", - min: 0, + min: -200, max: 200, step: 0.1, decimals: 2, @@ -371,7 +437,13 @@ const GROUPS = [ className: "black", onClick: copySkyboxURLToAmbientURL } ], propertyID: "copyURLToAmbient", showPropertyRule: { "ambientLightMode": "enabled" }, - }, + } + ] + }, + { + id: "zone_haze", + label: "ZONE HAZE", + properties: [ { label: "Haze", type: "dropdown", @@ -454,7 +526,13 @@ const GROUPS = [ decimals: 0, propertyID: "haze.hazeGlareAngle", showPropertyRule: { "hazeMode": "enabled" }, - }, + } + ] + }, + { + id: "zone_bloom", + label: "ZONE BLOOM", + properties: [ { label: "Bloom", type: "dropdown", @@ -490,19 +568,30 @@ const GROUPS = [ decimals: 3, propertyID: "bloom.bloomSize", showPropertyRule: { "bloomMode": "enabled" }, - }, + } + ] + }, + { + id: "zone_avatar_priority", + label: "ZONE AVATAR PRIORITY", + properties: [ { label: "Avatar Priority", type: "dropdown", options: { inherit: "Inherit", crowd: "Crowd", hero: "Hero" }, propertyID: "avatarPriority", }, - + { + label: "Screen-share", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "screenshare", + } ] }, { id: "model", - addToGroup: "base", + label: "MODEL", properties: [ { label: "Model", @@ -542,7 +631,7 @@ const GROUPS = [ propertyID: "animation.loop", }, { - label: "Allow Transition", + label: "Allow Translation", type: "bool", propertyID: "animation.allowTranslation", }, @@ -587,12 +676,12 @@ const GROUPS = [ label: "Group Culled", type: "bool", propertyID: "groupCulled", - }, + } ] }, { id: "image", - addToGroup: "base", + label: "IMAGE", properties: [ { label: "Image", @@ -606,6 +695,16 @@ const GROUPS = [ propertyID: "imageColor", propertyName: "color", // actual entity property name }, + { + label: "Alpha", + type: "number-draggable", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "imageAlpha", + propertyName: "alpha", + }, { label: "Emissive", type: "bool", @@ -630,12 +729,12 @@ const GROUPS = [ label: "Keep Aspect Ratio", type: "bool", propertyID: "keepAspectRatio", - }, + } ] }, { id: "web", - addToGroup: "base", + label: "WEB", properties: [ { label: "Source", @@ -670,17 +769,38 @@ const GROUPS = [ decimals: 0, propertyID: "maxFPS", }, + { + label: "Billboard Mode", + type: "dropdown", + options: { none: "None", yaw: "Yaw", full: "Full"}, + propertyID: "webBillboardMode", + propertyName: "billboardMode", // actual entity property name + }, + { + label: "Input Mode", + type: "dropdown", + options: { + touch: "Touch events", + mouse: "Mouse events" + }, + propertyID: "inputMode", + }, + { + label: "Focus Highlight", + type: "bool", + propertyID: "showKeyboardFocusHighlight", + }, { label: "Script URL", type: "string", propertyID: "scriptURL", placeholder: "URL", - }, + } ] }, { id: "light", - addToGroup: "base", + label: "LIGHT", properties: [ { label: "Light Color", @@ -691,7 +811,7 @@ const GROUPS = [ { label: "Intensity", type: "number-draggable", - min: 0, + min: -1000, max: 10000, step: 0.1, decimals: 2, @@ -726,12 +846,12 @@ const GROUPS = [ step: 0.01, decimals: 2, propertyID: "cutoff", - }, + } ] }, { id: "material", - addToGroup: "base", + label: "MATERIAL", properties: [ { label: "Material URL", @@ -800,12 +920,12 @@ const GROUPS = [ label: "Material Repeat", type: "bool", propertyID: "materialRepeat", - }, + } ] }, { id: "grid", - addToGroup: "base", + label: "GRID", properties: [ { label: "Color", @@ -833,12 +953,12 @@ const GROUPS = [ step: 0.01, decimals: 2, propertyID: "minorGridEvery", - }, + } ] }, { id: "particles", - addToGroup: "base", + label: "PARTICLES", properties: [ { label: "Emit", @@ -864,13 +984,12 @@ const GROUPS = [ type: "texture", propertyID: "particleTextures", propertyName: "textures", // actual entity property name - }, + } ] }, { id: "particles_emit", - label: "EMIT", - isMinor: true, + label: "PARTICLES EMIT", properties: [ { label: "Emit Rate", @@ -937,13 +1056,12 @@ const GROUPS = [ label: "Trails", type: "bool", propertyID: "emitterShouldTrail", - }, + } ] }, { id: "particles_size", - label: "SIZE", - isMinor: true, + label: "PARTICLES SIZE", properties: [ { type: "triple", @@ -972,7 +1090,7 @@ const GROUPS = [ decimals: 2, propertyID: "radiusFinish", fallbackProperty: "particleRadius", - }, + } ] }, { @@ -981,13 +1099,12 @@ const GROUPS = [ step: 0.01, decimals: 2, propertyID: "radiusSpread", - }, + } ] }, { id: "particles_color", - label: "COLOR", - isMinor: true, + label: "PARTICLES COLOR", properties: [ { type: "triple", @@ -1011,7 +1128,7 @@ const GROUPS = [ type: "color", propertyID: "colorFinish", fallbackProperty: "color", - }, + } ] }, { @@ -1019,13 +1136,6 @@ const GROUPS = [ type: "color", propertyID: "colorSpread", }, - ] - }, - { - id: "particles_alpha", - label: "ALPHA", - isMinor: true, - properties: [ { type: "triple", label: "Alpha", @@ -1053,7 +1163,7 @@ const GROUPS = [ decimals: 3, propertyID: "alphaFinish", fallbackProperty: "alpha", - }, + } ] }, { @@ -1062,13 +1172,12 @@ const GROUPS = [ step: 0.001, decimals: 3, propertyID: "alphaSpread", - }, + } ] }, { - id: "particles_acceleration", - label: "ACCELERATION", - isMinor: true, + id: "particles_behavior", + label: "PARTICLES BEHAVIOR", properties: [ { label: "Emit Acceleration", @@ -1088,13 +1197,6 @@ const GROUPS = [ subLabels: [ "x", "y", "z" ], propertyID: "accelerationSpread", }, - ] - }, - { - id: "particles_spin", - label: "SPIN", - isMinor: true, - properties: [ { type: "triple", label: "Spin", @@ -1128,7 +1230,7 @@ const GROUPS = [ unit: "deg", propertyID: "spinFinish", fallbackProperty: "particleSpin", - }, + } ] }, { @@ -1144,13 +1246,12 @@ const GROUPS = [ label: "Rotate with Entity", type: "bool", propertyID: "rotateWithEntity", - }, + } ] }, { id: "particles_constraints", - label: "CONSTRAINTS", - isMinor: true, + label: "PARTICLES CONSTRAINTS", properties: [ { type: "triple", @@ -1174,7 +1275,7 @@ const GROUPS = [ multiplier: DEGREES_TO_RADIANS, unit: "deg", propertyID: "polarFinish", - }, + } ], }, { @@ -1199,7 +1300,7 @@ const GROUPS = [ multiplier: DEGREES_TO_RADIANS, unit: "deg", propertyID: "azimuthFinish", - }, + } ] } ] @@ -1299,7 +1400,7 @@ const GROUPS = [ buttons: [ { id: "selection", label: "Selection to Grid", className: "black", onClick: moveSelectionToGrid }, { id: "all", label: "All to Grid", className: "black", onClick: moveAllToGrid } ], propertyID: "alignToGrid", - }, + } ] }, { @@ -1369,6 +1470,18 @@ const GROUPS = [ type: "bool", propertyID: "ignorePickIntersection", }, + { + label: "Lifetime", + type: "number", + unit: "s", + propertyID: "lifetime", + } + ] + }, + { + id: "scripts", + label: "SCRIPTS", + properties: [ { label: "Script", type: "string", @@ -1391,12 +1504,6 @@ const GROUPS = [ propertyID: "serverScriptStatus", selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, }, - { - label: "Lifetime", - type: "number", - unit: "s", - propertyID: "lifetime", - }, { label: "User Data", type: "textarea", @@ -1404,7 +1511,7 @@ const GROUPS = [ { id: "edit", label: "Edit as JSON", className: "blue", onClick: newJSONEditor }, { id: "save", label: "Save User Data", className: "black", onClick: saveUserData } ], propertyID: "userData", - }, + } ] }, { @@ -1469,7 +1576,7 @@ const GROUPS = [ label: "Dynamic", type: "bool", propertyID: "dynamic", - }, + } ] }, { @@ -1554,27 +1661,28 @@ const GROUPS = [ decimals: 4, unit: "m/s2", propertyID: "acceleration", - }, + } ] }, ]; const GROUPS_PER_TYPE = { - None: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], - Shape: [ 'base', 'shape', 'spatial', 'behavior', 'collision', 'physics' ], - Text: [ 'base', 'text', 'spatial', 'behavior', 'collision', 'physics' ], - Zone: [ 'base', 'zone', 'spatial', 'behavior', 'physics' ], - Model: [ 'base', 'model', 'spatial', 'behavior', 'collision', 'physics' ], - Image: [ 'base', 'image', 'spatial', 'behavior', 'collision', 'physics' ], - Web: [ 'base', 'web', 'spatial', 'behavior', 'collision', 'physics' ], - Light: [ 'base', 'light', 'spatial', 'behavior', 'collision', 'physics' ], - Material: [ 'base', 'material', 'spatial', 'behavior' ], - ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', 'particles_alpha', - 'particles_acceleration', 'particles_spin', 'particles_constraints', 'spatial', 'behavior', 'physics' ], - PolyLine: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], - PolyVox: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], - Grid: [ 'base', 'grid', 'spatial', 'behavior', 'physics' ], - Multiple: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], + None: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Shape: [ 'base', 'shape', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Text: [ 'base', 'text', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', + 'zone_bloom', 'zone_avatar_priority', 'spatial', 'behavior', 'scripts', 'physics' ], + Model: [ 'base', 'model', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Image: [ 'base', 'image', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Web: [ 'base', 'web', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Light: [ 'base', 'light', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Material: [ 'base', 'material', 'spatial', 'behavior', 'scripts', 'physics' ], + ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', + 'particles_behavior', 'particles_constraints', 'spatial', 'behavior', 'scripts', 'physics' ], + PolyLine: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + PolyVox: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], + Grid: [ 'base', 'grid', 'spatial', 'behavior', 'scripts', 'physics' ], + Multiple: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], }; const EDITOR_TIMEOUT_DURATION = 1500; @@ -1641,7 +1749,7 @@ let selectedEntityIDs = new Set(); let currentSelections = []; let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; - +let zonesList = []; function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); @@ -1658,7 +1766,7 @@ function isFlagSet(value, flag) { */ function getPropertyInputElement(propertyID) { - let property = properties[propertyID]; + let property = properties[propertyID]; switch (property.data.type) { case 'string': case 'number': @@ -1667,6 +1775,8 @@ function getPropertyInputElement(propertyID) { case 'textarea': case 'texture': return property.elInput; + case 'multipleZonesSelection': + return property.elInput; case 'number-draggable': return property.elNumber.elInput; case 'rect': @@ -1707,12 +1817,7 @@ function disableChildren(el, selector) { function enableProperties() { enableChildren(document.getElementById("properties-list"), ENABLE_DISABLE_SELECTOR); enableChildren(document, ".colpick"); - - let elLocked = getPropertyInputElement("locked"); - if (elLocked.checked === false) { - removeStaticUserData(); - removeStaticMaterialData(); - } + enableAllMultipleZoneSelector(); } function disableProperties() { @@ -1721,16 +1826,7 @@ function disableProperties() { for (let pickKey in colorPickers) { colorPickers[pickKey].colpickHide(); } - - let elLocked = getPropertyInputElement("locked"); - if (elLocked.checked === true) { - if ($('#property-userData-editor').css('display') === "block") { - showStaticUserData(); - } - if ($('#property-materialData-editor').css('display') === "block") { - showStaticMaterialData(); - } - } + disableAllMultipleZoneSelector(); } function showPropertyElement(propertyID, show) { @@ -1743,7 +1839,7 @@ function setPropertyVisibility(property, visible) { function resetProperties() { for (let propertyID in properties) { - let property = properties[propertyID]; + let property = properties[propertyID]; let propertyData = property.data; switch (propertyData.type) { @@ -1805,6 +1901,12 @@ function resetProperties() { setTextareaScrolling(property.elInput); break; } + case 'multipleZonesSelection': { + property.elInput.classList.remove('multi-diff'); + property.elInput.value = "[]"; + setZonesSelectionData(property.elInput, false); + break; + } case 'icon': { property.elSpan.style.display = "none"; break; @@ -1820,7 +1922,7 @@ function resetProperties() { break; } } - + let showPropertyRules = properties[propertyID].showPropertyRules; if (showPropertyRules !== undefined) { for (let propertyToHide in showPropertyRules) { @@ -1842,9 +1944,15 @@ function resetServerScriptStatus() { function showGroupsForType(type) { if (type === "Box" || type === "Sphere") { showGroupsForTypes(["Shape"]); + showOnTheSamePage(["Shape"]); return; } + if (type === "None") { + showGroupsForTypes(["None"]); + return; + } showGroupsForTypes([type]); + showOnTheSamePage([type]); } function getGroupsForTypes(types) { @@ -1858,9 +1966,15 @@ function getGroupsForTypes(types) { function showGroupsForTypes(types) { Object.entries(elGroups).forEach(([groupKey, elGroup]) => { if (types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { return hasGroup; })) { - elGroup.style.display = "block"; + elGroup.style.display = "none"; + if (types !== "None") { + document.getElementById("tab-" + groupKey).style.display = "block"; + } else { + document.getElementById("tab-" + groupKey).style.display = "none"; + } } else { elGroup.style.display = "none"; + document.getElementById("tab-" + groupKey).style.display = "none"; } }); } @@ -2257,7 +2371,7 @@ function updateCheckedSubProperty(propertyName, propertyMultiValue, subPropertyE * PROPERTY ELEMENT CREATION FUNCTIONS */ -function createStringProperty(property, elProperty) { +function createStringProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; @@ -2270,12 +2384,12 @@ function createStringProperty(property, elProperty) { ${propertyData.readOnly ? 'readonly' : ''}/> `); - + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); if (propertyData.onChange !== undefined) { elInput.addEventListener('change', propertyData.onChange); } - + let elMultiDiff = document.createElement('span'); elMultiDiff.className = "multi-diff"; @@ -2286,7 +2400,7 @@ function createStringProperty(property, elProperty) { if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, false); } - + return elInput; } @@ -2294,9 +2408,9 @@ function createBoolProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - + elProperty.className = "checkbox"; - + if (propertyData.glyph !== undefined) { let elSpan = document.createElement('span'); elSpan.innerHTML = propertyData.glyph; @@ -2525,7 +2639,7 @@ function createVec3Property(property, elProperty) { let propertyData = property.data; elProperty.className = propertyData.vec3Type + " fstuple"; - + let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); let elNumberZ = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER]); @@ -2540,7 +2654,7 @@ function createVec3Property(property, elProperty) { elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); elNumberZ.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'z')); - + let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; @@ -2548,11 +2662,11 @@ function createVec3Property(property, elProperty) { return elResult; } -function createVec2Property(property, elProperty) { +function createVec2Property(property, elProperty) { let propertyData = property.data; elProperty.className = propertyData.vec2Type + " fstuple"; - + let elTuple = document.createElement('div'); elTuple.className = "tuple"; @@ -2589,19 +2703,19 @@ function createColorProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - + elProperty.className += " rgb fstuple"; - + let elColorPicker = document.createElement('div'); elColorPicker.className = "color-picker"; elColorPicker.setAttribute("id", elementID); - + let elTuple = document.createElement('div'); elTuple.className = "tuple"; - + elProperty.appendChild(elColorPicker); elProperty.appendChild(elTuple); - + if (propertyData.min === undefined) { propertyData.min = COLOR_MIN; } @@ -2611,19 +2725,19 @@ function createColorProperty(property, elProperty) { if (propertyData.step === undefined) { propertyData.step = COLOR_STEP; } - + let elNumberR = createTupleNumberInput(property, "red"); let elNumberG = createTupleNumberInput(property, "green"); let elNumberB = createTupleNumberInput(property, "blue"); elTuple.appendChild(elNumberR.elDiv); elTuple.appendChild(elNumberG.elDiv); elTuple.appendChild(elNumberB.elDiv); - + let valueChangeFunction = createEmitColorPropertyUpdateFunction(property); elNumberR.setValueChangeFunction(valueChangeFunction); elNumberG.setValueChangeFunction(valueChangeFunction); elNumberB.setValueChangeFunction(valueChangeFunction); - + let colorPickerID = "#" + elementID; colorPickers[colorPickerID] = $(colorPickerID).colpick({ colorScheme: 'dark', @@ -2652,7 +2766,7 @@ function createColorProperty(property, elProperty) { } } }); - + let elResult = []; elResult[COLOR_ELEMENTS.COLOR_PICKER] = elColorPicker; elResult[COLOR_ELEMENTS.RED_NUMBER] = elNumberR; @@ -2677,26 +2791,26 @@ function createDropdownProperty(property, propertyID, elProperty) { option.text = propertyData.options[optionKey]; elInput.add(option); } - + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); - + return elInput; } -function createTextareaProperty(property, elProperty) { +function createTextareaProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - + elProperty.className = "textarea"; - + let elInput = document.createElement('textarea'); elInput.setAttribute("id", elementID); if (propertyData.readOnly) { elInput.readOnly = true; - } - + } + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); let elMultiDiff = document.createElement('span'); @@ -2704,42 +2818,42 @@ function createTextareaProperty(property, elProperty) { elProperty.appendChild(elInput); elProperty.appendChild(elMultiDiff); - + if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, true); } - + return elInput; } -function createIconProperty(property, elProperty) { +function createIconProperty(property, elProperty) { let elementID = property.elementID; elProperty.className = "value"; - + let elSpan = document.createElement('span'); elSpan.setAttribute("id", elementID + "-icon"); elSpan.className = 'icon'; elProperty.appendChild(elSpan); - + return elSpan; } -function createTextureProperty(property, elProperty) { +function createTextureProperty(property, elProperty) { let elementID = property.elementID; - + elProperty.className = "texture"; - + let elDiv = document.createElement("div"); let elImage = document.createElement("img"); elDiv.className = "texture-image no-texture"; elDiv.appendChild(elImage); - + let elInput = document.createElement('input'); elInput.setAttribute("id", elementID); elInput.setAttribute("type", "text"); - + let imageLoad = function(url) { elDiv.style.display = null; if (url.slice(0, 5).toLowerCase() === "atp:/") { @@ -2776,7 +2890,7 @@ function createTextureProperty(property, elProperty) { elMultiDiff.className = "multi-diff"; elProperty.appendChild(elMultiDiff); elProperty.appendChild(elDiv); - + let elResult = []; elResult[TEXTURE_ELEMENTS.IMAGE] = elImage; elResult[TEXTURE_ELEMENTS.TEXT_INPUT] = elInput; @@ -2792,23 +2906,23 @@ function createButtonsProperty(property, elProperty) { if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, false); } - + return elProperty; } function createDynamicMultiselectProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - + elProperty.className = "dynamic-multiselect"; - + let elDivOptions = document.createElement('div'); elDivOptions.setAttribute("id", elementID + "-options"); elDivOptions.style = "overflow-y:scroll;max-height:160px;"; - + let elDivButtons = document.createElement('div'); elDivButtons.setAttribute("id", elDivOptions.getAttribute("id") + "-buttons"); - + let elLabel = document.createElement('label'); elLabel.innerText = "No Options"; elDivOptions.appendChild(elLabel); @@ -2816,10 +2930,10 @@ function createDynamicMultiselectProperty(property, elProperty) { let buttons = [ { id: "selectAll", label: "Select All", className: "black", onClick: selectAllMaterialTarget }, { id: "clearAll", label: "Clear All", className: "black", onClick: clearAllMaterialTarget } ]; addButtons(elDivButtons, elementID, buttons, false); - + elProperty.appendChild(elDivOptions); elProperty.appendChild(elDivButtons); - + return elDivOptions; } @@ -2837,13 +2951,13 @@ function createTupleNumberInput(property, subLabel) { let propertyElementID = property.elementID; let propertyData = property.data; let elementID = propertyElementID + "-" + subLabel.toLowerCase(); - + let elLabel = document.createElement('label'); elLabel.className = "sublabel " + subLabel; elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); elLabel.setAttribute("for", elementID); elLabel.style.visibility = "visible"; - + let dragStartFunction = createDragStartFunction(property); let dragEndFunction = createDragEndFunction(property); let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, @@ -2851,14 +2965,14 @@ function createTupleNumberInput(property, subLabel) { elDraggableNumber.elInput.setAttribute("id", elementID); elDraggableNumber.elDiv.className += " fstuple"; elDraggableNumber.elDiv.insertBefore(elLabel, elDraggableNumber.elLeftArrow); - + return elDraggableNumber; } function addButtons(elProperty, propertyID, buttons, newRow) { let elDiv = document.createElement('div'); elDiv.className = "row"; - + buttons.forEach(function(button) { let elButton = document.createElement('input'); elButton.className = button.className; @@ -2880,7 +2994,7 @@ function addButtons(elProperty, propertyID, buttons, newRow) { } function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { - let property = { + let property = { data: propertyData, elementID: propertyElementID, name: propertyName, @@ -2942,6 +3056,10 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI property.elInput = createTextareaProperty(property, elProperty); break; } + case 'multipleZonesSelection': { + property.elInput = createZonesSelection(property, elProperty); + break; + } case 'icon': { property.elSpan = createIconProperty(property, elProperty); break; @@ -3171,19 +3289,6 @@ function hideUserDataSaved() { $('#property-userData-saved').hide(); } -function showStaticUserData() { - if (editor !== null) { - let $propertyUserDataStatic = $('#property-userData-static'); - $propertyUserDataStatic.show(); - $propertyUserDataStatic.css('height', $('#property-userData-editor').height()); - $propertyUserDataStatic.text(editor.getText()); - } -} - -function removeStaticUserData() { - $('#property-userData-static').hide(); -} - function setEditorJSON(json) { editor.set(json); if (editor.hasOwnProperty('expandAll')) { @@ -3336,19 +3441,6 @@ function hideMaterialDataSaved() { $('#property-materialData-saved').hide(); } -function showStaticMaterialData() { - if (materialEditor !== null) { - let $propertyMaterialDataStatic = $('#property-materialData-static'); - $propertyMaterialDataStatic.show(); - $propertyMaterialDataStatic.css('height', $('#property-materialData-editor').height()); - $propertyMaterialDataStatic.text(materialEditor.getText()); - } -} - -function removeStaticMaterialData() { - $('#property-materialData-static').hide(); -} - function setMaterialEditorJSON(json) { materialEditor.set(json); if (materialEditor.hasOwnProperty('expandAll')) { @@ -3460,6 +3552,175 @@ function setTextareaScrolling(element) { element.setAttribute("scrolling", isScrolling ? "true" : "false"); } +/** + * ZONE SELECTOR FUNCTIONS + */ + +function enableAllMultipleZoneSelector() { + let allMultiZoneSelectors = document.querySelectorAll(".hiddenMultiZonesSelection"); + let i, propId; + for (i = 0; i < allMultiZoneSelectors.length; i++) { + propId = allMultiZoneSelectors[i].id; + displaySelectedZones(propId, true); + } +} + +function disableAllMultipleZoneSelector() { + let allMultiZoneSelectors = document.querySelectorAll(".hiddenMultiZonesSelection"); + let i, propId; + for (i = 0; i < allMultiZoneSelectors.length; i++) { + propId = allMultiZoneSelectors[i].id; + displaySelectedZones(propId, false); + } +} + +function requestZoneList() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "zoneListRequest" + })); +} + +function addZoneToZonesSelection(propertyId) { + let hiddenField = document.getElementById(propertyId); + if (JSON.stringify(hiddenField.value) === '"undefined"') { + hiddenField.value = "[]"; + } + let selectedZones = JSON.parse(hiddenField.value); + let zoneToAdd = document.getElementById("zones-select-" + propertyId).value; + if (!selectedZones.includes(zoneToAdd)) { + selectedZones.push(zoneToAdd); + } + hiddenField.value = JSON.stringify(selectedZones); + displaySelectedZones(propertyId, true); + let propertyName = propertyId.replace("property-", ""); + updateProperty(propertyName, selectedZones, false); +} + +function removeZoneFromZonesSelection(propertyId, zoneId) { + let hiddenField = document.getElementById(propertyId); + if (JSON.stringify(hiddenField.value) === '"undefined"') { + hiddenField.value = "[]"; + } + let selectedZones = JSON.parse(hiddenField.value); + let index = selectedZones.indexOf(zoneId); + if (index > -1) { + selectedZones.splice(index, 1); + } + hiddenField.value = JSON.stringify(selectedZones); + displaySelectedZones(propertyId, true); + let propertyName = propertyId.replace("property-", ""); + updateProperty(propertyName, selectedZones, false); +} + +function displaySelectedZones(propertyId, isEditable) { + let i,j, name, listedZoneInner, hiddenData, isMultiple; + hiddenData = document.getElementById(propertyId).value; + if (JSON.stringify(hiddenData) === '"undefined"') { + isMultiple = true; + hiddenData = "[]"; + } else { + isMultiple = false; + } + let selectedZones = JSON.parse(hiddenData); + listedZoneInner = ""; + if (selectedZones.length === 0) { + if (!isMultiple) { + listedZoneInner += ""; + } else { + listedZoneInner += ""; + } + } else { + for (i = 0; i < selectedZones.length; i++) { + name = "{ERROR: NOT FOUND}"; + for (j = 0; j < zonesList.length; j++) { + if (selectedZones[i] === zonesList[j].id) { + if (zonesList[j].name !== "") { + name = zonesList[j].name; + } else { + name = zonesList[j].id; + } + break; + } + } + if (isEditable) { + listedZoneInner += ""; + } else { + listedZoneInner += ""; + } + } + } + listedZoneInner += "
  
[ WARNING: Any changes will apply to all. ] 
" + name + ""; + listedZoneInner += "
" + name + " 
"; + document.getElementById("selected-zones-" + propertyId).innerHTML = listedZoneInner; + if (isEditable) { + document.getElementById("multiZoneSelTools-" + propertyId).style.display = "block"; + } else { + document.getElementById("multiZoneSelTools-" + propertyId).style.display = "none"; + } +} + +function createZonesSelection(property, elProperty) { + let elementID = property.elementID; + requestZoneList(); + elProperty.className = "multipleZonesSelection"; + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "hidden"); + elInput.className = "hiddenMultiZonesSelection"; + + let elZonesSelector = document.createElement('div'); + elZonesSelector.setAttribute("id", "zones-selector-" + elementID); + + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + + elProperty.appendChild(elInput); + elProperty.appendChild(elZonesSelector); + elProperty.appendChild(elMultiDiff); + + return elInput; +} + +function setZonesSelectionData(element, isEditable) { + let zoneSelectorContainer = document.getElementById("zones-selector-" + element.id); + let zoneSelector = "
 "; + zoneSelector += "
"; + zoneSelector += "
"; + zoneSelectorContainer.innerHTML = zoneSelector; + displaySelectedZones(element.id, isEditable); +} + +function updateAllZoneSelect() { + let allZoneSelects = document.querySelectorAll(".zoneSelect"); + let i, j, name, propId; + for (i = 0; i < allZoneSelects.length; i++) { + allZoneSelects[i].options.length = 0; + for (j = 0; j < zonesList.length; j++) { + if (zonesList[j].name === "") { + name = zonesList[j].id; + } else { + name = zonesList[j].name; + } + allZoneSelects[i].options[j] = new Option(name, zonesList[j].id, false , false); + } + propId = allZoneSelects[i].id.replace("zones-select-", ""); + if (document.getElementById("multiZoneSelTools-" + propId).style.display === "block") { + displaySelectedZones(propId, true); + } else { + displaySelectedZones(propId, false); + } + } +} /** * MATERIAL TARGET FUNCTIONS @@ -3471,15 +3732,15 @@ function requestMaterialTarget() { entityID: getFirstSelectedID(), })); } - + function setMaterialTargetData(materialTargetData) { let elDivOptions = getPropertyInputElement("parentMaterialName"); resetDynamicMultiselectProperty(elDivOptions); - + if (materialTargetData === undefined) { return; } - + elDivOptions.firstChild.style.display = "none"; // hide "No Options" text elDivOptions.parentNode.lastChild.style.display = null; // show Select/Clear all buttons @@ -3487,7 +3748,7 @@ function setMaterialTargetData(materialTargetData) { for (let i = 0; i < numMeshes; ++i) { addMaterialTarget(elDivOptions, i, false); } - + let materialNames = materialTargetData.materialNames; let materialNamesAdded = []; for (let i = 0; i < materialNames.length; ++i) { @@ -3497,7 +3758,7 @@ function setMaterialTargetData(materialTargetData) { materialNamesAdded.push(materialName); } } - + materialTargetPropertyUpdate(elDivOptions.propertyValue); } @@ -3505,12 +3766,12 @@ function addMaterialTarget(elDivOptions, targetID, isMaterialName) { let elementID = elDivOptions.getAttribute("id"); elementID += isMaterialName ? "-material-" : "-mesh-"; elementID += targetID; - + let elDiv = document.createElement('div'); elDiv.className = "materialTargetDiv"; elDiv.onclick = onToggleMaterialTarget; elDivOptions.appendChild(elDiv); - + let elInput = document.createElement('input'); elInput.className = "materialTargetInput"; elInput.setAttribute("type", "checkbox"); @@ -3518,12 +3779,12 @@ function addMaterialTarget(elDivOptions, targetID, isMaterialName) { elInput.setAttribute("targetID", targetID); elInput.setAttribute("isMaterialName", isMaterialName); elDiv.appendChild(elInput); - + let elLabel = document.createElement('label'); elLabel.setAttribute("for", elementID); elLabel.innerText = isMaterialName ? "Material " + targetID : "Mesh Index " + targetID; elDiv.appendChild(elLabel); - + return elDiv; } @@ -3556,7 +3817,7 @@ function clearAllMaterialTarget() { function sendMaterialTargetProperty() { let elDivOptions = getPropertyInputElement("parentMaterialName"); let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - + let materialTargetList = []; for (let i = 0; i < elInputs.length; ++i) { let elInput = elInputs[i]; @@ -3569,26 +3830,26 @@ function sendMaterialTargetProperty() { } } } - + let propertyValue = materialTargetList.join(","); if (propertyValue.length > 1) { propertyValue = "[" + propertyValue + "]"; } - + updateProperty("parentMaterialName", propertyValue, false); } function materialTargetPropertyUpdate(propertyValue) { let elDivOptions = getPropertyInputElement("parentMaterialName"); let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - + if (propertyValue.startsWith('[')) { propertyValue = propertyValue.substring(1, propertyValue.length); } if (propertyValue.endsWith(']')) { propertyValue = propertyValue.substring(0, propertyValue.length - 1); } - + let materialTargets = propertyValue.split(","); for (let i = 0; i < elInputs.length; ++i) { let elInput = elInputs[i]; @@ -3599,7 +3860,7 @@ function materialTargetPropertyUpdate(propertyValue) { } elInput.checked = materialTargets.indexOf(materialTargetName) >= 0; } - + elDivOptions.propertyValue = propertyValue; } @@ -3633,7 +3894,9 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { selectedEntityIDs = new Set(selections.map(selection => selection.id)); const multipleSelections = currentSelections.length > 1; const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); - + + requestZoneList(); + if (selections.length === 0) { deleteJSONEditor(); deleteJSONMaterialEditor(); @@ -3678,6 +3941,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { const shownGroups = getGroupsForTypes(entityTypes); showGroupsForTypes(entityTypes); + showOnTheSamePage(entityTypes); const lockedMultiValue = getMultiplePropertyValue('locked'); @@ -3687,7 +3951,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { enableProperties(); disableSaveUserDataButton(); - disableSaveMaterialDataButton() + disableSaveMaterialDataButton(); } const certificateIDMultiValue = getMultiplePropertyValue('certificateID'); @@ -3843,6 +4107,15 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { setTextareaScrolling(property.elInput); break; } + case 'multipleZonesSelection': { + property.elInput.value = JSON.stringify(propertyValue); + if (lockedMultiValue.isMultiDiffValue || lockedMultiValue.value) { + setZonesSelectionData(property.elInput, false); + } else { + setZonesSelectionData(property.elInput, true); + } + break; + } case 'icon': { property.elSpan.innerHTML = propertyData.icons[propertyValue]; property.elSpan.style.display = "inline-block"; @@ -3888,7 +4161,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } } - if (json !== null) { + if (json !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) { if (editor === null) { createJSONEditor(); } @@ -3920,7 +4193,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } } - if (materialJson !== null) { + if (materialJson !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) { if (materialEditor === null) { createJSONMaterialEditor(); } @@ -3954,44 +4227,35 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { function loaded() { openEventBridge(function() { - let elPropertiesList = document.getElementById("properties-list"); - + let elPropertiesList = document.getElementById("properties-pages"); + let elTabs = document.getElementById("tabs"); + GROUPS.forEach(function(group) { let elGroup; - if (group.addToGroup !== undefined) { - let fieldset = document.getElementById("properties-" + group.addToGroup); - elGroup = document.createElement('div'); - fieldset.appendChild(elGroup); - } else { - elGroup = document.createElement('div'); - elGroup.className = 'section ' + (group.isMinor ? "minor" : "major"); - elGroup.setAttribute("id", "properties-" + group.id); - elPropertiesList.appendChild(elGroup); - } + + elGroup = document.createElement('div'); + elGroup.className = 'section ' + "major"; + elGroup.setAttribute("id", "properties-" + group.id); + elPropertiesList.appendChild(elGroup); if (group.label !== undefined) { let elLegend = document.createElement('div'); - elLegend.className = "section-header"; - - elLegend.appendChild(createElementFromHTML(`
${group.label}
`)); - - let elSpan = document.createElement('span'); - elSpan.className = "collapse-icon"; - elSpan.innerText = "M"; - elLegend.appendChild(elSpan); + elLegend.className = "tab-section-header"; + elLegend.appendChild(createElementFromHTML(`
${group.label}
`)); elGroup.appendChild(elLegend); + elTabs.appendChild(createElementFromHTML('')); } - + group.properties.forEach(function(propertyData) { let propertyType = propertyData.type; - let propertyID = propertyData.propertyID; + let propertyID = propertyData.propertyID; let propertyName = propertyData.propertyName !== undefined ? propertyData.propertyName : propertyID; let propertySpaceMode = propertyData.spaceMode !== undefined ? propertyData.spaceMode : PROPERTY_SPACE_MODE.ALL; let propertyElementID = "property-" + propertyID; propertyElementID = propertyElementID.replace('.', '-'); - + let elContainer, elLabel; - + if (propertyData.replaceID === undefined) { // Create subheader, or create new property and append it. if (propertyType === "sub-header") { @@ -4081,15 +4345,15 @@ function loaded() { property.elContainer = elContainer; property.spaceMode = propertySpaceMode; property.group_id = group.id; - + if (property.type !== 'placeholder') { properties[propertyID] = property; - } + } if (propertyData.type === 'number' || propertyData.type === 'number-draggable' || propertyData.type === 'vec2' || propertyData.type === 'vec3' || propertyData.type === 'rect') { propertyRangeRequests.push(propertyID); } - + let showPropertyRule = propertyData.showPropertyRule; if (showPropertyRule !== undefined) { let dependentProperty = Object.keys(showPropertyRule)[0]; @@ -4104,15 +4368,12 @@ function loaded() { } } }); - + elGroups[group.id] = elGroup; }); - let minorSections = document.querySelectorAll(".section.minor"); - minorSections[minorSections.length - 1].className += " last"; - updateVisibleSpaceModeProperties(); - + if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); @@ -4184,6 +4445,9 @@ function loaded() { if (data.entityID === getFirstSelectedID()) { setMaterialTargetData(data.materialTargetData); } + } else if (data.type === 'zoneListRequest') { + zonesList = data.zones; + updateAllZoneSelect(); } }); @@ -4191,7 +4455,7 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'tooltipsRequest' })); EventBridge.emitWebEvent(JSON.stringify({ type: 'propertyRangeRequest', properties: propertyRangeRequests })); } - + // Server Script Status let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); let elServerScriptStatusContainer = document.getElementById('div-property-serverScriptStatus').childNodes[1]; @@ -4200,7 +4464,7 @@ function loaded() { let elServerScriptStatus = document.createElement('div'); elServerScriptStatus.setAttribute("id", serverScriptStatusElementID); elServerScriptStatusContainer.appendChild(elServerScriptStatus); - + // Server Script Error let elServerScripts = getPropertyInputElement("serverScripts"); let elDiv = document.createElement('div'); @@ -4210,18 +4474,16 @@ function loaded() { elServerScriptError.setAttribute("id", serverScriptErrorElementID); elDiv.appendChild(elServerScriptError); elServerScriptStatusContainer.appendChild(elDiv); - + let elScript = getPropertyInputElement("script"); elScript.parentNode.className = "url refresh"; elServerScripts.parentNode.className = "url refresh"; - + // User Data let userDataProperty = properties["userData"]; let elUserData = userDataProperty.elInput; let userDataElementID = userDataProperty.elementID; elDiv = elUserData.parentNode; - let elStaticUserData = document.createElement('div'); - elStaticUserData.setAttribute("id", userDataElementID + "-static"); let elUserDataEditor = document.createElement('div'); elUserDataEditor.setAttribute("id", userDataElementID + "-editor"); let elUserDataEditorStatus = document.createElement('div'); @@ -4230,17 +4492,14 @@ function loaded() { elUserDataSaved.setAttribute("id", userDataElementID + "-saved"); elUserDataSaved.innerText = "Saved!"; elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elUserDataSaved); - elDiv.insertBefore(elStaticUserData, elUserData); elDiv.insertBefore(elUserDataEditor, elUserData); elDiv.insertBefore(elUserDataEditorStatus, elUserData); - + // Material Data let materialDataProperty = properties["materialData"]; let elMaterialData = materialDataProperty.elInput; let materialDataElementID = materialDataProperty.elementID; elDiv = elMaterialData.parentNode; - let elStaticMaterialData = document.createElement('div'); - elStaticMaterialData.setAttribute("id", materialDataElementID + "-static"); let elMaterialDataEditor = document.createElement('div'); elMaterialDataEditor.setAttribute("id", materialDataElementID + "-editor"); let elMaterialDataEditorStatus = document.createElement('div'); @@ -4249,26 +4508,9 @@ function loaded() { elMaterialDataSaved.setAttribute("id", materialDataElementID + "-saved"); elMaterialDataSaved.innerText = "Saved!"; elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elMaterialDataSaved); - elDiv.insertBefore(elStaticMaterialData, elMaterialData); elDiv.insertBefore(elMaterialDataEditor, elMaterialData); elDiv.insertBefore(elMaterialDataEditorStatus, elMaterialData); - - // Collapsible sections - let elCollapsible = document.getElementsByClassName("collapse-icon"); - let toggleCollapsedEvent = function(event) { - let element = this.parentNode.parentNode; - let isCollapsed = element.dataset.collapsed !== "true"; - element.dataset.collapsed = isCollapsed ? "true" : false; - element.setAttribute("collapsed", isCollapsed ? "true" : "false"); - this.textContent = isCollapsed ? "L" : "M"; - }; - - for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { - let curCollapsibleElement = elCollapsible[collapseIndex]; - curCollapsibleElement.addEventListener("click", toggleCollapsedEvent, true); - } - // Textarea scrollbars let elTextareas = document.getElementsByTagName("TEXTAREA"); @@ -4285,7 +4527,7 @@ function loaded() { event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ curTextAreaElement.addEventListener("mouseup", textareaOnChangeEvent, false); } - + // Dropdowns // For each dropdown the following replacement is created in place of the original dropdown... // Structure created: @@ -4297,7 +4539,7 @@ function loaded() { //
  • ...
  • // // - // + // let elDropdowns = document.getElementsByTagName("select"); for (let dropDownIndex = 0; dropDownIndex < elDropdowns.length; ++dropDownIndex) { let elDropdown = elDropdowns[dropDownIndex]; @@ -4346,7 +4588,7 @@ function loaded() { li.addEventListener("click", setDropdownValue); ul.appendChild(li); } - + let propertyID = elDropdown.getAttribute("propertyID"); let property = properties[propertyID]; property.elInput = dt; @@ -4407,14 +4649,14 @@ function loaded() { } })); }, false); - + window.onblur = function() { // Fake a change event let ev = document.createEvent("HTMLEvents"); ev.initEvent("change", true, true); document.activeElement.dispatchEvent(ev); }; - + // For input and textarea elements, select all of the text on focus let els = document.querySelectorAll("input, textarea"); for (let i = 0; i < els.length; ++i) { @@ -4422,12 +4664,14 @@ function loaded() { e.target.select(); }; } - - bindAllNonJSONEditorElements(); + + bindAllNonJSONEditorElements(); showGroupsForType("None"); + showPage("base"); resetProperties(); - disableProperties(); + disableProperties(); + }); augmentSpinButtons(); @@ -4442,3 +4686,30 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'propertiesPageReady' })); }, 1000); } + +function showOnTheSamePage(entityType) { + let numberOfTypes = entityType.length; + let matchingType = 0; + for (let i = 0; i < numberOfTypes; i++) { + if (GROUPS_PER_TYPE[entityType[i]].includes(currentTab)) { + matchingType = matchingType + 1; + } + } + if (matchingType !== numberOfTypes) { + currentTab = "base"; + } + showPage(currentTab); +} + +function showPage(id) { + currentTab = id; + Object.entries(elGroups).forEach(([groupKey, elGroup]) => { + if (groupKey === id) { + elGroup.style.display = "block"; + document.getElementById("tab-" + groupKey).style.backgroundColor = "#2E2E2E"; + } else { + elGroup.style.display = "none"; + document.getElementById("tab-" + groupKey).style.backgroundColor = "#404040"; + } + }); +} diff --git a/scripts/system/create/entityProperties/html/tabs/base.png b/scripts/system/create/entityProperties/html/tabs/base.png new file mode 100644 index 0000000000..9f656abe27 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/base.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/behavior.png b/scripts/system/create/entityProperties/html/tabs/behavior.png new file mode 100644 index 0000000000..12662b8a1d Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/behavior.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/collision.png b/scripts/system/create/entityProperties/html/tabs/collision.png new file mode 100644 index 0000000000..c9bed39385 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/collision.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/grid.png b/scripts/system/create/entityProperties/html/tabs/grid.png new file mode 100644 index 0000000000..d9b8afb1ae Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/grid.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/image.png b/scripts/system/create/entityProperties/html/tabs/image.png new file mode 100644 index 0000000000..ebd03648f6 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/image.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/light.png b/scripts/system/create/entityProperties/html/tabs/light.png new file mode 100644 index 0000000000..bed097d54e Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/light.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/material.png b/scripts/system/create/entityProperties/html/tabs/material.png new file mode 100644 index 0000000000..458c6bad48 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/material.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/model.png b/scripts/system/create/entityProperties/html/tabs/model.png new file mode 100644 index 0000000000..79aa6b3830 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/model.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles.png b/scripts/system/create/entityProperties/html/tabs/particles.png new file mode 100644 index 0000000000..6a0d47cacb Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles_alpha.png b/scripts/system/create/entityProperties/html/tabs/particles_alpha.png new file mode 100644 index 0000000000..8df2981f14 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles_alpha.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles_behavior.png b/scripts/system/create/entityProperties/html/tabs/particles_behavior.png new file mode 100644 index 0000000000..6be9f90638 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles_behavior.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles_color.png b/scripts/system/create/entityProperties/html/tabs/particles_color.png new file mode 100644 index 0000000000..ac66a902cf Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles_color.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles_constraints.png b/scripts/system/create/entityProperties/html/tabs/particles_constraints.png new file mode 100644 index 0000000000..9c783acc9b Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles_constraints.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles_emit.png b/scripts/system/create/entityProperties/html/tabs/particles_emit.png new file mode 100644 index 0000000000..223baa5d56 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles_emit.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/particles_size.png b/scripts/system/create/entityProperties/html/tabs/particles_size.png new file mode 100644 index 0000000000..b51fe65cdf Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/particles_size.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/physics.png b/scripts/system/create/entityProperties/html/tabs/physics.png new file mode 100644 index 0000000000..f0fc451d37 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/physics.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/scripts.png b/scripts/system/create/entityProperties/html/tabs/scripts.png new file mode 100644 index 0000000000..2249af165b Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/scripts.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/shape.png b/scripts/system/create/entityProperties/html/tabs/shape.png new file mode 100644 index 0000000000..5f3722caf7 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/shape.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/spatial.png b/scripts/system/create/entityProperties/html/tabs/spatial.png new file mode 100644 index 0000000000..a280d0e822 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/spatial.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/text.png b/scripts/system/create/entityProperties/html/tabs/text.png new file mode 100644 index 0000000000..405d8e4104 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/text.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/web.png b/scripts/system/create/entityProperties/html/tabs/web.png new file mode 100644 index 0000000000..c1fc573619 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/web.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone.png b/scripts/system/create/entityProperties/html/tabs/zone.png new file mode 100644 index 0000000000..276ba26799 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_ambient_light.png b/scripts/system/create/entityProperties/html/tabs/zone_ambient_light.png new file mode 100644 index 0000000000..ff01b16aaf Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_ambient_light.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_avatar_priority.png b/scripts/system/create/entityProperties/html/tabs/zone_avatar_priority.png new file mode 100644 index 0000000000..e91111fb9b Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_avatar_priority.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_bloom.png b/scripts/system/create/entityProperties/html/tabs/zone_bloom.png new file mode 100644 index 0000000000..925654df81 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_bloom.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_haze.png b/scripts/system/create/entityProperties/html/tabs/zone_haze.png new file mode 100644 index 0000000000..0cf96692f8 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_haze.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_key_light.png b/scripts/system/create/entityProperties/html/tabs/zone_key_light.png new file mode 100644 index 0000000000..6527c65320 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_key_light.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_skybox.png b/scripts/system/create/entityProperties/html/tabs/zone_skybox.png new file mode 100644 index 0000000000..17697a817b Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_skybox.png differ diff --git a/scripts/system/create/entitySelectionTool/entitySelectionTool.js b/scripts/system/create/entitySelectionTool/entitySelectionTool.js index 6774c72627..e224e28fca 100644 --- a/scripts/system/create/entitySelectionTool/entitySelectionTool.js +++ b/scripts/system/create/entitySelectionTool/entitySelectionTool.js @@ -6,6 +6,7 @@ // Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017 // Modified by David Back on 1/9/2018 // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors // // This script implements a class useful for building tools for editing entities. // @@ -31,6 +32,46 @@ function deepCopy(v) { return JSON.parse(JSON.stringify(v)); } +function getDuplicateAppendedName(name) { + var appendiceChar = "(0123456789)"; + var currentChar = ""; + var rippedName = name; + var existingSequence = 0; + var sequenceReader = ""; + for (var i = name.length - 1; i >= 0; i--) { + currentChar = name.charAt(i); + if (i === (name.length - 1) && currentChar !== ")") { + rippedName = name; + break; + } else { + if (appendiceChar.indexOf(currentChar) === -1) { + rippedName = name; + break; + } else { + if (currentChar == "(" && i === name.length - 2) { + rippedName = name; + break; + } else { + if (currentChar == "(") { + rippedName = name.substr(0, i-1); + existingSequence = parseInt(sequenceReader); + break; + } else { + sequenceReader = currentChar + sequenceReader; + } + } + } + } + } + if (existingSequence === 0) { + rippedName = rippedName.trim() + " (2)"; + } else { + existingSequence++; + rippedName = rippedName.trim() + " (" + existingSequence + ")"; + } + return rippedName.trim(); +} + SelectionManager = (function() { var that = {}; @@ -289,6 +330,8 @@ SelectionManager = (function() { properties.localRotation = properties.rotation; } + properties.name = getDuplicateAppendedName(properties.name); + properties.localVelocity = Vec3.ZERO; properties.localAngularVelocity = Vec3.ZERO; @@ -1118,8 +1161,9 @@ SelectionDisplay = (function() { return false; } - // No action if the Alt key is pressed. - if (event.isAlt) { + // No action if the Alt key is pressed unless on Mac. + var isMac = Controller.getValue(Controller.Hardware.Application.PlatformMac); + if (event.isAlt && !isMac) { return; } @@ -2044,10 +2088,11 @@ SelectionDisplay = (function() { Vec3.print(" pickResult.intersection", pickResult.intersection); } - // Duplicate entities if Ctrl is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isControl || doDuplicate) { + // Duplicate entities if Ctrl is pressed on Windows or Alt is press on Mac. + // This will make a copy of the selected entities and move the _original_ entities, not the new ones. + var isMac = Controller.getValue(Controller.Hardware.Application.PlatformMac); + var isDuplicate = isMac ? event.isAlt : event.isControl; + if (isDuplicate || doDuplicate) { duplicatedEntityIDs = SelectionManager.duplicateSelection(); var ids = []; for (var i = 0; i < duplicatedEntityIDs.length; ++i) { @@ -2270,10 +2315,11 @@ SelectionDisplay = (function() { addHandleTool(overlay, { mode: mode, onBegin: function(event, pickRay, pickResult) { - // Duplicate entities if Ctrl is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isControl) { + // Duplicate entities if Ctrl is pressed on Windows or Alt is pressed on Mac. + // This will make a copy of the selected entities and move the _original_ entities, not the new ones. + var isMac = Controller.getValue(Controller.Hardware.Application.PlatformMac); + var isDuplicate = isMac ? event.isAlt : event.isControl; + if (isDuplicate) { duplicatedEntityIDs = SelectionManager.duplicateSelection(); var ids = []; for (var i = 0; i < duplicatedEntityIDs.length; ++i) { diff --git a/scripts/system/graphicsSettings.js b/scripts/system/graphicsSettings.js new file mode 100644 index 0000000000..df556a91b1 --- /dev/null +++ b/scripts/system/graphicsSettings.js @@ -0,0 +1,70 @@ +// +// graphicsSettings.js +// +// Created by Kalila L. on 8/5/2020 +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + + var AppUi = Script.require('appUi'); + + // cellphone-cog MDI + // var customIcon = 'data:image/svg+xml;utf8,' + + // application-cog MDI + var customIcon = 'data:image/svg+xml;utf8,' + var lqIcon = 'data:image/svg+xml;utf8,'; + var mqIcon = 'data:image/svg+xml;utf8,'; + var hqIcon = 'data:image/svg+xml;utf8,'; + + var BUTTON_NAME = "GRAPHICS"; + var GRAPHICS_QML_SOURCE = "hifi/dialogs/graphics/GraphicsSettings.qml"; + var ui; + + function getIcon() { + // TODO: Implement only once AppUi can be told to constantly retrieve / reset icons... + // var performanceProfile = Performance.getPerformancePreset(); + // + // switch (performanceProfile) { + // case 0: + // return customIcon; + // break; + // case 1: + // return lqIcon; + // break; + // case 2: + // return mqIcon; + // break; + // case 3: + // return hqIcon; + // break; + // default: + // return customIcon; + // } + return customIcon; + } + + function startup() { + ui = new AppUi({ + buttonName: BUTTON_NAME, + sortOrder: 8, + normalButton: getIcon(), + activeButton: getIcon().replace('white', 'black'), + home: GRAPHICS_QML_SOURCE + }); + } + + function shutdown() { + } + + // + // Run the functions. + // + startup(); + Script.scriptEnding.connect(shutdown); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 470e57ad6d..c72456d414 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -3,6 +3,7 @@ // // Created by Ryan Huffman on 13 Nov 2014 // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -1794,3 +1795,28 @@ input[type=number].hide-spinner::-webkit-inner-spin-button { div.jsoneditor-menu a.jsoneditor-poweredBy { display: none; } +td.zoneItem { + width: 100%; +} + +select.zoneSelect { + clear: both; + cursor: pointer; + font-family: FiraSans-SemiBold; + font-size: 15px; + width: 90%; + height: 28px; + padding: 0 28px 0 12px; + color: #afafaf; + background: #575757; + position: relative; + display: inline; + border: 0px; + align-items: center; + outline: none; +} + +div.multiZoneSelToolbar { + padding: 0px; +} + diff --git a/scripts/system/html/css/img/add_icon.png b/scripts/system/html/css/img/add_icon.png new file mode 100644 index 0000000000..f60e5561f2 Binary files /dev/null and b/scripts/system/html/css/img/add_icon.png differ diff --git a/scripts/system/html/css/img/remove_icon.png b/scripts/system/html/css/img/remove_icon.png new file mode 100644 index 0000000000..7a34bd0304 Binary files /dev/null and b/scripts/system/html/css/img/remove_icon.png differ diff --git a/scripts/system/html/css/miniHandTablet.css b/scripts/system/html/css/miniHandTablet.css new file mode 100644 index 0000000000..ef2c27ff14 --- /dev/null +++ b/scripts/system/html/css/miniHandTablet.css @@ -0,0 +1,56 @@ +/* +miniTablet.css + +Copyright 2019 High Fidelity, Inc. + +Distributed under the Apache License, Version 2.0. +See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + +* { + box-sizing: border-box; + padding: 0; + margin: 0; + user-select: none; +} + +html { + background-color: #404040; +} + +body { + height: 100%; +} + +section { + background-color: #404040; + position: relative; + padding: 32px 0px; +} + +.button { + text-align: center; +} + +img { + width: 149px; + height: 149px; +} + +#expand { + width: 149px; + height: 149px; + background-size: 100% 100%; + background-image: url("./img/mt-expand-normal.svg"); +} + +#expand:hover { + background-image: url("./img/mt-expand-hover.svg"); +} + +#expand:hover.unhover { + background-image: url("./img/mt-expand-normal.svg"); +} + +#expand img { +} diff --git a/scripts/system/html/css/tabs.css b/scripts/system/html/css/tabs.css new file mode 100644 index 0000000000..6abd910300 --- /dev/null +++ b/scripts/system/html/css/tabs.css @@ -0,0 +1,77 @@ +/* +// tabs.css +// +// Created by Alezia Kurdis on 27 Feb 2020 +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + +div.tabsContainer { + float: left; + width: 32px; + padding: 0px; +} + +.tabsContainer button { + padding: 4px; + text-align: center; + cursor: pointer; + transition: 0.4s; + font-size: 14px; + background-color: #404040; + border-color: #404040; + border-width: 1px 0px 1px 1px; + border-radius: 5px 0px 0px 5px; + outline: none; +} + +.tabsContainer button:hover { + background-color: #575757; +} + +.tabsContainer button.active { + background-color: #2E2E2E; +} + +div.labelTabHeader { + font-size: 20px; + font-weight: 700; + height: 40px; + color: #ffffff; +} + +div.tab-section-header { + width: 100%; + padding: 5px; +} + +table.tabsTableFrame { + width: 100%; + min-height: 352px; + display: block; +} + +tr.tabsTrFrame { + width: 100%; +} + +td.tabsFrame { + width: 32px; + vertical-align: top; + background-color: #575757; + padding: 0px; + border: 0px; +} + +td.tabsPropertiesFrame { + width: 100%; + vertical-align: top; + border: 0px; +} + +div.tabsPropertiesPage { + min-height: 352px; + display: block; +} diff --git a/scripts/system/html/js/miniTablet.js b/scripts/system/html/js/miniTablet.js index c48201cef5..b02c6ae213 100644 --- a/scripts/system/html/js/miniTablet.js +++ b/scripts/system/html/js/miniTablet.js @@ -35,7 +35,9 @@ function setUnhover() { if (!isUnhover) { - gotoButton.classList.add("unhover"); + if (gotoButton) { + gotoButton.classList.add("unhover"); + } expandButton.classList.add("unhover"); isUnhover = true; } @@ -43,7 +45,9 @@ function clearUnhover() { if (isUnhover) { - gotoButton.classList.remove("unhover"); + if (gotoButton) { + gotoButton.classList.remove("unhover"); + } expandButton.classList.remove("unhover"); isUnhover = false; } @@ -62,10 +66,14 @@ switch (message.type) { case MUTE_MESSAGE: - muteImage.src = message.icon; + if (muteImage) { + muteImage.src = message.icon; + } break; case GOTO_MESSAGE: - gotoImage.src = message.icon; + if (gotoImage) { + gotoImage.src = message.icon; + } break; } } @@ -130,9 +138,7 @@ function onLoad() { muteButton = document.getElementById("mute"); - muteImage = document.getElementById("mute-img"); gotoButton = document.getElementById("goto"); - gotoImage = document.getElementById("goto-img"); expandButton = document.getElementById("expand"); connectEventBridge(); @@ -140,11 +146,19 @@ document.body.addEventListener("mouseenter", onBodyHover, false); document.body.addEventListener("mouseleave", onBodyUnhover, false); - muteButton.addEventListener("mouseenter", onButtonHover, false); - gotoButton.addEventListener("mouseenter", onButtonHover, false); + if (muteButton) { + muteImage = document.getElementById("mute-img"); + muteButton.addEventListener("mouseenter", onButtonHover, false); + muteButton.addEventListener("click", onMuteButtonClick, true); + } + + if (gotoButton) { + gotoImage = document.getElementById("goto-img"); + gotoButton.addEventListener("mouseenter", onButtonHover, false); + gotoButton.addEventListener("click", onGotoButtonClick, true); + } + expandButton.addEventListener("mouseenter", onButtonHover, false); - muteButton.addEventListener("click", onMuteButtonClick, true); - gotoButton.addEventListener("click", onGotoButtonClick, true); expandButton.addEventListener("click", onExpandButtonClick, true); document.body.onunload = function () { diff --git a/scripts/system/html/miniHandsTablet.html b/scripts/system/html/miniHandsTablet.html new file mode 100644 index 0000000000..1d140797f4 --- /dev/null +++ b/scripts/system/html/miniHandsTablet.html @@ -0,0 +1,26 @@ + + + + + + + + + + +
    +
    + +
    +
    + + + diff --git a/scripts/system/inspect.js b/scripts/system/inspect.js index 17260a4358..9aacfd14c2 100644 --- a/scripts/system/inspect.js +++ b/scripts/system/inspect.js @@ -123,10 +123,11 @@ function orientationOf(vector) { } function handleRadialMode(dx, dy) { + var MIN_INSPECT_RADIUS = 0.1; azimuth += dx / AZIMUTH_RATE; radius += radius * dy * RADIUS_RATE; - if (radius < 1) { - radius = 1; + if (radius < MIN_INSPECT_RADIUS) { + radius = MIN_INSPECT_RADIUS; } vector = { diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 2d225fd2a6..23cff7699a 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -48,7 +48,6 @@ "Tip: Don't want others invading your personal space? Turn on the Bubble!", "Tip: Want to make a friend? Shake hands with them in VR!", "Tip: Enjoy live music? Visit Rust to dance your heart out!", - "Tip: Have you visited BodyMart to check out the new avatars recently?", "Tip: Use the Create app to import models and create custom entities.", "Tip: We're open source! Feel free to contribute to our code on GitHub!", "Tip: What emotes have you used in the Emote app?", diff --git a/scripts/system/inventory/.gitignore b/scripts/system/inventory/.gitignore new file mode 100644 index 0000000000..c2b66ff67b --- /dev/null +++ b/scripts/system/inventory/.gitignore @@ -0,0 +1,20 @@ +.DS_Store +node_modules + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/scripts/system/inventory/README.md b/scripts/system/inventory/README.md new file mode 100644 index 0000000000..bba10a92ea --- /dev/null +++ b/scripts/system/inventory/README.md @@ -0,0 +1,24 @@ +# inventory + +## Project setup +``` +npm install +``` + +### Compiles and hot-reloads for development +``` +npm run serve +``` + +### Compiles and minifies for production +``` +npm run build +``` + +### Lints and fixes files +``` +npm run lint +``` + +### Customize configuration +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/scripts/system/inventory/babel.config.js b/scripts/system/inventory/babel.config.js new file mode 100644 index 0000000000..e9558405fd --- /dev/null +++ b/scripts/system/inventory/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/scripts/system/inventory/dist/css/app.a93e8b1f.css b/scripts/system/inventory/dist/css/app.a93e8b1f.css new file mode 100644 index 0000000000..a2bdb2246f --- /dev/null +++ b/scripts/system/inventory/dist/css/app.a93e8b1f.css @@ -0,0 +1 @@ +.draggable-card{background-color:#272727;margin:5px 0}.draggable-card .handle{width:40px!important}.top-level-folder{background-color:#272727}.top-level-folder .v-list-group__header__prepend-icon{background-color:rgba(0,0,0,.3);width:50px;height:50px;margin:5px 5px 7px 0!important;padding:5px 18px 5px 8px}.top-level-folder .handle{width:40px!important}.top-level-folder .folder-icon{margin-right:10px}.top-level-folder .folder-button{font-size:.795rem!important}.v-list-group .column-item{max-width:100%!important;margin-top:5px;margin-bottom:5px}.v-list-group .draggable-card{background-color:rgba(0,0,0,.3);padding-right:16px;padding-left:0!important}.v-list-group .draggable-card .handle{margin-right:16px}.app-version{text-align:center;color:hsla(0,0%,100%,.6);font-weight:lighter}.handle{background-color:rgba(0,0,0,.3)}.inventoryApp::-webkit-scrollbar{width:0!important} \ No newline at end of file diff --git a/scripts/system/inventory/dist/css/chunk-vendors.8540aa41.css b/scripts/system/inventory/dist/css/chunk-vendors.8540aa41.css new file mode 100644 index 0000000000..c328a9511a --- /dev/null +++ b/scripts/system/inventory/dist/css/chunk-vendors.8540aa41.css @@ -0,0 +1,5 @@ +.v-btn:not(.v-btn--outlined).accent,.v-btn:not(.v-btn--outlined).error,.v-btn:not(.v-btn--outlined).info,.v-btn:not(.v-btn--outlined).primary,.v-btn:not(.v-btn--outlined).secondary,.v-btn:not(.v-btn--outlined).success,.v-btn:not(.v-btn--outlined).warning{color:#fff}.theme--light.v-btn{color:rgba(0,0,0,.87)}.theme--light.v-btn.v-btn--disabled,.theme--light.v-btn.v-btn--disabled .v-btn__loading,.theme--light.v-btn.v-btn--disabled .v-icon{color:rgba(0,0,0,.26)!important}.theme--light.v-btn.v-btn--disabled:not(.v-btn--flat):not(.v-btn--text):not(.v-btn--outlined){background-color:rgba(0,0,0,.12)!important}.theme--light.v-btn:not(.v-btn--flat):not(.v-btn--text):not(.v-btn--outlined){background-color:#f5f5f5}.theme--light.v-btn.v-btn--outlined.v-btn--text{border-color:rgba(0,0,0,.12)}.theme--light.v-btn.v-btn--icon{color:rgba(0,0,0,.54)}.theme--light.v-btn:hover:before{opacity:.04}.theme--light.v-btn--active:before,.theme--light.v-btn--active:hover:before,.theme--light.v-btn:focus:before{opacity:.12}.theme--light.v-btn--active:focus:before{opacity:.16}.theme--dark.v-btn{color:#fff}.theme--dark.v-btn.v-btn--disabled,.theme--dark.v-btn.v-btn--disabled .v-btn__loading,.theme--dark.v-btn.v-btn--disabled .v-icon{color:hsla(0,0%,100%,.3)!important}.theme--dark.v-btn.v-btn--disabled:not(.v-btn--flat):not(.v-btn--text):not(.v-btn--outlined){background-color:hsla(0,0%,100%,.12)!important}.theme--dark.v-btn:not(.v-btn--flat):not(.v-btn--text):not(.v-btn--outlined){background-color:#272727}.theme--dark.v-btn.v-btn--outlined.v-btn--text{border-color:hsla(0,0%,100%,.12)}.theme--dark.v-btn.v-btn--icon{color:#fff}.theme--dark.v-btn:hover:before{opacity:.08}.theme--dark.v-btn--active:before,.theme--dark.v-btn--active:hover:before,.theme--dark.v-btn:focus:before{opacity:.24}.theme--dark.v-btn--active:focus:before{opacity:.32}.v-btn{align-items:center;border-radius:4px;display:inline-flex;flex:0 0 auto;font-weight:500;letter-spacing:.0892857143em;justify-content:center;outline:0;position:relative;text-decoration:none;text-indent:.0892857143em;text-transform:uppercase;transition-duration:.28s;transition-property:box-shadow,transform,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;white-space:nowrap}.v-btn.v-size--x-small{font-size:.625rem}.v-btn.v-size--small{font-size:.75rem}.v-btn.v-size--default,.v-btn.v-size--large{font-size:.875rem}.v-btn.v-size--x-large{font-size:1rem}.v-btn:before{border-radius:inherit;bottom:0;color:inherit;content:"";left:0;opacity:0;pointer-events:none;position:absolute;right:0;top:0;transition:opacity .2s cubic-bezier(.4,0,.6,1);background-color:currentColor}.v-btn:not(.v-btn--disabled){will-change:box-shadow}.v-btn:not(.v-btn--round).v-size--x-small{height:20px;min-width:36px;padding:0 8.8888888889px}.v-btn:not(.v-btn--round).v-size--small{height:28px;min-width:50px;padding:0 12.4444444444px}.v-btn:not(.v-btn--round).v-size--default{height:36px;min-width:64px;padding:0 16px}.v-btn:not(.v-btn--round).v-size--large{height:44px;min-width:78px;padding:0 19.5555555556px}.v-btn:not(.v-btn--round).v-size--x-large{height:52px;min-width:92px;padding:0 23.1111111111px}.v-btn>.v-btn__content .v-icon{color:inherit}.v-btn__content{align-items:center;color:inherit;display:flex;flex:1 0 auto;justify-content:inherit;line-height:normal;position:relative}.v-btn__content .v-icon--left,.v-btn__content .v-icon--right{font-size:18px;height:18px;width:18px}.v-application--is-ltr .v-btn__content .v-icon--left{margin-left:-4px;margin-right:8px}.v-application--is-ltr .v-btn__content .v-icon--right,.v-application--is-rtl .v-btn__content .v-icon--left{margin-left:8px;margin-right:-4px}.v-application--is-rtl .v-btn__content .v-icon--right{margin-left:-4px;margin-right:8px}.v-btn__loader{align-items:center;display:flex;height:100%;justify-content:center;left:0;position:absolute;top:0;width:100%}.v-btn:not(.v-btn--text):not(.v-btn--outlined).v-btn--active:before{opacity:.18}.v-btn:not(.v-btn--text):not(.v-btn--outlined):hover:before{opacity:.08}.v-btn:not(.v-btn--text):not(.v-btn--outlined):focus:before{opacity:.24}.v-btn--absolute,.v-btn--fixed{position:absolute}.v-btn--absolute.v-btn--right,.v-btn--fixed.v-btn--right{right:16px}.v-btn--absolute.v-btn--left,.v-btn--fixed.v-btn--left{left:16px}.v-btn--absolute.v-btn--top,.v-btn--fixed.v-btn--top{top:16px}.v-btn--absolute.v-btn--bottom,.v-btn--fixed.v-btn--bottom{bottom:16px}.v-btn--block{display:flex;flex:1 0 auto;min-width:100%!important;max-width:auto}.v-btn--contained{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.v-btn--contained:after{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.v-btn--contained:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.v-btn--depressed{box-shadow:none!important}.v-btn--disabled{box-shadow:none;pointer-events:none}.v-btn--fab,.v-btn--icon{min-height:0;min-width:0;padding:0}.v-btn--fab.v-size--x-small .v-icon,.v-btn--icon.v-size--x-small .v-icon{height:18px;font-size:18px;width:18px}.v-btn--fab.v-size--default .v-icon,.v-btn--fab.v-size--small .v-icon,.v-btn--icon.v-size--default .v-icon,.v-btn--icon.v-size--small .v-icon{height:24px;font-size:24px;width:24px}.v-btn--fab.v-size--large .v-icon,.v-btn--icon.v-size--large .v-icon{height:28px;font-size:28px;width:28px}.v-btn--fab.v-size--x-large .v-icon,.v-btn--icon.v-size--x-large .v-icon{height:32px;font-size:32px;width:32px}.v-btn--icon.v-size--x-small{height:20px;width:20px}.v-btn--icon.v-size--small{height:28px;width:28px}.v-btn--icon.v-size--default{height:36px;width:36px}.v-btn--icon.v-size--large{height:44px;width:44px}.v-btn--icon.v-size--x-large{height:52px;width:52px}.v-btn--fab.v-btn--contained{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12)}.v-btn--fab.v-btn--contained:after{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.v-btn--fab.v-btn--contained:active{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 12px 17px 2px rgba(0,0,0,.14),0 5px 22px 4px rgba(0,0,0,.12)}.v-btn--fab.v-btn--absolute,.v-btn--fab.v-btn--fixed{z-index:4}.v-btn--fab.v-size--x-small{height:32px;width:32px}.v-btn--fab.v-size--x-small.v-btn--absolute.v-btn--bottom{bottom:-16px}.v-btn--fab.v-size--x-small.v-btn--absolute.v-btn--top{top:-16px}.v-btn--fab.v-size--small{height:40px;width:40px}.v-btn--fab.v-size--small.v-btn--absolute.v-btn--bottom{bottom:-20px}.v-btn--fab.v-size--small.v-btn--absolute.v-btn--top{top:-20px}.v-btn--fab.v-size--default{height:56px;width:56px}.v-btn--fab.v-size--default.v-btn--absolute.v-btn--bottom{bottom:-28px}.v-btn--fab.v-size--default.v-btn--absolute.v-btn--top{top:-28px}.v-btn--fab.v-size--large{height:64px;width:64px}.v-btn--fab.v-size--large.v-btn--absolute.v-btn--bottom{bottom:-32px}.v-btn--fab.v-size--large.v-btn--absolute.v-btn--top{top:-32px}.v-btn--fab.v-size--x-large{height:72px;width:72px}.v-btn--fab.v-size--x-large.v-btn--absolute.v-btn--bottom{bottom:-36px}.v-btn--fab.v-size--x-large.v-btn--absolute.v-btn--top{top:-36px}.v-btn--fixed{position:fixed}.v-btn--loading{pointer-events:none;transition:none}.v-btn--loading .v-btn__content{opacity:0}.v-btn--outlined{border:thin solid currentColor}.v-btn--outlined .v-btn__content .v-icon,.v-btn--round .v-btn__content .v-icon{color:currentColor}.v-btn--flat,.v-btn--outlined,.v-btn--text{background-color:transparent}.v-btn--outlined:before,.v-btn--round:before,.v-btn--rounded:before{border-radius:inherit}.v-btn--round{border-radius:50%}.v-btn--rounded{border-radius:28px}.v-btn--tile{border-radius:0}.theme--light.v-sheet{background-color:#fff;border-color:#fff;color:rgba(0,0,0,.87)}.theme--light.v-sheet--outlined{border:thin solid rgba(0,0,0,.12)}.theme--dark.v-sheet{background-color:#1e1e1e;border-color:#1e1e1e;color:#fff}.theme--dark.v-sheet--outlined{border:thin solid hsla(0,0%,100%,.12)}.v-sheet{border-radius:0}.v-sheet:not(.v-sheet--outlined){box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)}.v-sheet.v-sheet--shaped{border-radius:24px 0}@-webkit-keyframes v-shake{59%{margin-left:0}60%,80%{margin-left:2px}70%,90%{margin-left:-2px}}@keyframes v-shake{59%{margin-left:0}60%,80%{margin-left:2px}70%,90%{margin-left:-2px}}.v-application .black{background-color:#000!important;border-color:#000!important}.v-application .black--text{color:#000!important;caret-color:#000!important}.v-application .white{background-color:#fff!important;border-color:#fff!important}.v-application .white--text{color:#fff!important;caret-color:#fff!important}.v-application .transparent{background-color:transparent!important;border-color:transparent!important}.v-application .transparent--text{color:transparent!important;caret-color:transparent!important}.v-application .red{background-color:#f44336!important;border-color:#f44336!important}.v-application .red--text{color:#f44336!important;caret-color:#f44336!important}.v-application .red.lighten-5{background-color:#ffebee!important;border-color:#ffebee!important}.v-application .red--text.text--lighten-5{color:#ffebee!important;caret-color:#ffebee!important}.v-application .red.lighten-4{background-color:#ffcdd2!important;border-color:#ffcdd2!important}.v-application .red--text.text--lighten-4{color:#ffcdd2!important;caret-color:#ffcdd2!important}.v-application .red.lighten-3{background-color:#ef9a9a!important;border-color:#ef9a9a!important}.v-application .red--text.text--lighten-3{color:#ef9a9a!important;caret-color:#ef9a9a!important}.v-application .red.lighten-2{background-color:#e57373!important;border-color:#e57373!important}.v-application .red--text.text--lighten-2{color:#e57373!important;caret-color:#e57373!important}.v-application .red.lighten-1{background-color:#ef5350!important;border-color:#ef5350!important}.v-application .red--text.text--lighten-1{color:#ef5350!important;caret-color:#ef5350!important}.v-application .red.darken-1{background-color:#e53935!important;border-color:#e53935!important}.v-application .red--text.text--darken-1{color:#e53935!important;caret-color:#e53935!important}.v-application .red.darken-2{background-color:#d32f2f!important;border-color:#d32f2f!important}.v-application .red--text.text--darken-2{color:#d32f2f!important;caret-color:#d32f2f!important}.v-application .red.darken-3{background-color:#c62828!important;border-color:#c62828!important}.v-application .red--text.text--darken-3{color:#c62828!important;caret-color:#c62828!important}.v-application .red.darken-4{background-color:#b71c1c!important;border-color:#b71c1c!important}.v-application .red--text.text--darken-4{color:#b71c1c!important;caret-color:#b71c1c!important}.v-application .red.accent-1{background-color:#ff8a80!important;border-color:#ff8a80!important}.v-application .red--text.text--accent-1{color:#ff8a80!important;caret-color:#ff8a80!important}.v-application .red.accent-2{background-color:#ff5252!important;border-color:#ff5252!important}.v-application .red--text.text--accent-2{color:#ff5252!important;caret-color:#ff5252!important}.v-application .red.accent-3{background-color:#ff1744!important;border-color:#ff1744!important}.v-application .red--text.text--accent-3{color:#ff1744!important;caret-color:#ff1744!important}.v-application .red.accent-4{background-color:#d50000!important;border-color:#d50000!important}.v-application .red--text.text--accent-4{color:#d50000!important;caret-color:#d50000!important}.v-application .pink{background-color:#e91e63!important;border-color:#e91e63!important}.v-application .pink--text{color:#e91e63!important;caret-color:#e91e63!important}.v-application .pink.lighten-5{background-color:#fce4ec!important;border-color:#fce4ec!important}.v-application .pink--text.text--lighten-5{color:#fce4ec!important;caret-color:#fce4ec!important}.v-application .pink.lighten-4{background-color:#f8bbd0!important;border-color:#f8bbd0!important}.v-application .pink--text.text--lighten-4{color:#f8bbd0!important;caret-color:#f8bbd0!important}.v-application .pink.lighten-3{background-color:#f48fb1!important;border-color:#f48fb1!important}.v-application .pink--text.text--lighten-3{color:#f48fb1!important;caret-color:#f48fb1!important}.v-application .pink.lighten-2{background-color:#f06292!important;border-color:#f06292!important}.v-application .pink--text.text--lighten-2{color:#f06292!important;caret-color:#f06292!important}.v-application .pink.lighten-1{background-color:#ec407a!important;border-color:#ec407a!important}.v-application .pink--text.text--lighten-1{color:#ec407a!important;caret-color:#ec407a!important}.v-application .pink.darken-1{background-color:#d81b60!important;border-color:#d81b60!important}.v-application .pink--text.text--darken-1{color:#d81b60!important;caret-color:#d81b60!important}.v-application .pink.darken-2{background-color:#c2185b!important;border-color:#c2185b!important}.v-application .pink--text.text--darken-2{color:#c2185b!important;caret-color:#c2185b!important}.v-application .pink.darken-3{background-color:#ad1457!important;border-color:#ad1457!important}.v-application .pink--text.text--darken-3{color:#ad1457!important;caret-color:#ad1457!important}.v-application .pink.darken-4{background-color:#880e4f!important;border-color:#880e4f!important}.v-application .pink--text.text--darken-4{color:#880e4f!important;caret-color:#880e4f!important}.v-application .pink.accent-1{background-color:#ff80ab!important;border-color:#ff80ab!important}.v-application .pink--text.text--accent-1{color:#ff80ab!important;caret-color:#ff80ab!important}.v-application .pink.accent-2{background-color:#ff4081!important;border-color:#ff4081!important}.v-application .pink--text.text--accent-2{color:#ff4081!important;caret-color:#ff4081!important}.v-application .pink.accent-3{background-color:#f50057!important;border-color:#f50057!important}.v-application .pink--text.text--accent-3{color:#f50057!important;caret-color:#f50057!important}.v-application .pink.accent-4{background-color:#c51162!important;border-color:#c51162!important}.v-application .pink--text.text--accent-4{color:#c51162!important;caret-color:#c51162!important}.v-application .purple{background-color:#9c27b0!important;border-color:#9c27b0!important}.v-application .purple--text{color:#9c27b0!important;caret-color:#9c27b0!important}.v-application .purple.lighten-5{background-color:#f3e5f5!important;border-color:#f3e5f5!important}.v-application .purple--text.text--lighten-5{color:#f3e5f5!important;caret-color:#f3e5f5!important}.v-application .purple.lighten-4{background-color:#e1bee7!important;border-color:#e1bee7!important}.v-application .purple--text.text--lighten-4{color:#e1bee7!important;caret-color:#e1bee7!important}.v-application .purple.lighten-3{background-color:#ce93d8!important;border-color:#ce93d8!important}.v-application .purple--text.text--lighten-3{color:#ce93d8!important;caret-color:#ce93d8!important}.v-application .purple.lighten-2{background-color:#ba68c8!important;border-color:#ba68c8!important}.v-application .purple--text.text--lighten-2{color:#ba68c8!important;caret-color:#ba68c8!important}.v-application .purple.lighten-1{background-color:#ab47bc!important;border-color:#ab47bc!important}.v-application .purple--text.text--lighten-1{color:#ab47bc!important;caret-color:#ab47bc!important}.v-application .purple.darken-1{background-color:#8e24aa!important;border-color:#8e24aa!important}.v-application .purple--text.text--darken-1{color:#8e24aa!important;caret-color:#8e24aa!important}.v-application .purple.darken-2{background-color:#7b1fa2!important;border-color:#7b1fa2!important}.v-application .purple--text.text--darken-2{color:#7b1fa2!important;caret-color:#7b1fa2!important}.v-application .purple.darken-3{background-color:#6a1b9a!important;border-color:#6a1b9a!important}.v-application .purple--text.text--darken-3{color:#6a1b9a!important;caret-color:#6a1b9a!important}.v-application .purple.darken-4{background-color:#4a148c!important;border-color:#4a148c!important}.v-application .purple--text.text--darken-4{color:#4a148c!important;caret-color:#4a148c!important}.v-application .purple.accent-1{background-color:#ea80fc!important;border-color:#ea80fc!important}.v-application .purple--text.text--accent-1{color:#ea80fc!important;caret-color:#ea80fc!important}.v-application .purple.accent-2{background-color:#e040fb!important;border-color:#e040fb!important}.v-application .purple--text.text--accent-2{color:#e040fb!important;caret-color:#e040fb!important}.v-application .purple.accent-3{background-color:#d500f9!important;border-color:#d500f9!important}.v-application .purple--text.text--accent-3{color:#d500f9!important;caret-color:#d500f9!important}.v-application .purple.accent-4{background-color:#a0f!important;border-color:#a0f!important}.v-application .purple--text.text--accent-4{color:#a0f!important;caret-color:#a0f!important}.v-application .deep-purple{background-color:#673ab7!important;border-color:#673ab7!important}.v-application .deep-purple--text{color:#673ab7!important;caret-color:#673ab7!important}.v-application .deep-purple.lighten-5{background-color:#ede7f6!important;border-color:#ede7f6!important}.v-application .deep-purple--text.text--lighten-5{color:#ede7f6!important;caret-color:#ede7f6!important}.v-application .deep-purple.lighten-4{background-color:#d1c4e9!important;border-color:#d1c4e9!important}.v-application .deep-purple--text.text--lighten-4{color:#d1c4e9!important;caret-color:#d1c4e9!important}.v-application .deep-purple.lighten-3{background-color:#b39ddb!important;border-color:#b39ddb!important}.v-application .deep-purple--text.text--lighten-3{color:#b39ddb!important;caret-color:#b39ddb!important}.v-application .deep-purple.lighten-2{background-color:#9575cd!important;border-color:#9575cd!important}.v-application .deep-purple--text.text--lighten-2{color:#9575cd!important;caret-color:#9575cd!important}.v-application .deep-purple.lighten-1{background-color:#7e57c2!important;border-color:#7e57c2!important}.v-application .deep-purple--text.text--lighten-1{color:#7e57c2!important;caret-color:#7e57c2!important}.v-application .deep-purple.darken-1{background-color:#5e35b1!important;border-color:#5e35b1!important}.v-application .deep-purple--text.text--darken-1{color:#5e35b1!important;caret-color:#5e35b1!important}.v-application .deep-purple.darken-2{background-color:#512da8!important;border-color:#512da8!important}.v-application .deep-purple--text.text--darken-2{color:#512da8!important;caret-color:#512da8!important}.v-application .deep-purple.darken-3{background-color:#4527a0!important;border-color:#4527a0!important}.v-application .deep-purple--text.text--darken-3{color:#4527a0!important;caret-color:#4527a0!important}.v-application .deep-purple.darken-4{background-color:#311b92!important;border-color:#311b92!important}.v-application .deep-purple--text.text--darken-4{color:#311b92!important;caret-color:#311b92!important}.v-application .deep-purple.accent-1{background-color:#b388ff!important;border-color:#b388ff!important}.v-application .deep-purple--text.text--accent-1{color:#b388ff!important;caret-color:#b388ff!important}.v-application .deep-purple.accent-2{background-color:#7c4dff!important;border-color:#7c4dff!important}.v-application .deep-purple--text.text--accent-2{color:#7c4dff!important;caret-color:#7c4dff!important}.v-application .deep-purple.accent-3{background-color:#651fff!important;border-color:#651fff!important}.v-application .deep-purple--text.text--accent-3{color:#651fff!important;caret-color:#651fff!important}.v-application .deep-purple.accent-4{background-color:#6200ea!important;border-color:#6200ea!important}.v-application .deep-purple--text.text--accent-4{color:#6200ea!important;caret-color:#6200ea!important}.v-application .indigo{background-color:#3f51b5!important;border-color:#3f51b5!important}.v-application .indigo--text{color:#3f51b5!important;caret-color:#3f51b5!important}.v-application .indigo.lighten-5{background-color:#e8eaf6!important;border-color:#e8eaf6!important}.v-application .indigo--text.text--lighten-5{color:#e8eaf6!important;caret-color:#e8eaf6!important}.v-application .indigo.lighten-4{background-color:#c5cae9!important;border-color:#c5cae9!important}.v-application .indigo--text.text--lighten-4{color:#c5cae9!important;caret-color:#c5cae9!important}.v-application .indigo.lighten-3{background-color:#9fa8da!important;border-color:#9fa8da!important}.v-application .indigo--text.text--lighten-3{color:#9fa8da!important;caret-color:#9fa8da!important}.v-application .indigo.lighten-2{background-color:#7986cb!important;border-color:#7986cb!important}.v-application .indigo--text.text--lighten-2{color:#7986cb!important;caret-color:#7986cb!important}.v-application .indigo.lighten-1{background-color:#5c6bc0!important;border-color:#5c6bc0!important}.v-application .indigo--text.text--lighten-1{color:#5c6bc0!important;caret-color:#5c6bc0!important}.v-application .indigo.darken-1{background-color:#3949ab!important;border-color:#3949ab!important}.v-application .indigo--text.text--darken-1{color:#3949ab!important;caret-color:#3949ab!important}.v-application .indigo.darken-2{background-color:#303f9f!important;border-color:#303f9f!important}.v-application .indigo--text.text--darken-2{color:#303f9f!important;caret-color:#303f9f!important}.v-application .indigo.darken-3{background-color:#283593!important;border-color:#283593!important}.v-application .indigo--text.text--darken-3{color:#283593!important;caret-color:#283593!important}.v-application .indigo.darken-4{background-color:#1a237e!important;border-color:#1a237e!important}.v-application .indigo--text.text--darken-4{color:#1a237e!important;caret-color:#1a237e!important}.v-application .indigo.accent-1{background-color:#8c9eff!important;border-color:#8c9eff!important}.v-application .indigo--text.text--accent-1{color:#8c9eff!important;caret-color:#8c9eff!important}.v-application .indigo.accent-2{background-color:#536dfe!important;border-color:#536dfe!important}.v-application .indigo--text.text--accent-2{color:#536dfe!important;caret-color:#536dfe!important}.v-application .indigo.accent-3{background-color:#3d5afe!important;border-color:#3d5afe!important}.v-application .indigo--text.text--accent-3{color:#3d5afe!important;caret-color:#3d5afe!important}.v-application .indigo.accent-4{background-color:#304ffe!important;border-color:#304ffe!important}.v-application .indigo--text.text--accent-4{color:#304ffe!important;caret-color:#304ffe!important}.v-application .blue{background-color:#2196f3!important;border-color:#2196f3!important}.v-application .blue--text{color:#2196f3!important;caret-color:#2196f3!important}.v-application .blue.lighten-5{background-color:#e3f2fd!important;border-color:#e3f2fd!important}.v-application .blue--text.text--lighten-5{color:#e3f2fd!important;caret-color:#e3f2fd!important}.v-application .blue.lighten-4{background-color:#bbdefb!important;border-color:#bbdefb!important}.v-application .blue--text.text--lighten-4{color:#bbdefb!important;caret-color:#bbdefb!important}.v-application .blue.lighten-3{background-color:#90caf9!important;border-color:#90caf9!important}.v-application .blue--text.text--lighten-3{color:#90caf9!important;caret-color:#90caf9!important}.v-application .blue.lighten-2{background-color:#64b5f6!important;border-color:#64b5f6!important}.v-application .blue--text.text--lighten-2{color:#64b5f6!important;caret-color:#64b5f6!important}.v-application .blue.lighten-1{background-color:#42a5f5!important;border-color:#42a5f5!important}.v-application .blue--text.text--lighten-1{color:#42a5f5!important;caret-color:#42a5f5!important}.v-application .blue.darken-1{background-color:#1e88e5!important;border-color:#1e88e5!important}.v-application .blue--text.text--darken-1{color:#1e88e5!important;caret-color:#1e88e5!important}.v-application .blue.darken-2{background-color:#1976d2!important;border-color:#1976d2!important}.v-application .blue--text.text--darken-2{color:#1976d2!important;caret-color:#1976d2!important}.v-application .blue.darken-3{background-color:#1565c0!important;border-color:#1565c0!important}.v-application .blue--text.text--darken-3{color:#1565c0!important;caret-color:#1565c0!important}.v-application .blue.darken-4{background-color:#0d47a1!important;border-color:#0d47a1!important}.v-application .blue--text.text--darken-4{color:#0d47a1!important;caret-color:#0d47a1!important}.v-application .blue.accent-1{background-color:#82b1ff!important;border-color:#82b1ff!important}.v-application .blue--text.text--accent-1{color:#82b1ff!important;caret-color:#82b1ff!important}.v-application .blue.accent-2{background-color:#448aff!important;border-color:#448aff!important}.v-application .blue--text.text--accent-2{color:#448aff!important;caret-color:#448aff!important}.v-application .blue.accent-3{background-color:#2979ff!important;border-color:#2979ff!important}.v-application .blue--text.text--accent-3{color:#2979ff!important;caret-color:#2979ff!important}.v-application .blue.accent-4{background-color:#2962ff!important;border-color:#2962ff!important}.v-application .blue--text.text--accent-4{color:#2962ff!important;caret-color:#2962ff!important}.v-application .light-blue{background-color:#03a9f4!important;border-color:#03a9f4!important}.v-application .light-blue--text{color:#03a9f4!important;caret-color:#03a9f4!important}.v-application .light-blue.lighten-5{background-color:#e1f5fe!important;border-color:#e1f5fe!important}.v-application .light-blue--text.text--lighten-5{color:#e1f5fe!important;caret-color:#e1f5fe!important}.v-application .light-blue.lighten-4{background-color:#b3e5fc!important;border-color:#b3e5fc!important}.v-application .light-blue--text.text--lighten-4{color:#b3e5fc!important;caret-color:#b3e5fc!important}.v-application .light-blue.lighten-3{background-color:#81d4fa!important;border-color:#81d4fa!important}.v-application .light-blue--text.text--lighten-3{color:#81d4fa!important;caret-color:#81d4fa!important}.v-application .light-blue.lighten-2{background-color:#4fc3f7!important;border-color:#4fc3f7!important}.v-application .light-blue--text.text--lighten-2{color:#4fc3f7!important;caret-color:#4fc3f7!important}.v-application .light-blue.lighten-1{background-color:#29b6f6!important;border-color:#29b6f6!important}.v-application .light-blue--text.text--lighten-1{color:#29b6f6!important;caret-color:#29b6f6!important}.v-application .light-blue.darken-1{background-color:#039be5!important;border-color:#039be5!important}.v-application .light-blue--text.text--darken-1{color:#039be5!important;caret-color:#039be5!important}.v-application .light-blue.darken-2{background-color:#0288d1!important;border-color:#0288d1!important}.v-application .light-blue--text.text--darken-2{color:#0288d1!important;caret-color:#0288d1!important}.v-application .light-blue.darken-3{background-color:#0277bd!important;border-color:#0277bd!important}.v-application .light-blue--text.text--darken-3{color:#0277bd!important;caret-color:#0277bd!important}.v-application .light-blue.darken-4{background-color:#01579b!important;border-color:#01579b!important}.v-application .light-blue--text.text--darken-4{color:#01579b!important;caret-color:#01579b!important}.v-application .light-blue.accent-1{background-color:#80d8ff!important;border-color:#80d8ff!important}.v-application .light-blue--text.text--accent-1{color:#80d8ff!important;caret-color:#80d8ff!important}.v-application .light-blue.accent-2{background-color:#40c4ff!important;border-color:#40c4ff!important}.v-application .light-blue--text.text--accent-2{color:#40c4ff!important;caret-color:#40c4ff!important}.v-application .light-blue.accent-3{background-color:#00b0ff!important;border-color:#00b0ff!important}.v-application .light-blue--text.text--accent-3{color:#00b0ff!important;caret-color:#00b0ff!important}.v-application .light-blue.accent-4{background-color:#0091ea!important;border-color:#0091ea!important}.v-application .light-blue--text.text--accent-4{color:#0091ea!important;caret-color:#0091ea!important}.v-application .cyan{background-color:#00bcd4!important;border-color:#00bcd4!important}.v-application .cyan--text{color:#00bcd4!important;caret-color:#00bcd4!important}.v-application .cyan.lighten-5{background-color:#e0f7fa!important;border-color:#e0f7fa!important}.v-application .cyan--text.text--lighten-5{color:#e0f7fa!important;caret-color:#e0f7fa!important}.v-application .cyan.lighten-4{background-color:#b2ebf2!important;border-color:#b2ebf2!important}.v-application .cyan--text.text--lighten-4{color:#b2ebf2!important;caret-color:#b2ebf2!important}.v-application .cyan.lighten-3{background-color:#80deea!important;border-color:#80deea!important}.v-application .cyan--text.text--lighten-3{color:#80deea!important;caret-color:#80deea!important}.v-application .cyan.lighten-2{background-color:#4dd0e1!important;border-color:#4dd0e1!important}.v-application .cyan--text.text--lighten-2{color:#4dd0e1!important;caret-color:#4dd0e1!important}.v-application .cyan.lighten-1{background-color:#26c6da!important;border-color:#26c6da!important}.v-application .cyan--text.text--lighten-1{color:#26c6da!important;caret-color:#26c6da!important}.v-application .cyan.darken-1{background-color:#00acc1!important;border-color:#00acc1!important}.v-application .cyan--text.text--darken-1{color:#00acc1!important;caret-color:#00acc1!important}.v-application .cyan.darken-2{background-color:#0097a7!important;border-color:#0097a7!important}.v-application .cyan--text.text--darken-2{color:#0097a7!important;caret-color:#0097a7!important}.v-application .cyan.darken-3{background-color:#00838f!important;border-color:#00838f!important}.v-application .cyan--text.text--darken-3{color:#00838f!important;caret-color:#00838f!important}.v-application .cyan.darken-4{background-color:#006064!important;border-color:#006064!important}.v-application .cyan--text.text--darken-4{color:#006064!important;caret-color:#006064!important}.v-application .cyan.accent-1{background-color:#84ffff!important;border-color:#84ffff!important}.v-application .cyan--text.text--accent-1{color:#84ffff!important;caret-color:#84ffff!important}.v-application .cyan.accent-2{background-color:#18ffff!important;border-color:#18ffff!important}.v-application .cyan--text.text--accent-2{color:#18ffff!important;caret-color:#18ffff!important}.v-application .cyan.accent-3{background-color:#00e5ff!important;border-color:#00e5ff!important}.v-application .cyan--text.text--accent-3{color:#00e5ff!important;caret-color:#00e5ff!important}.v-application .cyan.accent-4{background-color:#00b8d4!important;border-color:#00b8d4!important}.v-application .cyan--text.text--accent-4{color:#00b8d4!important;caret-color:#00b8d4!important}.v-application .teal{background-color:#009688!important;border-color:#009688!important}.v-application .teal--text{color:#009688!important;caret-color:#009688!important}.v-application .teal.lighten-5{background-color:#e0f2f1!important;border-color:#e0f2f1!important}.v-application .teal--text.text--lighten-5{color:#e0f2f1!important;caret-color:#e0f2f1!important}.v-application .teal.lighten-4{background-color:#b2dfdb!important;border-color:#b2dfdb!important}.v-application .teal--text.text--lighten-4{color:#b2dfdb!important;caret-color:#b2dfdb!important}.v-application .teal.lighten-3{background-color:#80cbc4!important;border-color:#80cbc4!important}.v-application .teal--text.text--lighten-3{color:#80cbc4!important;caret-color:#80cbc4!important}.v-application .teal.lighten-2{background-color:#4db6ac!important;border-color:#4db6ac!important}.v-application .teal--text.text--lighten-2{color:#4db6ac!important;caret-color:#4db6ac!important}.v-application .teal.lighten-1{background-color:#26a69a!important;border-color:#26a69a!important}.v-application .teal--text.text--lighten-1{color:#26a69a!important;caret-color:#26a69a!important}.v-application .teal.darken-1{background-color:#00897b!important;border-color:#00897b!important}.v-application .teal--text.text--darken-1{color:#00897b!important;caret-color:#00897b!important}.v-application .teal.darken-2{background-color:#00796b!important;border-color:#00796b!important}.v-application .teal--text.text--darken-2{color:#00796b!important;caret-color:#00796b!important}.v-application .teal.darken-3{background-color:#00695c!important;border-color:#00695c!important}.v-application .teal--text.text--darken-3{color:#00695c!important;caret-color:#00695c!important}.v-application .teal.darken-4{background-color:#004d40!important;border-color:#004d40!important}.v-application .teal--text.text--darken-4{color:#004d40!important;caret-color:#004d40!important}.v-application .teal.accent-1{background-color:#a7ffeb!important;border-color:#a7ffeb!important}.v-application .teal--text.text--accent-1{color:#a7ffeb!important;caret-color:#a7ffeb!important}.v-application .teal.accent-2{background-color:#64ffda!important;border-color:#64ffda!important}.v-application .teal--text.text--accent-2{color:#64ffda!important;caret-color:#64ffda!important}.v-application .teal.accent-3{background-color:#1de9b6!important;border-color:#1de9b6!important}.v-application .teal--text.text--accent-3{color:#1de9b6!important;caret-color:#1de9b6!important}.v-application .teal.accent-4{background-color:#00bfa5!important;border-color:#00bfa5!important}.v-application .teal--text.text--accent-4{color:#00bfa5!important;caret-color:#00bfa5!important}.v-application .green{background-color:#4caf50!important;border-color:#4caf50!important}.v-application .green--text{color:#4caf50!important;caret-color:#4caf50!important}.v-application .green.lighten-5{background-color:#e8f5e9!important;border-color:#e8f5e9!important}.v-application .green--text.text--lighten-5{color:#e8f5e9!important;caret-color:#e8f5e9!important}.v-application .green.lighten-4{background-color:#c8e6c9!important;border-color:#c8e6c9!important}.v-application .green--text.text--lighten-4{color:#c8e6c9!important;caret-color:#c8e6c9!important}.v-application .green.lighten-3{background-color:#a5d6a7!important;border-color:#a5d6a7!important}.v-application .green--text.text--lighten-3{color:#a5d6a7!important;caret-color:#a5d6a7!important}.v-application .green.lighten-2{background-color:#81c784!important;border-color:#81c784!important}.v-application .green--text.text--lighten-2{color:#81c784!important;caret-color:#81c784!important}.v-application .green.lighten-1{background-color:#66bb6a!important;border-color:#66bb6a!important}.v-application .green--text.text--lighten-1{color:#66bb6a!important;caret-color:#66bb6a!important}.v-application .green.darken-1{background-color:#43a047!important;border-color:#43a047!important}.v-application .green--text.text--darken-1{color:#43a047!important;caret-color:#43a047!important}.v-application .green.darken-2{background-color:#388e3c!important;border-color:#388e3c!important}.v-application .green--text.text--darken-2{color:#388e3c!important;caret-color:#388e3c!important}.v-application .green.darken-3{background-color:#2e7d32!important;border-color:#2e7d32!important}.v-application .green--text.text--darken-3{color:#2e7d32!important;caret-color:#2e7d32!important}.v-application .green.darken-4{background-color:#1b5e20!important;border-color:#1b5e20!important}.v-application .green--text.text--darken-4{color:#1b5e20!important;caret-color:#1b5e20!important}.v-application .green.accent-1{background-color:#b9f6ca!important;border-color:#b9f6ca!important}.v-application .green--text.text--accent-1{color:#b9f6ca!important;caret-color:#b9f6ca!important}.v-application .green.accent-2{background-color:#69f0ae!important;border-color:#69f0ae!important}.v-application .green--text.text--accent-2{color:#69f0ae!important;caret-color:#69f0ae!important}.v-application .green.accent-3{background-color:#00e676!important;border-color:#00e676!important}.v-application .green--text.text--accent-3{color:#00e676!important;caret-color:#00e676!important}.v-application .green.accent-4{background-color:#00c853!important;border-color:#00c853!important}.v-application .green--text.text--accent-4{color:#00c853!important;caret-color:#00c853!important}.v-application .light-green{background-color:#8bc34a!important;border-color:#8bc34a!important}.v-application .light-green--text{color:#8bc34a!important;caret-color:#8bc34a!important}.v-application .light-green.lighten-5{background-color:#f1f8e9!important;border-color:#f1f8e9!important}.v-application .light-green--text.text--lighten-5{color:#f1f8e9!important;caret-color:#f1f8e9!important}.v-application .light-green.lighten-4{background-color:#dcedc8!important;border-color:#dcedc8!important}.v-application .light-green--text.text--lighten-4{color:#dcedc8!important;caret-color:#dcedc8!important}.v-application .light-green.lighten-3{background-color:#c5e1a5!important;border-color:#c5e1a5!important}.v-application .light-green--text.text--lighten-3{color:#c5e1a5!important;caret-color:#c5e1a5!important}.v-application .light-green.lighten-2{background-color:#aed581!important;border-color:#aed581!important}.v-application .light-green--text.text--lighten-2{color:#aed581!important;caret-color:#aed581!important}.v-application .light-green.lighten-1{background-color:#9ccc65!important;border-color:#9ccc65!important}.v-application .light-green--text.text--lighten-1{color:#9ccc65!important;caret-color:#9ccc65!important}.v-application .light-green.darken-1{background-color:#7cb342!important;border-color:#7cb342!important}.v-application .light-green--text.text--darken-1{color:#7cb342!important;caret-color:#7cb342!important}.v-application .light-green.darken-2{background-color:#689f38!important;border-color:#689f38!important}.v-application .light-green--text.text--darken-2{color:#689f38!important;caret-color:#689f38!important}.v-application .light-green.darken-3{background-color:#558b2f!important;border-color:#558b2f!important}.v-application .light-green--text.text--darken-3{color:#558b2f!important;caret-color:#558b2f!important}.v-application .light-green.darken-4{background-color:#33691e!important;border-color:#33691e!important}.v-application .light-green--text.text--darken-4{color:#33691e!important;caret-color:#33691e!important}.v-application .light-green.accent-1{background-color:#ccff90!important;border-color:#ccff90!important}.v-application .light-green--text.text--accent-1{color:#ccff90!important;caret-color:#ccff90!important}.v-application .light-green.accent-2{background-color:#b2ff59!important;border-color:#b2ff59!important}.v-application .light-green--text.text--accent-2{color:#b2ff59!important;caret-color:#b2ff59!important}.v-application .light-green.accent-3{background-color:#76ff03!important;border-color:#76ff03!important}.v-application .light-green--text.text--accent-3{color:#76ff03!important;caret-color:#76ff03!important}.v-application .light-green.accent-4{background-color:#64dd17!important;border-color:#64dd17!important}.v-application .light-green--text.text--accent-4{color:#64dd17!important;caret-color:#64dd17!important}.v-application .lime{background-color:#cddc39!important;border-color:#cddc39!important}.v-application .lime--text{color:#cddc39!important;caret-color:#cddc39!important}.v-application .lime.lighten-5{background-color:#f9fbe7!important;border-color:#f9fbe7!important}.v-application .lime--text.text--lighten-5{color:#f9fbe7!important;caret-color:#f9fbe7!important}.v-application .lime.lighten-4{background-color:#f0f4c3!important;border-color:#f0f4c3!important}.v-application .lime--text.text--lighten-4{color:#f0f4c3!important;caret-color:#f0f4c3!important}.v-application .lime.lighten-3{background-color:#e6ee9c!important;border-color:#e6ee9c!important}.v-application .lime--text.text--lighten-3{color:#e6ee9c!important;caret-color:#e6ee9c!important}.v-application .lime.lighten-2{background-color:#dce775!important;border-color:#dce775!important}.v-application .lime--text.text--lighten-2{color:#dce775!important;caret-color:#dce775!important}.v-application .lime.lighten-1{background-color:#d4e157!important;border-color:#d4e157!important}.v-application .lime--text.text--lighten-1{color:#d4e157!important;caret-color:#d4e157!important}.v-application .lime.darken-1{background-color:#c0ca33!important;border-color:#c0ca33!important}.v-application .lime--text.text--darken-1{color:#c0ca33!important;caret-color:#c0ca33!important}.v-application .lime.darken-2{background-color:#afb42b!important;border-color:#afb42b!important}.v-application .lime--text.text--darken-2{color:#afb42b!important;caret-color:#afb42b!important}.v-application .lime.darken-3{background-color:#9e9d24!important;border-color:#9e9d24!important}.v-application .lime--text.text--darken-3{color:#9e9d24!important;caret-color:#9e9d24!important}.v-application .lime.darken-4{background-color:#827717!important;border-color:#827717!important}.v-application .lime--text.text--darken-4{color:#827717!important;caret-color:#827717!important}.v-application .lime.accent-1{background-color:#f4ff81!important;border-color:#f4ff81!important}.v-application .lime--text.text--accent-1{color:#f4ff81!important;caret-color:#f4ff81!important}.v-application .lime.accent-2{background-color:#eeff41!important;border-color:#eeff41!important}.v-application .lime--text.text--accent-2{color:#eeff41!important;caret-color:#eeff41!important}.v-application .lime.accent-3{background-color:#c6ff00!important;border-color:#c6ff00!important}.v-application .lime--text.text--accent-3{color:#c6ff00!important;caret-color:#c6ff00!important}.v-application .lime.accent-4{background-color:#aeea00!important;border-color:#aeea00!important}.v-application .lime--text.text--accent-4{color:#aeea00!important;caret-color:#aeea00!important}.v-application .yellow{background-color:#ffeb3b!important;border-color:#ffeb3b!important}.v-application .yellow--text{color:#ffeb3b!important;caret-color:#ffeb3b!important}.v-application .yellow.lighten-5{background-color:#fffde7!important;border-color:#fffde7!important}.v-application .yellow--text.text--lighten-5{color:#fffde7!important;caret-color:#fffde7!important}.v-application .yellow.lighten-4{background-color:#fff9c4!important;border-color:#fff9c4!important}.v-application .yellow--text.text--lighten-4{color:#fff9c4!important;caret-color:#fff9c4!important}.v-application .yellow.lighten-3{background-color:#fff59d!important;border-color:#fff59d!important}.v-application .yellow--text.text--lighten-3{color:#fff59d!important;caret-color:#fff59d!important}.v-application .yellow.lighten-2{background-color:#fff176!important;border-color:#fff176!important}.v-application .yellow--text.text--lighten-2{color:#fff176!important;caret-color:#fff176!important}.v-application .yellow.lighten-1{background-color:#ffee58!important;border-color:#ffee58!important}.v-application .yellow--text.text--lighten-1{color:#ffee58!important;caret-color:#ffee58!important}.v-application .yellow.darken-1{background-color:#fdd835!important;border-color:#fdd835!important}.v-application .yellow--text.text--darken-1{color:#fdd835!important;caret-color:#fdd835!important}.v-application .yellow.darken-2{background-color:#fbc02d!important;border-color:#fbc02d!important}.v-application .yellow--text.text--darken-2{color:#fbc02d!important;caret-color:#fbc02d!important}.v-application .yellow.darken-3{background-color:#f9a825!important;border-color:#f9a825!important}.v-application .yellow--text.text--darken-3{color:#f9a825!important;caret-color:#f9a825!important}.v-application .yellow.darken-4{background-color:#f57f17!important;border-color:#f57f17!important}.v-application .yellow--text.text--darken-4{color:#f57f17!important;caret-color:#f57f17!important}.v-application .yellow.accent-1{background-color:#ffff8d!important;border-color:#ffff8d!important}.v-application .yellow--text.text--accent-1{color:#ffff8d!important;caret-color:#ffff8d!important}.v-application .yellow.accent-2{background-color:#ff0!important;border-color:#ff0!important}.v-application .yellow--text.text--accent-2{color:#ff0!important;caret-color:#ff0!important}.v-application .yellow.accent-3{background-color:#ffea00!important;border-color:#ffea00!important}.v-application .yellow--text.text--accent-3{color:#ffea00!important;caret-color:#ffea00!important}.v-application .yellow.accent-4{background-color:#ffd600!important;border-color:#ffd600!important}.v-application .yellow--text.text--accent-4{color:#ffd600!important;caret-color:#ffd600!important}.v-application .amber{background-color:#ffc107!important;border-color:#ffc107!important}.v-application .amber--text{color:#ffc107!important;caret-color:#ffc107!important}.v-application .amber.lighten-5{background-color:#fff8e1!important;border-color:#fff8e1!important}.v-application .amber--text.text--lighten-5{color:#fff8e1!important;caret-color:#fff8e1!important}.v-application .amber.lighten-4{background-color:#ffecb3!important;border-color:#ffecb3!important}.v-application .amber--text.text--lighten-4{color:#ffecb3!important;caret-color:#ffecb3!important}.v-application .amber.lighten-3{background-color:#ffe082!important;border-color:#ffe082!important}.v-application .amber--text.text--lighten-3{color:#ffe082!important;caret-color:#ffe082!important}.v-application .amber.lighten-2{background-color:#ffd54f!important;border-color:#ffd54f!important}.v-application .amber--text.text--lighten-2{color:#ffd54f!important;caret-color:#ffd54f!important}.v-application .amber.lighten-1{background-color:#ffca28!important;border-color:#ffca28!important}.v-application .amber--text.text--lighten-1{color:#ffca28!important;caret-color:#ffca28!important}.v-application .amber.darken-1{background-color:#ffb300!important;border-color:#ffb300!important}.v-application .amber--text.text--darken-1{color:#ffb300!important;caret-color:#ffb300!important}.v-application .amber.darken-2{background-color:#ffa000!important;border-color:#ffa000!important}.v-application .amber--text.text--darken-2{color:#ffa000!important;caret-color:#ffa000!important}.v-application .amber.darken-3{background-color:#ff8f00!important;border-color:#ff8f00!important}.v-application .amber--text.text--darken-3{color:#ff8f00!important;caret-color:#ff8f00!important}.v-application .amber.darken-4{background-color:#ff6f00!important;border-color:#ff6f00!important}.v-application .amber--text.text--darken-4{color:#ff6f00!important;caret-color:#ff6f00!important}.v-application .amber.accent-1{background-color:#ffe57f!important;border-color:#ffe57f!important}.v-application .amber--text.text--accent-1{color:#ffe57f!important;caret-color:#ffe57f!important}.v-application .amber.accent-2{background-color:#ffd740!important;border-color:#ffd740!important}.v-application .amber--text.text--accent-2{color:#ffd740!important;caret-color:#ffd740!important}.v-application .amber.accent-3{background-color:#ffc400!important;border-color:#ffc400!important}.v-application .amber--text.text--accent-3{color:#ffc400!important;caret-color:#ffc400!important}.v-application .amber.accent-4{background-color:#ffab00!important;border-color:#ffab00!important}.v-application .amber--text.text--accent-4{color:#ffab00!important;caret-color:#ffab00!important}.v-application .orange{background-color:#ff9800!important;border-color:#ff9800!important}.v-application .orange--text{color:#ff9800!important;caret-color:#ff9800!important}.v-application .orange.lighten-5{background-color:#fff3e0!important;border-color:#fff3e0!important}.v-application .orange--text.text--lighten-5{color:#fff3e0!important;caret-color:#fff3e0!important}.v-application .orange.lighten-4{background-color:#ffe0b2!important;border-color:#ffe0b2!important}.v-application .orange--text.text--lighten-4{color:#ffe0b2!important;caret-color:#ffe0b2!important}.v-application .orange.lighten-3{background-color:#ffcc80!important;border-color:#ffcc80!important}.v-application .orange--text.text--lighten-3{color:#ffcc80!important;caret-color:#ffcc80!important}.v-application .orange.lighten-2{background-color:#ffb74d!important;border-color:#ffb74d!important}.v-application .orange--text.text--lighten-2{color:#ffb74d!important;caret-color:#ffb74d!important}.v-application .orange.lighten-1{background-color:#ffa726!important;border-color:#ffa726!important}.v-application .orange--text.text--lighten-1{color:#ffa726!important;caret-color:#ffa726!important}.v-application .orange.darken-1{background-color:#fb8c00!important;border-color:#fb8c00!important}.v-application .orange--text.text--darken-1{color:#fb8c00!important;caret-color:#fb8c00!important}.v-application .orange.darken-2{background-color:#f57c00!important;border-color:#f57c00!important}.v-application .orange--text.text--darken-2{color:#f57c00!important;caret-color:#f57c00!important}.v-application .orange.darken-3{background-color:#ef6c00!important;border-color:#ef6c00!important}.v-application .orange--text.text--darken-3{color:#ef6c00!important;caret-color:#ef6c00!important}.v-application .orange.darken-4{background-color:#e65100!important;border-color:#e65100!important}.v-application .orange--text.text--darken-4{color:#e65100!important;caret-color:#e65100!important}.v-application .orange.accent-1{background-color:#ffd180!important;border-color:#ffd180!important}.v-application .orange--text.text--accent-1{color:#ffd180!important;caret-color:#ffd180!important}.v-application .orange.accent-2{background-color:#ffab40!important;border-color:#ffab40!important}.v-application .orange--text.text--accent-2{color:#ffab40!important;caret-color:#ffab40!important}.v-application .orange.accent-3{background-color:#ff9100!important;border-color:#ff9100!important}.v-application .orange--text.text--accent-3{color:#ff9100!important;caret-color:#ff9100!important}.v-application .orange.accent-4{background-color:#ff6d00!important;border-color:#ff6d00!important}.v-application .orange--text.text--accent-4{color:#ff6d00!important;caret-color:#ff6d00!important}.v-application .deep-orange{background-color:#ff5722!important;border-color:#ff5722!important}.v-application .deep-orange--text{color:#ff5722!important;caret-color:#ff5722!important}.v-application .deep-orange.lighten-5{background-color:#fbe9e7!important;border-color:#fbe9e7!important}.v-application .deep-orange--text.text--lighten-5{color:#fbe9e7!important;caret-color:#fbe9e7!important}.v-application .deep-orange.lighten-4{background-color:#ffccbc!important;border-color:#ffccbc!important}.v-application .deep-orange--text.text--lighten-4{color:#ffccbc!important;caret-color:#ffccbc!important}.v-application .deep-orange.lighten-3{background-color:#ffab91!important;border-color:#ffab91!important}.v-application .deep-orange--text.text--lighten-3{color:#ffab91!important;caret-color:#ffab91!important}.v-application .deep-orange.lighten-2{background-color:#ff8a65!important;border-color:#ff8a65!important}.v-application .deep-orange--text.text--lighten-2{color:#ff8a65!important;caret-color:#ff8a65!important}.v-application .deep-orange.lighten-1{background-color:#ff7043!important;border-color:#ff7043!important}.v-application .deep-orange--text.text--lighten-1{color:#ff7043!important;caret-color:#ff7043!important}.v-application .deep-orange.darken-1{background-color:#f4511e!important;border-color:#f4511e!important}.v-application .deep-orange--text.text--darken-1{color:#f4511e!important;caret-color:#f4511e!important}.v-application .deep-orange.darken-2{background-color:#e64a19!important;border-color:#e64a19!important}.v-application .deep-orange--text.text--darken-2{color:#e64a19!important;caret-color:#e64a19!important}.v-application .deep-orange.darken-3{background-color:#d84315!important;border-color:#d84315!important}.v-application .deep-orange--text.text--darken-3{color:#d84315!important;caret-color:#d84315!important}.v-application .deep-orange.darken-4{background-color:#bf360c!important;border-color:#bf360c!important}.v-application .deep-orange--text.text--darken-4{color:#bf360c!important;caret-color:#bf360c!important}.v-application .deep-orange.accent-1{background-color:#ff9e80!important;border-color:#ff9e80!important}.v-application .deep-orange--text.text--accent-1{color:#ff9e80!important;caret-color:#ff9e80!important}.v-application .deep-orange.accent-2{background-color:#ff6e40!important;border-color:#ff6e40!important}.v-application .deep-orange--text.text--accent-2{color:#ff6e40!important;caret-color:#ff6e40!important}.v-application .deep-orange.accent-3{background-color:#ff3d00!important;border-color:#ff3d00!important}.v-application .deep-orange--text.text--accent-3{color:#ff3d00!important;caret-color:#ff3d00!important}.v-application .deep-orange.accent-4{background-color:#dd2c00!important;border-color:#dd2c00!important}.v-application .deep-orange--text.text--accent-4{color:#dd2c00!important;caret-color:#dd2c00!important}.v-application .brown{background-color:#795548!important;border-color:#795548!important}.v-application .brown--text{color:#795548!important;caret-color:#795548!important}.v-application .brown.lighten-5{background-color:#efebe9!important;border-color:#efebe9!important}.v-application .brown--text.text--lighten-5{color:#efebe9!important;caret-color:#efebe9!important}.v-application .brown.lighten-4{background-color:#d7ccc8!important;border-color:#d7ccc8!important}.v-application .brown--text.text--lighten-4{color:#d7ccc8!important;caret-color:#d7ccc8!important}.v-application .brown.lighten-3{background-color:#bcaaa4!important;border-color:#bcaaa4!important}.v-application .brown--text.text--lighten-3{color:#bcaaa4!important;caret-color:#bcaaa4!important}.v-application .brown.lighten-2{background-color:#a1887f!important;border-color:#a1887f!important}.v-application .brown--text.text--lighten-2{color:#a1887f!important;caret-color:#a1887f!important}.v-application .brown.lighten-1{background-color:#8d6e63!important;border-color:#8d6e63!important}.v-application .brown--text.text--lighten-1{color:#8d6e63!important;caret-color:#8d6e63!important}.v-application .brown.darken-1{background-color:#6d4c41!important;border-color:#6d4c41!important}.v-application .brown--text.text--darken-1{color:#6d4c41!important;caret-color:#6d4c41!important}.v-application .brown.darken-2{background-color:#5d4037!important;border-color:#5d4037!important}.v-application .brown--text.text--darken-2{color:#5d4037!important;caret-color:#5d4037!important}.v-application .brown.darken-3{background-color:#4e342e!important;border-color:#4e342e!important}.v-application .brown--text.text--darken-3{color:#4e342e!important;caret-color:#4e342e!important}.v-application .brown.darken-4{background-color:#3e2723!important;border-color:#3e2723!important}.v-application .brown--text.text--darken-4{color:#3e2723!important;caret-color:#3e2723!important}.v-application .blue-grey{background-color:#607d8b!important;border-color:#607d8b!important}.v-application .blue-grey--text{color:#607d8b!important;caret-color:#607d8b!important}.v-application .blue-grey.lighten-5{background-color:#eceff1!important;border-color:#eceff1!important}.v-application .blue-grey--text.text--lighten-5{color:#eceff1!important;caret-color:#eceff1!important}.v-application .blue-grey.lighten-4{background-color:#cfd8dc!important;border-color:#cfd8dc!important}.v-application .blue-grey--text.text--lighten-4{color:#cfd8dc!important;caret-color:#cfd8dc!important}.v-application .blue-grey.lighten-3{background-color:#b0bec5!important;border-color:#b0bec5!important}.v-application .blue-grey--text.text--lighten-3{color:#b0bec5!important;caret-color:#b0bec5!important}.v-application .blue-grey.lighten-2{background-color:#90a4ae!important;border-color:#90a4ae!important}.v-application .blue-grey--text.text--lighten-2{color:#90a4ae!important;caret-color:#90a4ae!important}.v-application .blue-grey.lighten-1{background-color:#78909c!important;border-color:#78909c!important}.v-application .blue-grey--text.text--lighten-1{color:#78909c!important;caret-color:#78909c!important}.v-application .blue-grey.darken-1{background-color:#546e7a!important;border-color:#546e7a!important}.v-application .blue-grey--text.text--darken-1{color:#546e7a!important;caret-color:#546e7a!important}.v-application .blue-grey.darken-2{background-color:#455a64!important;border-color:#455a64!important}.v-application .blue-grey--text.text--darken-2{color:#455a64!important;caret-color:#455a64!important}.v-application .blue-grey.darken-3{background-color:#37474f!important;border-color:#37474f!important}.v-application .blue-grey--text.text--darken-3{color:#37474f!important;caret-color:#37474f!important}.v-application .blue-grey.darken-4{background-color:#263238!important;border-color:#263238!important}.v-application .blue-grey--text.text--darken-4{color:#263238!important;caret-color:#263238!important}.v-application .grey{background-color:#9e9e9e!important;border-color:#9e9e9e!important}.v-application .grey--text{color:#9e9e9e!important;caret-color:#9e9e9e!important}.v-application .grey.lighten-5{background-color:#fafafa!important;border-color:#fafafa!important}.v-application .grey--text.text--lighten-5{color:#fafafa!important;caret-color:#fafafa!important}.v-application .grey.lighten-4{background-color:#f5f5f5!important;border-color:#f5f5f5!important}.v-application .grey--text.text--lighten-4{color:#f5f5f5!important;caret-color:#f5f5f5!important}.v-application .grey.lighten-3{background-color:#eee!important;border-color:#eee!important}.v-application .grey--text.text--lighten-3{color:#eee!important;caret-color:#eee!important}.v-application .grey.lighten-2{background-color:#e0e0e0!important;border-color:#e0e0e0!important}.v-application .grey--text.text--lighten-2{color:#e0e0e0!important;caret-color:#e0e0e0!important}.v-application .grey.lighten-1{background-color:#bdbdbd!important;border-color:#bdbdbd!important}.v-application .grey--text.text--lighten-1{color:#bdbdbd!important;caret-color:#bdbdbd!important}.v-application .grey.darken-1{background-color:#757575!important;border-color:#757575!important}.v-application .grey--text.text--darken-1{color:#757575!important;caret-color:#757575!important}.v-application .grey.darken-2{background-color:#616161!important;border-color:#616161!important}.v-application .grey--text.text--darken-2{color:#616161!important;caret-color:#616161!important}.v-application .grey.darken-3{background-color:#424242!important;border-color:#424242!important}.v-application .grey--text.text--darken-3{color:#424242!important;caret-color:#424242!important}.v-application .grey.darken-4{background-color:#212121!important;border-color:#212121!important}.v-application .grey--text.text--darken-4{color:#212121!important;caret-color:#212121!important}.v-application .shades.black{background-color:#000!important;border-color:#000!important}.v-application .shades--text.text--black{color:#000!important;caret-color:#000!important}.v-application .shades.white{background-color:#fff!important;border-color:#fff!important}.v-application .shades--text.text--white{color:#fff!important;caret-color:#fff!important}.v-application .shades.transparent{background-color:transparent!important;border-color:transparent!important}.v-application .shades--text.text--transparent{color:transparent!important;caret-color:transparent!important}/*! + * ress.css • v2.0.4 + * MIT License + * github.com/filipelinhares/ress + */html{box-sizing:border-box;overflow-y:scroll;-webkit-text-size-adjust:100%;word-break:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4}*,:after,:before{background-repeat:no-repeat;box-sizing:inherit}:after,:before{text-decoration:inherit;vertical-align:inherit}*{padding:0;margin:0}hr{overflow:visible;height:0}details,main{display:block}summary{display:list-item}small{font-size:80%}[hidden]{display:none}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}a{background-color:transparent}a:active,a:hover{outline-width:0}code,kbd,pre,samp{font-family:monospace,monospace}pre{font-size:1em}b,strong{font-weight:bolder}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}input{border-radius:0}[disabled]{cursor:default}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}textarea{overflow:auto;resize:vertical}button,input,optgroup,select,textarea{font:inherit}optgroup{font-weight:700}button{overflow:visible}button,select{text-transform:none}[role=button],[type=button],[type=reset],[type=submit],button{cursor:pointer;color:inherit}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button:-moz-focusring{outline:1px dotted ButtonText}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}button,input,select,textarea{background-color:transparent;border-style:none}select{-moz-appearance:none;-webkit-appearance:none}select::-ms-expand{display:none}select::-ms-value{color:currentColor}legend{border:0;color:inherit;display:table;white-space:normal;max-width:100%}::-webkit-file-upload-button{-webkit-appearance:button;color:inherit;font:inherit}img{border-style:none}progress{vertical-align:baseline}svg:not([fill]){fill:currentColor}@media screen{[hidden~=screen]{display:inherit}[hidden~=screen]:not(:active):not(:focus):not(:target){position:absolute!important;clip:rect(0 0 0 0)!important}}[aria-busy=true]{cursor:progress}[aria-controls]{cursor:pointer}[aria-disabled=true]{cursor:default}.v-application .elevation-24{box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)!important}.v-application .elevation-23{box-shadow:0 11px 14px -7px rgba(0,0,0,.2),0 23px 36px 3px rgba(0,0,0,.14),0 9px 44px 8px rgba(0,0,0,.12)!important}.v-application .elevation-22{box-shadow:0 10px 14px -6px rgba(0,0,0,.2),0 22px 35px 3px rgba(0,0,0,.14),0 8px 42px 7px rgba(0,0,0,.12)!important}.v-application .elevation-21{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 21px 33px 3px rgba(0,0,0,.14),0 8px 40px 7px rgba(0,0,0,.12)!important}.v-application .elevation-20{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 20px 31px 3px rgba(0,0,0,.14),0 8px 38px 7px rgba(0,0,0,.12)!important}.v-application .elevation-19{box-shadow:0 9px 12px -6px rgba(0,0,0,.2),0 19px 29px 2px rgba(0,0,0,.14),0 7px 36px 6px rgba(0,0,0,.12)!important}.v-application .elevation-18{box-shadow:0 9px 11px -5px rgba(0,0,0,.2),0 18px 28px 2px rgba(0,0,0,.14),0 7px 34px 6px rgba(0,0,0,.12)!important}.v-application .elevation-17{box-shadow:0 8px 11px -5px rgba(0,0,0,.2),0 17px 26px 2px rgba(0,0,0,.14),0 6px 32px 5px rgba(0,0,0,.12)!important}.v-application .elevation-16{box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)!important}.v-application .elevation-15{box-shadow:0 8px 9px -5px rgba(0,0,0,.2),0 15px 22px 2px rgba(0,0,0,.14),0 6px 28px 5px rgba(0,0,0,.12)!important}.v-application .elevation-14{box-shadow:0 7px 9px -4px rgba(0,0,0,.2),0 14px 21px 2px rgba(0,0,0,.14),0 5px 26px 4px rgba(0,0,0,.12)!important}.v-application .elevation-13{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 13px 19px 2px rgba(0,0,0,.14),0 5px 24px 4px rgba(0,0,0,.12)!important}.v-application .elevation-12{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 12px 17px 2px rgba(0,0,0,.14),0 5px 22px 4px rgba(0,0,0,.12)!important}.v-application .elevation-11{box-shadow:0 6px 7px -4px rgba(0,0,0,.2),0 11px 15px 1px rgba(0,0,0,.14),0 4px 20px 3px rgba(0,0,0,.12)!important}.v-application .elevation-10{box-shadow:0 6px 6px -3px rgba(0,0,0,.2),0 10px 14px 1px rgba(0,0,0,.14),0 4px 18px 3px rgba(0,0,0,.12)!important}.v-application .elevation-9{box-shadow:0 5px 6px -3px rgba(0,0,0,.2),0 9px 12px 1px rgba(0,0,0,.14),0 3px 16px 2px rgba(0,0,0,.12)!important}.v-application .elevation-8{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)!important}.v-application .elevation-7{box-shadow:0 4px 5px -2px rgba(0,0,0,.2),0 7px 10px 1px rgba(0,0,0,.14),0 2px 16px 1px rgba(0,0,0,.12)!important}.v-application .elevation-6{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12)!important}.v-application .elevation-5{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px 0 rgba(0,0,0,.14),0 1px 14px 0 rgba(0,0,0,.12)!important}.v-application .elevation-4{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)!important}.v-application .elevation-3{box-shadow:0 3px 3px -2px rgba(0,0,0,.2),0 3px 4px 0 rgba(0,0,0,.14),0 1px 8px 0 rgba(0,0,0,.12)!important}.v-application .elevation-2{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)!important}.v-application .elevation-1{box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12)!important}.v-application .elevation-0{box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)!important}.v-application .carousel-transition-enter{transform:translate(100%)}.v-application .carousel-transition-leave,.v-application .carousel-transition-leave-to{position:absolute;top:0;transform:translate(-100%)}.carousel-reverse-transition-enter{transform:translate(-100%)}.carousel-reverse-transition-leave,.carousel-reverse-transition-leave-to{position:absolute;top:0;transform:translate(100%)}.dialog-transition-enter,.dialog-transition-leave-to{transform:scale(.5);opacity:0}.dialog-transition-enter-to,.dialog-transition-leave{opacity:1}.dialog-bottom-transition-enter,.dialog-bottom-transition-leave-to{transform:translateY(100%)}.picker-reverse-transition-enter-active,.picker-reverse-transition-leave-active,.picker-transition-enter-active,.picker-transition-leave-active{transition:.3s cubic-bezier(0,0,.2,1)}.picker-reverse-transition-enter,.picker-reverse-transition-leave-to,.picker-transition-enter,.picker-transition-leave-to{opacity:0}.picker-reverse-transition-leave,.picker-reverse-transition-leave-active,.picker-reverse-transition-leave-to,.picker-transition-leave,.picker-transition-leave-active,.picker-transition-leave-to{position:absolute!important}.picker-transition-enter{transform:translateY(100%)}.picker-reverse-transition-enter,.picker-transition-leave-to{transform:translateY(-100%)}.picker-reverse-transition-leave-to{transform:translateY(100%)}.picker-title-transition-enter-to,.picker-title-transition-leave{transform:translate(0)}.picker-title-transition-enter{transform:translate(-100%)}.picker-title-transition-leave-to{opacity:0;transform:translate(100%)}.picker-title-transition-leave,.picker-title-transition-leave-active,.picker-title-transition-leave-to{position:absolute!important}.tab-transition-enter{transform:translate(100%)}.tab-transition-leave,.tab-transition-leave-active{position:absolute;top:0}.tab-transition-leave-to{position:absolute}.tab-reverse-transition-enter,.tab-transition-leave-to{transform:translate(-100%)}.tab-reverse-transition-leave,.tab-reverse-transition-leave-to{top:0;position:absolute;transform:translate(100%)}.expand-transition-enter-active,.expand-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.expand-transition-move{transition:transform .6s}.expand-x-transition-enter-active,.expand-x-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.expand-x-transition-move{transition:transform .6s}.scale-transition-enter-active,.scale-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scale-transition-move{transition:transform .6s}.scale-transition-enter,.scale-transition-leave,.scale-transition-leave-to{opacity:0;transform:scale(0)}.scale-rotate-transition-enter-active,.scale-rotate-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scale-rotate-transition-move{transition:transform .6s}.scale-rotate-transition-enter,.scale-rotate-transition-leave,.scale-rotate-transition-leave-to{opacity:0;transform:scale(0) rotate(-45deg)}.scale-rotate-reverse-transition-enter-active,.scale-rotate-reverse-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scale-rotate-reverse-transition-move{transition:transform .6s}.scale-rotate-reverse-transition-enter,.scale-rotate-reverse-transition-leave,.scale-rotate-reverse-transition-leave-to{opacity:0;transform:scale(0) rotate(45deg)}.message-transition-enter-active,.message-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.message-transition-move{transition:transform .6s}.message-transition-enter,.message-transition-leave-to{opacity:0;transform:translateY(-15px)}.message-transition-leave,.message-transition-leave-active{position:absolute}.slide-y-transition-enter-active,.slide-y-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.slide-y-transition-move{transition:transform .6s}.slide-y-transition-enter,.slide-y-transition-leave-to{opacity:0;transform:translateY(-15px)}.slide-y-reverse-transition-enter-active,.slide-y-reverse-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.slide-y-reverse-transition-move{transition:transform .6s}.slide-y-reverse-transition-enter,.slide-y-reverse-transition-leave-to{opacity:0;transform:translateY(15px)}.scroll-y-transition-enter-active,.scroll-y-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scroll-y-transition-move{transition:transform .6s}.scroll-y-transition-enter,.scroll-y-transition-leave-to{opacity:0}.scroll-y-transition-enter{transform:translateY(-15px)}.scroll-y-transition-leave-to{transform:translateY(15px)}.scroll-y-reverse-transition-enter-active,.scroll-y-reverse-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scroll-y-reverse-transition-move{transition:transform .6s}.scroll-y-reverse-transition-enter,.scroll-y-reverse-transition-leave-to{opacity:0}.scroll-y-reverse-transition-enter{transform:translateY(15px)}.scroll-y-reverse-transition-leave-to{transform:translateY(-15px)}.scroll-x-transition-enter-active,.scroll-x-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scroll-x-transition-move{transition:transform .6s}.scroll-x-transition-enter,.scroll-x-transition-leave-to{opacity:0}.scroll-x-transition-enter{transform:translateX(-15px)}.scroll-x-transition-leave-to{transform:translateX(15px)}.scroll-x-reverse-transition-enter-active,.scroll-x-reverse-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.scroll-x-reverse-transition-move{transition:transform .6s}.scroll-x-reverse-transition-enter,.scroll-x-reverse-transition-leave-to{opacity:0}.scroll-x-reverse-transition-enter{transform:translateX(15px)}.scroll-x-reverse-transition-leave-to{transform:translateX(-15px)}.slide-x-transition-enter-active,.slide-x-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.slide-x-transition-move{transition:transform .6s}.slide-x-transition-enter,.slide-x-transition-leave-to{opacity:0;transform:translateX(-15px)}.slide-x-reverse-transition-enter-active,.slide-x-reverse-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.slide-x-reverse-transition-move{transition:transform .6s}.slide-x-reverse-transition-enter,.slide-x-reverse-transition-leave-to{opacity:0;transform:translateX(15px)}.fade-transition-enter-active,.fade-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.fade-transition-move{transition:transform .6s}.fade-transition-enter,.fade-transition-leave-to{opacity:0!important}.fab-transition-enter-active,.fab-transition-leave-active{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.fab-transition-move{transition:transform .6s}.fab-transition-enter,.fab-transition-leave-to{transform:scale(0) rotate(-45deg)}.v-application .blockquote{padding:16px 0 16px 24px;font-size:18px;font-weight:300}.v-application code,.v-application kbd{border-radius:3px;font-size:85%;font-weight:900}.v-application code{background-color:#fbe5e1;color:#c0341d;padding:0 .4rem}.v-application kbd{background:#212529;color:#fff;padding:.2rem .4rem}html{font-size:16px;overflow-x:hidden;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-tap-highlight-color:rgba(0,0,0,0)}html.overflow-y-hidden{overflow-y:hidden!important}.v-application{font-family:Roboto,sans-serif;line-height:1.5}.v-application ::-ms-clear,.v-application ::-ms-reveal{display:none}.v-application .theme--light.heading{color:rgba(0,0,0,.87)}.v-application .theme--dark.heading{color:#fff}.v-application ol,.v-application ul{padding-left:24px}.v-application .display-4{font-size:6rem!important;line-height:6rem;letter-spacing:-.015625em!important}.v-application .display-3,.v-application .display-4{font-weight:300;font-family:Roboto,sans-serif!important}.v-application .display-3{font-size:3.75rem!important;line-height:3.75rem;letter-spacing:-.0083333333em!important}.v-application .display-2{font-size:3rem!important;line-height:3.125rem;letter-spacing:normal!important}.v-application .display-1,.v-application .display-2{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .display-1{font-size:2.125rem!important;line-height:2.5rem;letter-spacing:.0073529412em!important}.v-application .headline{font-size:1.5rem!important;font-weight:400;letter-spacing:normal!important}.v-application .headline,.v-application .title{line-height:2rem;font-family:Roboto,sans-serif!important}.v-application .title{font-size:1.25rem!important;font-weight:500;letter-spacing:.0125em!important}.v-application .subtitle-2{font-size:.875rem!important;font-weight:500;letter-spacing:.0071428571em!important;line-height:1.375rem;font-family:Roboto,sans-serif!important}.v-application .subtitle-1{font-size:1rem!important;letter-spacing:.009375em!important;line-height:1.75rem}.v-application .body-2,.v-application .subtitle-1{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .body-2{font-size:.875rem!important;letter-spacing:.0178571429em!important;line-height:1.25rem}.v-application .body-1{font-size:1rem!important;letter-spacing:.03125em!important;line-height:1.5rem}.v-application .body-1,.v-application .caption{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .caption{font-size:.75rem!important;letter-spacing:.0333333333em!important;line-height:1.25rem}.v-application .overline{font-size:.75rem!important;font-weight:500;letter-spacing:.1666666667em!important;line-height:2rem;text-transform:uppercase;font-family:Roboto,sans-serif!important}.v-application p{margin-bottom:16px}@media only print{.v-application .hidden-print-only{display:none!important}}@media only screen{.v-application .hidden-screen-only{display:none!important}}@media only screen and (max-width:599px){.v-application .hidden-xs-only{display:none!important}}@media only screen and (min-width:600px)and (max-width:959px){.v-application .hidden-sm-only{display:none!important}}@media only screen and (max-width:959px){.v-application .hidden-sm-and-down{display:none!important}}@media only screen and (min-width:600px){.v-application .hidden-sm-and-up{display:none!important}}@media only screen and (min-width:960px)and (max-width:1263px){.v-application .hidden-md-only{display:none!important}}@media only screen and (max-width:1263px){.v-application .hidden-md-and-down{display:none!important}}@media only screen and (min-width:960px){.v-application .hidden-md-and-up{display:none!important}}@media only screen and (min-width:1264px)and (max-width:1903px){.v-application .hidden-lg-only{display:none!important}}@media only screen and (max-width:1903px){.v-application .hidden-lg-and-down{display:none!important}}@media only screen and (min-width:1264px){.v-application .hidden-lg-and-up{display:none!important}}@media only screen and (min-width:1904px){.v-application .hidden-xl-only{display:none!important}}.d-sr-only,.d-sr-only-focusable:not(:focus){border:0!important;clip:rect(0,0,0,0)!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}.v-application .font-weight-thin{font-weight:100!important}.v-application .font-weight-light{font-weight:300!important}.v-application .font-weight-regular{font-weight:400!important}.v-application .font-weight-medium{font-weight:500!important}.v-application .font-weight-bold{font-weight:700!important}.v-application .font-weight-black{font-weight:900!important}.v-application .font-italic{font-style:italic!important}.v-application .transition-fast-out-slow-in{transition:.3s cubic-bezier(.4,0,.2,1)!important}.v-application .transition-linear-out-slow-in{transition:.3s cubic-bezier(0,0,.2,1)!important}.v-application .transition-fast-out-linear-in{transition:.3s cubic-bezier(.4,0,1,1)!important}.v-application .transition-ease-in-out{transition:.3s cubic-bezier(.4,0,.6,1)!important}.v-application .transition-fast-in-fast-out{transition:.3s cubic-bezier(.25,.8,.25,1)!important}.v-application .transition-swing{transition:.3s cubic-bezier(.25,.8,.5,1)!important}.v-application .overflow-auto{overflow:auto!important}.v-application .overflow-hidden{overflow:hidden!important}.v-application .overflow-visible{overflow:visible!important}.v-application .overflow-x-auto{overflow-x:auto!important}.v-application .overflow-x-hidden{overflow-x:hidden!important}.v-application .overflow-y-auto{overflow-y:auto!important}.v-application .overflow-y-hidden{overflow-y:hidden!important}.v-application .d-none{display:none!important}.v-application .d-inline{display:inline!important}.v-application .d-inline-block{display:inline-block!important}.v-application .d-block{display:block!important}.v-application .d-table{display:table!important}.v-application .d-table-row{display:table-row!important}.v-application .d-table-cell{display:table-cell!important}.v-application .d-flex{display:flex!important}.v-application .d-inline-flex{display:inline-flex!important}.v-application .float-none{float:none!important}.v-application .float-left{float:left!important}.v-application .float-right{float:right!important}.v-application .flex-fill{flex:1 1 auto!important}.v-application .flex-row{flex-direction:row!important}.v-application .flex-column{flex-direction:column!important}.v-application .flex-row-reverse{flex-direction:row-reverse!important}.v-application .flex-column-reverse{flex-direction:column-reverse!important}.v-application .flex-grow-0{flex-grow:0!important}.v-application .flex-grow-1{flex-grow:1!important}.v-application .flex-shrink-0{flex-shrink:0!important}.v-application .flex-shrink-1{flex-shrink:1!important}.v-application .flex-wrap{flex-wrap:wrap!important}.v-application .flex-nowrap{flex-wrap:nowrap!important}.v-application .flex-wrap-reverse{flex-wrap:wrap-reverse!important}.v-application .justify-start{justify-content:flex-start!important}.v-application .justify-end{justify-content:flex-end!important}.v-application .justify-center{justify-content:center!important}.v-application .justify-space-between{justify-content:space-between!important}.v-application .justify-space-around{justify-content:space-around!important}.v-application .align-start{align-items:flex-start!important}.v-application .align-end{align-items:flex-end!important}.v-application .align-center{align-items:center!important}.v-application .align-baseline{align-items:baseline!important}.v-application .align-stretch{align-items:stretch!important}.v-application .align-content-start{align-content:flex-start!important}.v-application .align-content-end{align-content:flex-end!important}.v-application .align-content-center{align-content:center!important}.v-application .align-content-space-between{align-content:space-between!important}.v-application .align-content-space-around{align-content:space-around!important}.v-application .align-content-stretch{align-content:stretch!important}.v-application .align-self-auto{align-self:auto!important}.v-application .align-self-start{align-self:flex-start!important}.v-application .align-self-end{align-self:flex-end!important}.v-application .align-self-center{align-self:center!important}.v-application .align-self-baseline{align-self:baseline!important}.v-application .align-self-stretch{align-self:stretch!important}.v-application .order-first{order:-1!important}.v-application .order-0{order:0!important}.v-application .order-1{order:1!important}.v-application .order-2{order:2!important}.v-application .order-3{order:3!important}.v-application .order-4{order:4!important}.v-application .order-5{order:5!important}.v-application .order-6{order:6!important}.v-application .order-7{order:7!important}.v-application .order-8{order:8!important}.v-application .order-9{order:9!important}.v-application .order-10{order:10!important}.v-application .order-11{order:11!important}.v-application .order-12{order:12!important}.v-application .order-last{order:13!important}.v-application .ma-0{margin:0!important}.v-application .ma-1{margin:4px!important}.v-application .ma-2{margin:8px!important}.v-application .ma-3{margin:12px!important}.v-application .ma-4{margin:16px!important}.v-application .ma-5{margin:20px!important}.v-application .ma-6{margin:24px!important}.v-application .ma-7{margin:28px!important}.v-application .ma-8{margin:32px!important}.v-application .ma-9{margin:36px!important}.v-application .ma-10{margin:40px!important}.v-application .ma-11{margin:44px!important}.v-application .ma-12{margin:48px!important}.v-application .ma-13{margin:52px!important}.v-application .ma-14{margin:56px!important}.v-application .ma-15{margin:60px!important}.v-application .ma-16{margin:64px!important}.v-application .ma-auto{margin:auto!important}.v-application .mx-0{margin-right:0!important;margin-left:0!important}.v-application .mx-1{margin-right:4px!important;margin-left:4px!important}.v-application .mx-2{margin-right:8px!important;margin-left:8px!important}.v-application .mx-3{margin-right:12px!important;margin-left:12px!important}.v-application .mx-4{margin-right:16px!important;margin-left:16px!important}.v-application .mx-5{margin-right:20px!important;margin-left:20px!important}.v-application .mx-6{margin-right:24px!important;margin-left:24px!important}.v-application .mx-7{margin-right:28px!important;margin-left:28px!important}.v-application .mx-8{margin-right:32px!important;margin-left:32px!important}.v-application .mx-9{margin-right:36px!important;margin-left:36px!important}.v-application .mx-10{margin-right:40px!important;margin-left:40px!important}.v-application .mx-11{margin-right:44px!important;margin-left:44px!important}.v-application .mx-12{margin-right:48px!important;margin-left:48px!important}.v-application .mx-13{margin-right:52px!important;margin-left:52px!important}.v-application .mx-14{margin-right:56px!important;margin-left:56px!important}.v-application .mx-15{margin-right:60px!important;margin-left:60px!important}.v-application .mx-16{margin-right:64px!important;margin-left:64px!important}.v-application .mx-auto{margin-right:auto!important;margin-left:auto!important}.v-application .my-0{margin-top:0!important;margin-bottom:0!important}.v-application .my-1{margin-top:4px!important;margin-bottom:4px!important}.v-application .my-2{margin-top:8px!important;margin-bottom:8px!important}.v-application .my-3{margin-top:12px!important;margin-bottom:12px!important}.v-application .my-4{margin-top:16px!important;margin-bottom:16px!important}.v-application .my-5{margin-top:20px!important;margin-bottom:20px!important}.v-application .my-6{margin-top:24px!important;margin-bottom:24px!important}.v-application .my-7{margin-top:28px!important;margin-bottom:28px!important}.v-application .my-8{margin-top:32px!important;margin-bottom:32px!important}.v-application .my-9{margin-top:36px!important;margin-bottom:36px!important}.v-application .my-10{margin-top:40px!important;margin-bottom:40px!important}.v-application .my-11{margin-top:44px!important;margin-bottom:44px!important}.v-application .my-12{margin-top:48px!important;margin-bottom:48px!important}.v-application .my-13{margin-top:52px!important;margin-bottom:52px!important}.v-application .my-14{margin-top:56px!important;margin-bottom:56px!important}.v-application .my-15{margin-top:60px!important;margin-bottom:60px!important}.v-application .my-16{margin-top:64px!important;margin-bottom:64px!important}.v-application .my-auto{margin-top:auto!important;margin-bottom:auto!important}.v-application .mt-0{margin-top:0!important}.v-application .mt-1{margin-top:4px!important}.v-application .mt-2{margin-top:8px!important}.v-application .mt-3{margin-top:12px!important}.v-application .mt-4{margin-top:16px!important}.v-application .mt-5{margin-top:20px!important}.v-application .mt-6{margin-top:24px!important}.v-application .mt-7{margin-top:28px!important}.v-application .mt-8{margin-top:32px!important}.v-application .mt-9{margin-top:36px!important}.v-application .mt-10{margin-top:40px!important}.v-application .mt-11{margin-top:44px!important}.v-application .mt-12{margin-top:48px!important}.v-application .mt-13{margin-top:52px!important}.v-application .mt-14{margin-top:56px!important}.v-application .mt-15{margin-top:60px!important}.v-application .mt-16{margin-top:64px!important}.v-application .mt-auto{margin-top:auto!important}.v-application .mr-0{margin-right:0!important}.v-application .mr-1{margin-right:4px!important}.v-application .mr-2{margin-right:8px!important}.v-application .mr-3{margin-right:12px!important}.v-application .mr-4{margin-right:16px!important}.v-application .mr-5{margin-right:20px!important}.v-application .mr-6{margin-right:24px!important}.v-application .mr-7{margin-right:28px!important}.v-application .mr-8{margin-right:32px!important}.v-application .mr-9{margin-right:36px!important}.v-application .mr-10{margin-right:40px!important}.v-application .mr-11{margin-right:44px!important}.v-application .mr-12{margin-right:48px!important}.v-application .mr-13{margin-right:52px!important}.v-application .mr-14{margin-right:56px!important}.v-application .mr-15{margin-right:60px!important}.v-application .mr-16{margin-right:64px!important}.v-application .mr-auto{margin-right:auto!important}.v-application .mb-0{margin-bottom:0!important}.v-application .mb-1{margin-bottom:4px!important}.v-application .mb-2{margin-bottom:8px!important}.v-application .mb-3{margin-bottom:12px!important}.v-application .mb-4{margin-bottom:16px!important}.v-application .mb-5{margin-bottom:20px!important}.v-application .mb-6{margin-bottom:24px!important}.v-application .mb-7{margin-bottom:28px!important}.v-application .mb-8{margin-bottom:32px!important}.v-application .mb-9{margin-bottom:36px!important}.v-application .mb-10{margin-bottom:40px!important}.v-application .mb-11{margin-bottom:44px!important}.v-application .mb-12{margin-bottom:48px!important}.v-application .mb-13{margin-bottom:52px!important}.v-application .mb-14{margin-bottom:56px!important}.v-application .mb-15{margin-bottom:60px!important}.v-application .mb-16{margin-bottom:64px!important}.v-application .mb-auto{margin-bottom:auto!important}.v-application .ml-0{margin-left:0!important}.v-application .ml-1{margin-left:4px!important}.v-application .ml-2{margin-left:8px!important}.v-application .ml-3{margin-left:12px!important}.v-application .ml-4{margin-left:16px!important}.v-application .ml-5{margin-left:20px!important}.v-application .ml-6{margin-left:24px!important}.v-application .ml-7{margin-left:28px!important}.v-application .ml-8{margin-left:32px!important}.v-application .ml-9{margin-left:36px!important}.v-application .ml-10{margin-left:40px!important}.v-application .ml-11{margin-left:44px!important}.v-application .ml-12{margin-left:48px!important}.v-application .ml-13{margin-left:52px!important}.v-application .ml-14{margin-left:56px!important}.v-application .ml-15{margin-left:60px!important}.v-application .ml-16{margin-left:64px!important}.v-application .ml-auto{margin-left:auto!important}.v-application--is-ltr .ms-0{margin-left:0!important}.v-application--is-rtl .ms-0{margin-right:0!important}.v-application--is-ltr .ms-1{margin-left:4px!important}.v-application--is-rtl .ms-1{margin-right:4px!important}.v-application--is-ltr .ms-2{margin-left:8px!important}.v-application--is-rtl .ms-2{margin-right:8px!important}.v-application--is-ltr .ms-3{margin-left:12px!important}.v-application--is-rtl .ms-3{margin-right:12px!important}.v-application--is-ltr .ms-4{margin-left:16px!important}.v-application--is-rtl .ms-4{margin-right:16px!important}.v-application--is-ltr .ms-5{margin-left:20px!important}.v-application--is-rtl .ms-5{margin-right:20px!important}.v-application--is-ltr .ms-6{margin-left:24px!important}.v-application--is-rtl .ms-6{margin-right:24px!important}.v-application--is-ltr .ms-7{margin-left:28px!important}.v-application--is-rtl .ms-7{margin-right:28px!important}.v-application--is-ltr .ms-8{margin-left:32px!important}.v-application--is-rtl .ms-8{margin-right:32px!important}.v-application--is-ltr .ms-9{margin-left:36px!important}.v-application--is-rtl .ms-9{margin-right:36px!important}.v-application--is-ltr .ms-10{margin-left:40px!important}.v-application--is-rtl .ms-10{margin-right:40px!important}.v-application--is-ltr .ms-11{margin-left:44px!important}.v-application--is-rtl .ms-11{margin-right:44px!important}.v-application--is-ltr .ms-12{margin-left:48px!important}.v-application--is-rtl .ms-12{margin-right:48px!important}.v-application--is-ltr .ms-13{margin-left:52px!important}.v-application--is-rtl .ms-13{margin-right:52px!important}.v-application--is-ltr .ms-14{margin-left:56px!important}.v-application--is-rtl .ms-14{margin-right:56px!important}.v-application--is-ltr .ms-15{margin-left:60px!important}.v-application--is-rtl .ms-15{margin-right:60px!important}.v-application--is-ltr .ms-16{margin-left:64px!important}.v-application--is-rtl .ms-16{margin-right:64px!important}.v-application--is-ltr .ms-auto{margin-left:auto!important}.v-application--is-rtl .ms-auto{margin-right:auto!important}.v-application--is-ltr .me-0{margin-right:0!important}.v-application--is-rtl .me-0{margin-left:0!important}.v-application--is-ltr .me-1{margin-right:4px!important}.v-application--is-rtl .me-1{margin-left:4px!important}.v-application--is-ltr .me-2{margin-right:8px!important}.v-application--is-rtl .me-2{margin-left:8px!important}.v-application--is-ltr .me-3{margin-right:12px!important}.v-application--is-rtl .me-3{margin-left:12px!important}.v-application--is-ltr .me-4{margin-right:16px!important}.v-application--is-rtl .me-4{margin-left:16px!important}.v-application--is-ltr .me-5{margin-right:20px!important}.v-application--is-rtl .me-5{margin-left:20px!important}.v-application--is-ltr .me-6{margin-right:24px!important}.v-application--is-rtl .me-6{margin-left:24px!important}.v-application--is-ltr .me-7{margin-right:28px!important}.v-application--is-rtl .me-7{margin-left:28px!important}.v-application--is-ltr .me-8{margin-right:32px!important}.v-application--is-rtl .me-8{margin-left:32px!important}.v-application--is-ltr .me-9{margin-right:36px!important}.v-application--is-rtl .me-9{margin-left:36px!important}.v-application--is-ltr .me-10{margin-right:40px!important}.v-application--is-rtl .me-10{margin-left:40px!important}.v-application--is-ltr .me-11{margin-right:44px!important}.v-application--is-rtl .me-11{margin-left:44px!important}.v-application--is-ltr .me-12{margin-right:48px!important}.v-application--is-rtl .me-12{margin-left:48px!important}.v-application--is-ltr .me-13{margin-right:52px!important}.v-application--is-rtl .me-13{margin-left:52px!important}.v-application--is-ltr .me-14{margin-right:56px!important}.v-application--is-rtl .me-14{margin-left:56px!important}.v-application--is-ltr .me-15{margin-right:60px!important}.v-application--is-rtl .me-15{margin-left:60px!important}.v-application--is-ltr .me-16{margin-right:64px!important}.v-application--is-rtl .me-16{margin-left:64px!important}.v-application--is-ltr .me-auto{margin-right:auto!important}.v-application--is-rtl .me-auto{margin-left:auto!important}.v-application .ma-n1{margin:-4px!important}.v-application .ma-n2{margin:-8px!important}.v-application .ma-n3{margin:-12px!important}.v-application .ma-n4{margin:-16px!important}.v-application .ma-n5{margin:-20px!important}.v-application .ma-n6{margin:-24px!important}.v-application .ma-n7{margin:-28px!important}.v-application .ma-n8{margin:-32px!important}.v-application .ma-n9{margin:-36px!important}.v-application .ma-n10{margin:-40px!important}.v-application .ma-n11{margin:-44px!important}.v-application .ma-n12{margin:-48px!important}.v-application .ma-n13{margin:-52px!important}.v-application .ma-n14{margin:-56px!important}.v-application .ma-n15{margin:-60px!important}.v-application .ma-n16{margin:-64px!important}.v-application .mx-n1{margin-right:-4px!important;margin-left:-4px!important}.v-application .mx-n2{margin-right:-8px!important;margin-left:-8px!important}.v-application .mx-n3{margin-right:-12px!important;margin-left:-12px!important}.v-application .mx-n4{margin-right:-16px!important;margin-left:-16px!important}.v-application .mx-n5{margin-right:-20px!important;margin-left:-20px!important}.v-application .mx-n6{margin-right:-24px!important;margin-left:-24px!important}.v-application .mx-n7{margin-right:-28px!important;margin-left:-28px!important}.v-application .mx-n8{margin-right:-32px!important;margin-left:-32px!important}.v-application .mx-n9{margin-right:-36px!important;margin-left:-36px!important}.v-application .mx-n10{margin-right:-40px!important;margin-left:-40px!important}.v-application .mx-n11{margin-right:-44px!important;margin-left:-44px!important}.v-application .mx-n12{margin-right:-48px!important;margin-left:-48px!important}.v-application .mx-n13{margin-right:-52px!important;margin-left:-52px!important}.v-application .mx-n14{margin-right:-56px!important;margin-left:-56px!important}.v-application .mx-n15{margin-right:-60px!important;margin-left:-60px!important}.v-application .mx-n16{margin-right:-64px!important;margin-left:-64px!important}.v-application .my-n1{margin-top:-4px!important;margin-bottom:-4px!important}.v-application .my-n2{margin-top:-8px!important;margin-bottom:-8px!important}.v-application .my-n3{margin-top:-12px!important;margin-bottom:-12px!important}.v-application .my-n4{margin-top:-16px!important;margin-bottom:-16px!important}.v-application .my-n5{margin-top:-20px!important;margin-bottom:-20px!important}.v-application .my-n6{margin-top:-24px!important;margin-bottom:-24px!important}.v-application .my-n7{margin-top:-28px!important;margin-bottom:-28px!important}.v-application .my-n8{margin-top:-32px!important;margin-bottom:-32px!important}.v-application .my-n9{margin-top:-36px!important;margin-bottom:-36px!important}.v-application .my-n10{margin-top:-40px!important;margin-bottom:-40px!important}.v-application .my-n11{margin-top:-44px!important;margin-bottom:-44px!important}.v-application .my-n12{margin-top:-48px!important;margin-bottom:-48px!important}.v-application .my-n13{margin-top:-52px!important;margin-bottom:-52px!important}.v-application .my-n14{margin-top:-56px!important;margin-bottom:-56px!important}.v-application .my-n15{margin-top:-60px!important;margin-bottom:-60px!important}.v-application .my-n16{margin-top:-64px!important;margin-bottom:-64px!important}.v-application .mt-n1{margin-top:-4px!important}.v-application .mt-n2{margin-top:-8px!important}.v-application .mt-n3{margin-top:-12px!important}.v-application .mt-n4{margin-top:-16px!important}.v-application .mt-n5{margin-top:-20px!important}.v-application .mt-n6{margin-top:-24px!important}.v-application .mt-n7{margin-top:-28px!important}.v-application .mt-n8{margin-top:-32px!important}.v-application .mt-n9{margin-top:-36px!important}.v-application .mt-n10{margin-top:-40px!important}.v-application .mt-n11{margin-top:-44px!important}.v-application .mt-n12{margin-top:-48px!important}.v-application .mt-n13{margin-top:-52px!important}.v-application .mt-n14{margin-top:-56px!important}.v-application .mt-n15{margin-top:-60px!important}.v-application .mt-n16{margin-top:-64px!important}.v-application .mr-n1{margin-right:-4px!important}.v-application .mr-n2{margin-right:-8px!important}.v-application .mr-n3{margin-right:-12px!important}.v-application .mr-n4{margin-right:-16px!important}.v-application .mr-n5{margin-right:-20px!important}.v-application .mr-n6{margin-right:-24px!important}.v-application .mr-n7{margin-right:-28px!important}.v-application .mr-n8{margin-right:-32px!important}.v-application .mr-n9{margin-right:-36px!important}.v-application .mr-n10{margin-right:-40px!important}.v-application .mr-n11{margin-right:-44px!important}.v-application .mr-n12{margin-right:-48px!important}.v-application .mr-n13{margin-right:-52px!important}.v-application .mr-n14{margin-right:-56px!important}.v-application .mr-n15{margin-right:-60px!important}.v-application .mr-n16{margin-right:-64px!important}.v-application .mb-n1{margin-bottom:-4px!important}.v-application .mb-n2{margin-bottom:-8px!important}.v-application .mb-n3{margin-bottom:-12px!important}.v-application .mb-n4{margin-bottom:-16px!important}.v-application .mb-n5{margin-bottom:-20px!important}.v-application .mb-n6{margin-bottom:-24px!important}.v-application .mb-n7{margin-bottom:-28px!important}.v-application .mb-n8{margin-bottom:-32px!important}.v-application .mb-n9{margin-bottom:-36px!important}.v-application .mb-n10{margin-bottom:-40px!important}.v-application .mb-n11{margin-bottom:-44px!important}.v-application .mb-n12{margin-bottom:-48px!important}.v-application .mb-n13{margin-bottom:-52px!important}.v-application .mb-n14{margin-bottom:-56px!important}.v-application .mb-n15{margin-bottom:-60px!important}.v-application .mb-n16{margin-bottom:-64px!important}.v-application .ml-n1{margin-left:-4px!important}.v-application .ml-n2{margin-left:-8px!important}.v-application .ml-n3{margin-left:-12px!important}.v-application .ml-n4{margin-left:-16px!important}.v-application .ml-n5{margin-left:-20px!important}.v-application .ml-n6{margin-left:-24px!important}.v-application .ml-n7{margin-left:-28px!important}.v-application .ml-n8{margin-left:-32px!important}.v-application .ml-n9{margin-left:-36px!important}.v-application .ml-n10{margin-left:-40px!important}.v-application .ml-n11{margin-left:-44px!important}.v-application .ml-n12{margin-left:-48px!important}.v-application .ml-n13{margin-left:-52px!important}.v-application .ml-n14{margin-left:-56px!important}.v-application .ml-n15{margin-left:-60px!important}.v-application .ml-n16{margin-left:-64px!important}.v-application--is-ltr .ms-n1{margin-left:-4px!important}.v-application--is-rtl .ms-n1{margin-right:-4px!important}.v-application--is-ltr .ms-n2{margin-left:-8px!important}.v-application--is-rtl .ms-n2{margin-right:-8px!important}.v-application--is-ltr .ms-n3{margin-left:-12px!important}.v-application--is-rtl .ms-n3{margin-right:-12px!important}.v-application--is-ltr .ms-n4{margin-left:-16px!important}.v-application--is-rtl .ms-n4{margin-right:-16px!important}.v-application--is-ltr .ms-n5{margin-left:-20px!important}.v-application--is-rtl .ms-n5{margin-right:-20px!important}.v-application--is-ltr .ms-n6{margin-left:-24px!important}.v-application--is-rtl .ms-n6{margin-right:-24px!important}.v-application--is-ltr .ms-n7{margin-left:-28px!important}.v-application--is-rtl .ms-n7{margin-right:-28px!important}.v-application--is-ltr .ms-n8{margin-left:-32px!important}.v-application--is-rtl .ms-n8{margin-right:-32px!important}.v-application--is-ltr .ms-n9{margin-left:-36px!important}.v-application--is-rtl .ms-n9{margin-right:-36px!important}.v-application--is-ltr .ms-n10{margin-left:-40px!important}.v-application--is-rtl .ms-n10{margin-right:-40px!important}.v-application--is-ltr .ms-n11{margin-left:-44px!important}.v-application--is-rtl .ms-n11{margin-right:-44px!important}.v-application--is-ltr .ms-n12{margin-left:-48px!important}.v-application--is-rtl .ms-n12{margin-right:-48px!important}.v-application--is-ltr .ms-n13{margin-left:-52px!important}.v-application--is-rtl .ms-n13{margin-right:-52px!important}.v-application--is-ltr .ms-n14{margin-left:-56px!important}.v-application--is-rtl .ms-n14{margin-right:-56px!important}.v-application--is-ltr .ms-n15{margin-left:-60px!important}.v-application--is-rtl .ms-n15{margin-right:-60px!important}.v-application--is-ltr .ms-n16{margin-left:-64px!important}.v-application--is-rtl .ms-n16{margin-right:-64px!important}.v-application--is-ltr .me-n1{margin-right:-4px!important}.v-application--is-rtl .me-n1{margin-left:-4px!important}.v-application--is-ltr .me-n2{margin-right:-8px!important}.v-application--is-rtl .me-n2{margin-left:-8px!important}.v-application--is-ltr .me-n3{margin-right:-12px!important}.v-application--is-rtl .me-n3{margin-left:-12px!important}.v-application--is-ltr .me-n4{margin-right:-16px!important}.v-application--is-rtl .me-n4{margin-left:-16px!important}.v-application--is-ltr .me-n5{margin-right:-20px!important}.v-application--is-rtl .me-n5{margin-left:-20px!important}.v-application--is-ltr .me-n6{margin-right:-24px!important}.v-application--is-rtl .me-n6{margin-left:-24px!important}.v-application--is-ltr .me-n7{margin-right:-28px!important}.v-application--is-rtl .me-n7{margin-left:-28px!important}.v-application--is-ltr .me-n8{margin-right:-32px!important}.v-application--is-rtl .me-n8{margin-left:-32px!important}.v-application--is-ltr .me-n9{margin-right:-36px!important}.v-application--is-rtl .me-n9{margin-left:-36px!important}.v-application--is-ltr .me-n10{margin-right:-40px!important}.v-application--is-rtl .me-n10{margin-left:-40px!important}.v-application--is-ltr .me-n11{margin-right:-44px!important}.v-application--is-rtl .me-n11{margin-left:-44px!important}.v-application--is-ltr .me-n12{margin-right:-48px!important}.v-application--is-rtl .me-n12{margin-left:-48px!important}.v-application--is-ltr .me-n13{margin-right:-52px!important}.v-application--is-rtl .me-n13{margin-left:-52px!important}.v-application--is-ltr .me-n14{margin-right:-56px!important}.v-application--is-rtl .me-n14{margin-left:-56px!important}.v-application--is-ltr .me-n15{margin-right:-60px!important}.v-application--is-rtl .me-n15{margin-left:-60px!important}.v-application--is-ltr .me-n16{margin-right:-64px!important}.v-application--is-rtl .me-n16{margin-left:-64px!important}.v-application .pa-0{padding:0!important}.v-application .pa-1{padding:4px!important}.v-application .pa-2{padding:8px!important}.v-application .pa-3{padding:12px!important}.v-application .pa-4{padding:16px!important}.v-application .pa-5{padding:20px!important}.v-application .pa-6{padding:24px!important}.v-application .pa-7{padding:28px!important}.v-application .pa-8{padding:32px!important}.v-application .pa-9{padding:36px!important}.v-application .pa-10{padding:40px!important}.v-application .pa-11{padding:44px!important}.v-application .pa-12{padding:48px!important}.v-application .pa-13{padding:52px!important}.v-application .pa-14{padding:56px!important}.v-application .pa-15{padding:60px!important}.v-application .pa-16{padding:64px!important}.v-application .px-0{padding-right:0!important;padding-left:0!important}.v-application .px-1{padding-right:4px!important;padding-left:4px!important}.v-application .px-2{padding-right:8px!important;padding-left:8px!important}.v-application .px-3{padding-right:12px!important;padding-left:12px!important}.v-application .px-4{padding-right:16px!important;padding-left:16px!important}.v-application .px-5{padding-right:20px!important;padding-left:20px!important}.v-application .px-6{padding-right:24px!important;padding-left:24px!important}.v-application .px-7{padding-right:28px!important;padding-left:28px!important}.v-application .px-8{padding-right:32px!important;padding-left:32px!important}.v-application .px-9{padding-right:36px!important;padding-left:36px!important}.v-application .px-10{padding-right:40px!important;padding-left:40px!important}.v-application .px-11{padding-right:44px!important;padding-left:44px!important}.v-application .px-12{padding-right:48px!important;padding-left:48px!important}.v-application .px-13{padding-right:52px!important;padding-left:52px!important}.v-application .px-14{padding-right:56px!important;padding-left:56px!important}.v-application .px-15{padding-right:60px!important;padding-left:60px!important}.v-application .px-16{padding-right:64px!important;padding-left:64px!important}.v-application .py-0{padding-top:0!important;padding-bottom:0!important}.v-application .py-1{padding-top:4px!important;padding-bottom:4px!important}.v-application .py-2{padding-top:8px!important;padding-bottom:8px!important}.v-application .py-3{padding-top:12px!important;padding-bottom:12px!important}.v-application .py-4{padding-top:16px!important;padding-bottom:16px!important}.v-application .py-5{padding-top:20px!important;padding-bottom:20px!important}.v-application .py-6{padding-top:24px!important;padding-bottom:24px!important}.v-application .py-7{padding-top:28px!important;padding-bottom:28px!important}.v-application .py-8{padding-top:32px!important;padding-bottom:32px!important}.v-application .py-9{padding-top:36px!important;padding-bottom:36px!important}.v-application .py-10{padding-top:40px!important;padding-bottom:40px!important}.v-application .py-11{padding-top:44px!important;padding-bottom:44px!important}.v-application .py-12{padding-top:48px!important;padding-bottom:48px!important}.v-application .py-13{padding-top:52px!important;padding-bottom:52px!important}.v-application .py-14{padding-top:56px!important;padding-bottom:56px!important}.v-application .py-15{padding-top:60px!important;padding-bottom:60px!important}.v-application .py-16{padding-top:64px!important;padding-bottom:64px!important}.v-application .pt-0{padding-top:0!important}.v-application .pt-1{padding-top:4px!important}.v-application .pt-2{padding-top:8px!important}.v-application .pt-3{padding-top:12px!important}.v-application .pt-4{padding-top:16px!important}.v-application .pt-5{padding-top:20px!important}.v-application .pt-6{padding-top:24px!important}.v-application .pt-7{padding-top:28px!important}.v-application .pt-8{padding-top:32px!important}.v-application .pt-9{padding-top:36px!important}.v-application .pt-10{padding-top:40px!important}.v-application .pt-11{padding-top:44px!important}.v-application .pt-12{padding-top:48px!important}.v-application .pt-13{padding-top:52px!important}.v-application .pt-14{padding-top:56px!important}.v-application .pt-15{padding-top:60px!important}.v-application .pt-16{padding-top:64px!important}.v-application .pr-0{padding-right:0!important}.v-application .pr-1{padding-right:4px!important}.v-application .pr-2{padding-right:8px!important}.v-application .pr-3{padding-right:12px!important}.v-application .pr-4{padding-right:16px!important}.v-application .pr-5{padding-right:20px!important}.v-application .pr-6{padding-right:24px!important}.v-application .pr-7{padding-right:28px!important}.v-application .pr-8{padding-right:32px!important}.v-application .pr-9{padding-right:36px!important}.v-application .pr-10{padding-right:40px!important}.v-application .pr-11{padding-right:44px!important}.v-application .pr-12{padding-right:48px!important}.v-application .pr-13{padding-right:52px!important}.v-application .pr-14{padding-right:56px!important}.v-application .pr-15{padding-right:60px!important}.v-application .pr-16{padding-right:64px!important}.v-application .pb-0{padding-bottom:0!important}.v-application .pb-1{padding-bottom:4px!important}.v-application .pb-2{padding-bottom:8px!important}.v-application .pb-3{padding-bottom:12px!important}.v-application .pb-4{padding-bottom:16px!important}.v-application .pb-5{padding-bottom:20px!important}.v-application .pb-6{padding-bottom:24px!important}.v-application .pb-7{padding-bottom:28px!important}.v-application .pb-8{padding-bottom:32px!important}.v-application .pb-9{padding-bottom:36px!important}.v-application .pb-10{padding-bottom:40px!important}.v-application .pb-11{padding-bottom:44px!important}.v-application .pb-12{padding-bottom:48px!important}.v-application .pb-13{padding-bottom:52px!important}.v-application .pb-14{padding-bottom:56px!important}.v-application .pb-15{padding-bottom:60px!important}.v-application .pb-16{padding-bottom:64px!important}.v-application .pl-0{padding-left:0!important}.v-application .pl-1{padding-left:4px!important}.v-application .pl-2{padding-left:8px!important}.v-application .pl-3{padding-left:12px!important}.v-application .pl-4{padding-left:16px!important}.v-application .pl-5{padding-left:20px!important}.v-application .pl-6{padding-left:24px!important}.v-application .pl-7{padding-left:28px!important}.v-application .pl-8{padding-left:32px!important}.v-application .pl-9{padding-left:36px!important}.v-application .pl-10{padding-left:40px!important}.v-application .pl-11{padding-left:44px!important}.v-application .pl-12{padding-left:48px!important}.v-application .pl-13{padding-left:52px!important}.v-application .pl-14{padding-left:56px!important}.v-application .pl-15{padding-left:60px!important}.v-application .pl-16{padding-left:64px!important}.v-application--is-ltr .ps-0{padding-left:0!important}.v-application--is-rtl .ps-0{padding-right:0!important}.v-application--is-ltr .ps-1{padding-left:4px!important}.v-application--is-rtl .ps-1{padding-right:4px!important}.v-application--is-ltr .ps-2{padding-left:8px!important}.v-application--is-rtl .ps-2{padding-right:8px!important}.v-application--is-ltr .ps-3{padding-left:12px!important}.v-application--is-rtl .ps-3{padding-right:12px!important}.v-application--is-ltr .ps-4{padding-left:16px!important}.v-application--is-rtl .ps-4{padding-right:16px!important}.v-application--is-ltr .ps-5{padding-left:20px!important}.v-application--is-rtl .ps-5{padding-right:20px!important}.v-application--is-ltr .ps-6{padding-left:24px!important}.v-application--is-rtl .ps-6{padding-right:24px!important}.v-application--is-ltr .ps-7{padding-left:28px!important}.v-application--is-rtl .ps-7{padding-right:28px!important}.v-application--is-ltr .ps-8{padding-left:32px!important}.v-application--is-rtl .ps-8{padding-right:32px!important}.v-application--is-ltr .ps-9{padding-left:36px!important}.v-application--is-rtl .ps-9{padding-right:36px!important}.v-application--is-ltr .ps-10{padding-left:40px!important}.v-application--is-rtl .ps-10{padding-right:40px!important}.v-application--is-ltr .ps-11{padding-left:44px!important}.v-application--is-rtl .ps-11{padding-right:44px!important}.v-application--is-ltr .ps-12{padding-left:48px!important}.v-application--is-rtl .ps-12{padding-right:48px!important}.v-application--is-ltr .ps-13{padding-left:52px!important}.v-application--is-rtl .ps-13{padding-right:52px!important}.v-application--is-ltr .ps-14{padding-left:56px!important}.v-application--is-rtl .ps-14{padding-right:56px!important}.v-application--is-ltr .ps-15{padding-left:60px!important}.v-application--is-rtl .ps-15{padding-right:60px!important}.v-application--is-ltr .ps-16{padding-left:64px!important}.v-application--is-rtl .ps-16{padding-right:64px!important}.v-application--is-ltr .pe-0{padding-right:0!important}.v-application--is-rtl .pe-0{padding-left:0!important}.v-application--is-ltr .pe-1{padding-right:4px!important}.v-application--is-rtl .pe-1{padding-left:4px!important}.v-application--is-ltr .pe-2{padding-right:8px!important}.v-application--is-rtl .pe-2{padding-left:8px!important}.v-application--is-ltr .pe-3{padding-right:12px!important}.v-application--is-rtl .pe-3{padding-left:12px!important}.v-application--is-ltr .pe-4{padding-right:16px!important}.v-application--is-rtl .pe-4{padding-left:16px!important}.v-application--is-ltr .pe-5{padding-right:20px!important}.v-application--is-rtl .pe-5{padding-left:20px!important}.v-application--is-ltr .pe-6{padding-right:24px!important}.v-application--is-rtl .pe-6{padding-left:24px!important}.v-application--is-ltr .pe-7{padding-right:28px!important}.v-application--is-rtl .pe-7{padding-left:28px!important}.v-application--is-ltr .pe-8{padding-right:32px!important}.v-application--is-rtl .pe-8{padding-left:32px!important}.v-application--is-ltr .pe-9{padding-right:36px!important}.v-application--is-rtl .pe-9{padding-left:36px!important}.v-application--is-ltr .pe-10{padding-right:40px!important}.v-application--is-rtl .pe-10{padding-left:40px!important}.v-application--is-ltr .pe-11{padding-right:44px!important}.v-application--is-rtl .pe-11{padding-left:44px!important}.v-application--is-ltr .pe-12{padding-right:48px!important}.v-application--is-rtl .pe-12{padding-left:48px!important}.v-application--is-ltr .pe-13{padding-right:52px!important}.v-application--is-rtl .pe-13{padding-left:52px!important}.v-application--is-ltr .pe-14{padding-right:56px!important}.v-application--is-rtl .pe-14{padding-left:56px!important}.v-application--is-ltr .pe-15{padding-right:60px!important}.v-application--is-rtl .pe-15{padding-left:60px!important}.v-application--is-ltr .pe-16{padding-right:64px!important}.v-application--is-rtl .pe-16{padding-left:64px!important}.v-application .rounded-0{border-radius:0!important}.v-application .rounded-sm{border-radius:2px!important}.v-application .rounded{border-radius:4px!important}.v-application .rounded-lg{border-radius:8px!important}.v-application .rounded-xl{border-radius:24px!important}.v-application .rounded-pill{border-radius:9999px!important}.v-application .rounded-circle{border-radius:50%!important}.v-application .rounded-t-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.v-application .rounded-t-sm{border-top-left-radius:2px!important;border-top-right-radius:2px!important}.v-application .rounded-t{border-top-left-radius:4px!important;border-top-right-radius:4px!important}.v-application .rounded-t-lg{border-top-left-radius:8px!important;border-top-right-radius:8px!important}.v-application .rounded-t-xl{border-top-left-radius:24px!important;border-top-right-radius:24px!important}.v-application .rounded-t-pill{border-top-left-radius:9999px!important;border-top-right-radius:9999px!important}.v-application .rounded-t-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.v-application .rounded-r-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.v-application .rounded-r-sm{border-top-right-radius:2px!important;border-bottom-right-radius:2px!important}.v-application .rounded-r{border-top-right-radius:4px!important;border-bottom-right-radius:4px!important}.v-application .rounded-r-lg{border-top-right-radius:8px!important;border-bottom-right-radius:8px!important}.v-application .rounded-r-xl{border-top-right-radius:24px!important;border-bottom-right-radius:24px!important}.v-application .rounded-r-pill{border-top-right-radius:9999px!important;border-bottom-right-radius:9999px!important}.v-application .rounded-r-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.v-application .rounded-b-0{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}.v-application .rounded-b-sm{border-bottom-left-radius:2px!important;border-bottom-right-radius:2px!important}.v-application .rounded-b{border-bottom-left-radius:4px!important;border-bottom-right-radius:4px!important}.v-application .rounded-b-lg{border-bottom-left-radius:8px!important;border-bottom-right-radius:8px!important}.v-application .rounded-b-xl{border-bottom-left-radius:24px!important;border-bottom-right-radius:24px!important}.v-application .rounded-b-pill{border-bottom-left-radius:9999px!important;border-bottom-right-radius:9999px!important}.v-application .rounded-b-circle{border-bottom-left-radius:50%!important;border-bottom-right-radius:50%!important}.v-application .rounded-l-0{border-top-left-radius:0!important;border-bottom-left-radius:0!important}.v-application .rounded-l-sm{border-top-left-radius:2px!important;border-bottom-left-radius:2px!important}.v-application .rounded-l{border-top-left-radius:4px!important;border-bottom-left-radius:4px!important}.v-application .rounded-l-lg{border-top-left-radius:8px!important;border-bottom-left-radius:8px!important}.v-application .rounded-l-xl{border-top-left-radius:24px!important;border-bottom-left-radius:24px!important}.v-application .rounded-l-pill{border-top-left-radius:9999px!important;border-bottom-left-radius:9999px!important}.v-application .rounded-l-circle{border-top-left-radius:50%!important;border-bottom-left-radius:50%!important}.v-application .rounded-tl-0{border-top-left-radius:0!important}.v-application .rounded-tl-sm{border-top-left-radius:2px!important}.v-application .rounded-tl{border-top-left-radius:4px!important}.v-application .rounded-tl-lg{border-top-left-radius:8px!important}.v-application .rounded-tl-xl{border-top-left-radius:24px!important}.v-application .rounded-tl-pill{border-top-left-radius:9999px!important}.v-application .rounded-tl-circle{border-top-left-radius:50%!important}.v-application .rounded-tr-0{border-top-right-radius:0!important}.v-application .rounded-tr-sm{border-top-right-radius:2px!important}.v-application .rounded-tr{border-top-right-radius:4px!important}.v-application .rounded-tr-lg{border-top-right-radius:8px!important}.v-application .rounded-tr-xl{border-top-right-radius:24px!important}.v-application .rounded-tr-pill{border-top-right-radius:9999px!important}.v-application .rounded-tr-circle{border-top-right-radius:50%!important}.v-application .rounded-br-0{border-bottom-right-radius:0!important}.v-application .rounded-br-sm{border-bottom-right-radius:2px!important}.v-application .rounded-br{border-bottom-right-radius:4px!important}.v-application .rounded-br-lg{border-bottom-right-radius:8px!important}.v-application .rounded-br-xl{border-bottom-right-radius:24px!important}.v-application .rounded-br-pill{border-bottom-right-radius:9999px!important}.v-application .rounded-br-circle{border-bottom-right-radius:50%!important}.v-application .rounded-bl-0{border-bottom-left-radius:0!important}.v-application .rounded-bl-sm{border-bottom-left-radius:2px!important}.v-application .rounded-bl{border-bottom-left-radius:4px!important}.v-application .rounded-bl-lg{border-bottom-left-radius:8px!important}.v-application .rounded-bl-xl{border-bottom-left-radius:24px!important}.v-application .rounded-bl-pill{border-bottom-left-radius:9999px!important}.v-application .rounded-bl-circle{border-bottom-left-radius:50%!important}.v-application .text-left{text-align:left!important}.v-application .text-right{text-align:right!important}.v-application .text-center{text-align:center!important}.v-application .text-justify{text-align:justify!important}.v-application .text-start{text-align:start!important}.v-application .text-end{text-align:end!important}.v-application .text-decoration-line-through{text-decoration:line-through!important}.v-application .text-decoration-none{text-decoration:none!important}.v-application .text-decoration-overline{text-decoration:overline!important}.v-application .text-decoration-underline{text-decoration:underline!important}.v-application .text-wrap{white-space:normal!important}.v-application .text-no-wrap{white-space:nowrap!important}.v-application .text-break{overflow-wrap:break-word!important;word-break:break-word!important}.v-application .text-truncate{white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important}.v-application .text-none{text-transform:none!important}.v-application .text-capitalize{text-transform:capitalize!important}.v-application .text-lowercase{text-transform:lowercase!important}.v-application .text-uppercase{text-transform:uppercase!important}.v-application .text-h1{font-size:6rem!important;line-height:6rem;letter-spacing:-.015625em!important}.v-application .text-h1,.v-application .text-h2{font-weight:300;font-family:Roboto,sans-serif!important}.v-application .text-h2{font-size:3.75rem!important;line-height:3.75rem;letter-spacing:-.0083333333em!important}.v-application .text-h3{font-size:3rem!important;line-height:3.125rem;letter-spacing:normal!important}.v-application .text-h3,.v-application .text-h4{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .text-h4{font-size:2.125rem!important;line-height:2.5rem;letter-spacing:.0073529412em!important}.v-application .text-h5{font-size:1.5rem!important;font-weight:400;letter-spacing:normal!important}.v-application .text-h5,.v-application .text-h6{line-height:2rem;font-family:Roboto,sans-serif!important}.v-application .text-h6{font-size:1.25rem!important;font-weight:500;letter-spacing:.0125em!important}.v-application .text-subtitle-1{font-size:1rem!important;font-weight:400;line-height:1.75rem;letter-spacing:.009375em!important;font-family:Roboto,sans-serif!important}.v-application .text-subtitle-2{font-size:.875rem!important;font-weight:500;line-height:1.375rem;letter-spacing:.0071428571em!important;font-family:Roboto,sans-serif!important}.v-application .text-body-1{font-size:1rem!important;font-weight:400;line-height:1.5rem;letter-spacing:.03125em!important;font-family:Roboto,sans-serif!important}.v-application .text-body-2{font-weight:400;line-height:1.25rem;letter-spacing:.0178571429em!important}.v-application .text-body-2,.v-application .text-button{font-size:.875rem!important;font-family:Roboto,sans-serif!important}.v-application .text-button{font-weight:500;line-height:2.25rem;letter-spacing:.0892857143em!important;text-transform:uppercase!important}.v-application .text-caption{font-weight:400;line-height:1.25rem;letter-spacing:.0333333333em!important}.v-application .text-caption,.v-application .text-overline{font-size:.75rem!important;font-family:Roboto,sans-serif!important}.v-application .text-overline{font-weight:500;line-height:2rem;letter-spacing:.1666666667em!important;text-transform:uppercase!important}@media(min-width:600px){.v-application .d-sm-none{display:none!important}.v-application .d-sm-inline{display:inline!important}.v-application .d-sm-inline-block{display:inline-block!important}.v-application .d-sm-block{display:block!important}.v-application .d-sm-table{display:table!important}.v-application .d-sm-table-row{display:table-row!important}.v-application .d-sm-table-cell{display:table-cell!important}.v-application .d-sm-flex{display:flex!important}.v-application .d-sm-inline-flex{display:inline-flex!important}.v-application .float-sm-none{float:none!important}.v-application .float-sm-left{float:left!important}.v-application .float-sm-right{float:right!important}.v-application .flex-sm-fill{flex:1 1 auto!important}.v-application .flex-sm-row{flex-direction:row!important}.v-application .flex-sm-column{flex-direction:column!important}.v-application .flex-sm-row-reverse{flex-direction:row-reverse!important}.v-application .flex-sm-column-reverse{flex-direction:column-reverse!important}.v-application .flex-sm-grow-0{flex-grow:0!important}.v-application .flex-sm-grow-1{flex-grow:1!important}.v-application .flex-sm-shrink-0{flex-shrink:0!important}.v-application .flex-sm-shrink-1{flex-shrink:1!important}.v-application .flex-sm-wrap{flex-wrap:wrap!important}.v-application .flex-sm-nowrap{flex-wrap:nowrap!important}.v-application .flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.v-application .justify-sm-start{justify-content:flex-start!important}.v-application .justify-sm-end{justify-content:flex-end!important}.v-application .justify-sm-center{justify-content:center!important}.v-application .justify-sm-space-between{justify-content:space-between!important}.v-application .justify-sm-space-around{justify-content:space-around!important}.v-application .align-sm-start{align-items:flex-start!important}.v-application .align-sm-end{align-items:flex-end!important}.v-application .align-sm-center{align-items:center!important}.v-application .align-sm-baseline{align-items:baseline!important}.v-application .align-sm-stretch{align-items:stretch!important}.v-application .align-content-sm-start{align-content:flex-start!important}.v-application .align-content-sm-end{align-content:flex-end!important}.v-application .align-content-sm-center{align-content:center!important}.v-application .align-content-sm-space-between{align-content:space-between!important}.v-application .align-content-sm-space-around{align-content:space-around!important}.v-application .align-content-sm-stretch{align-content:stretch!important}.v-application .align-self-sm-auto{align-self:auto!important}.v-application .align-self-sm-start{align-self:flex-start!important}.v-application .align-self-sm-end{align-self:flex-end!important}.v-application .align-self-sm-center{align-self:center!important}.v-application .align-self-sm-baseline{align-self:baseline!important}.v-application .align-self-sm-stretch{align-self:stretch!important}.v-application .order-sm-first{order:-1!important}.v-application .order-sm-0{order:0!important}.v-application .order-sm-1{order:1!important}.v-application .order-sm-2{order:2!important}.v-application .order-sm-3{order:3!important}.v-application .order-sm-4{order:4!important}.v-application .order-sm-5{order:5!important}.v-application .order-sm-6{order:6!important}.v-application .order-sm-7{order:7!important}.v-application .order-sm-8{order:8!important}.v-application .order-sm-9{order:9!important}.v-application .order-sm-10{order:10!important}.v-application .order-sm-11{order:11!important}.v-application .order-sm-12{order:12!important}.v-application .order-sm-last{order:13!important}.v-application .ma-sm-0{margin:0!important}.v-application .ma-sm-1{margin:4px!important}.v-application .ma-sm-2{margin:8px!important}.v-application .ma-sm-3{margin:12px!important}.v-application .ma-sm-4{margin:16px!important}.v-application .ma-sm-5{margin:20px!important}.v-application .ma-sm-6{margin:24px!important}.v-application .ma-sm-7{margin:28px!important}.v-application .ma-sm-8{margin:32px!important}.v-application .ma-sm-9{margin:36px!important}.v-application .ma-sm-10{margin:40px!important}.v-application .ma-sm-11{margin:44px!important}.v-application .ma-sm-12{margin:48px!important}.v-application .ma-sm-13{margin:52px!important}.v-application .ma-sm-14{margin:56px!important}.v-application .ma-sm-15{margin:60px!important}.v-application .ma-sm-16{margin:64px!important}.v-application .ma-sm-auto{margin:auto!important}.v-application .mx-sm-0{margin-right:0!important;margin-left:0!important}.v-application .mx-sm-1{margin-right:4px!important;margin-left:4px!important}.v-application .mx-sm-2{margin-right:8px!important;margin-left:8px!important}.v-application .mx-sm-3{margin-right:12px!important;margin-left:12px!important}.v-application .mx-sm-4{margin-right:16px!important;margin-left:16px!important}.v-application .mx-sm-5{margin-right:20px!important;margin-left:20px!important}.v-application .mx-sm-6{margin-right:24px!important;margin-left:24px!important}.v-application .mx-sm-7{margin-right:28px!important;margin-left:28px!important}.v-application .mx-sm-8{margin-right:32px!important;margin-left:32px!important}.v-application .mx-sm-9{margin-right:36px!important;margin-left:36px!important}.v-application .mx-sm-10{margin-right:40px!important;margin-left:40px!important}.v-application .mx-sm-11{margin-right:44px!important;margin-left:44px!important}.v-application .mx-sm-12{margin-right:48px!important;margin-left:48px!important}.v-application .mx-sm-13{margin-right:52px!important;margin-left:52px!important}.v-application .mx-sm-14{margin-right:56px!important;margin-left:56px!important}.v-application .mx-sm-15{margin-right:60px!important;margin-left:60px!important}.v-application .mx-sm-16{margin-right:64px!important;margin-left:64px!important}.v-application .mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.v-application .my-sm-0{margin-top:0!important;margin-bottom:0!important}.v-application .my-sm-1{margin-top:4px!important;margin-bottom:4px!important}.v-application .my-sm-2{margin-top:8px!important;margin-bottom:8px!important}.v-application .my-sm-3{margin-top:12px!important;margin-bottom:12px!important}.v-application .my-sm-4{margin-top:16px!important;margin-bottom:16px!important}.v-application .my-sm-5{margin-top:20px!important;margin-bottom:20px!important}.v-application .my-sm-6{margin-top:24px!important;margin-bottom:24px!important}.v-application .my-sm-7{margin-top:28px!important;margin-bottom:28px!important}.v-application .my-sm-8{margin-top:32px!important;margin-bottom:32px!important}.v-application .my-sm-9{margin-top:36px!important;margin-bottom:36px!important}.v-application .my-sm-10{margin-top:40px!important;margin-bottom:40px!important}.v-application .my-sm-11{margin-top:44px!important;margin-bottom:44px!important}.v-application .my-sm-12{margin-top:48px!important;margin-bottom:48px!important}.v-application .my-sm-13{margin-top:52px!important;margin-bottom:52px!important}.v-application .my-sm-14{margin-top:56px!important;margin-bottom:56px!important}.v-application .my-sm-15{margin-top:60px!important;margin-bottom:60px!important}.v-application .my-sm-16{margin-top:64px!important;margin-bottom:64px!important}.v-application .my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.v-application .mt-sm-0{margin-top:0!important}.v-application .mt-sm-1{margin-top:4px!important}.v-application .mt-sm-2{margin-top:8px!important}.v-application .mt-sm-3{margin-top:12px!important}.v-application .mt-sm-4{margin-top:16px!important}.v-application .mt-sm-5{margin-top:20px!important}.v-application .mt-sm-6{margin-top:24px!important}.v-application .mt-sm-7{margin-top:28px!important}.v-application .mt-sm-8{margin-top:32px!important}.v-application .mt-sm-9{margin-top:36px!important}.v-application .mt-sm-10{margin-top:40px!important}.v-application .mt-sm-11{margin-top:44px!important}.v-application .mt-sm-12{margin-top:48px!important}.v-application .mt-sm-13{margin-top:52px!important}.v-application .mt-sm-14{margin-top:56px!important}.v-application .mt-sm-15{margin-top:60px!important}.v-application .mt-sm-16{margin-top:64px!important}.v-application .mt-sm-auto{margin-top:auto!important}.v-application .mr-sm-0{margin-right:0!important}.v-application .mr-sm-1{margin-right:4px!important}.v-application .mr-sm-2{margin-right:8px!important}.v-application .mr-sm-3{margin-right:12px!important}.v-application .mr-sm-4{margin-right:16px!important}.v-application .mr-sm-5{margin-right:20px!important}.v-application .mr-sm-6{margin-right:24px!important}.v-application .mr-sm-7{margin-right:28px!important}.v-application .mr-sm-8{margin-right:32px!important}.v-application .mr-sm-9{margin-right:36px!important}.v-application .mr-sm-10{margin-right:40px!important}.v-application .mr-sm-11{margin-right:44px!important}.v-application .mr-sm-12{margin-right:48px!important}.v-application .mr-sm-13{margin-right:52px!important}.v-application .mr-sm-14{margin-right:56px!important}.v-application .mr-sm-15{margin-right:60px!important}.v-application .mr-sm-16{margin-right:64px!important}.v-application .mr-sm-auto{margin-right:auto!important}.v-application .mb-sm-0{margin-bottom:0!important}.v-application .mb-sm-1{margin-bottom:4px!important}.v-application .mb-sm-2{margin-bottom:8px!important}.v-application .mb-sm-3{margin-bottom:12px!important}.v-application .mb-sm-4{margin-bottom:16px!important}.v-application .mb-sm-5{margin-bottom:20px!important}.v-application .mb-sm-6{margin-bottom:24px!important}.v-application .mb-sm-7{margin-bottom:28px!important}.v-application .mb-sm-8{margin-bottom:32px!important}.v-application .mb-sm-9{margin-bottom:36px!important}.v-application .mb-sm-10{margin-bottom:40px!important}.v-application .mb-sm-11{margin-bottom:44px!important}.v-application .mb-sm-12{margin-bottom:48px!important}.v-application .mb-sm-13{margin-bottom:52px!important}.v-application .mb-sm-14{margin-bottom:56px!important}.v-application .mb-sm-15{margin-bottom:60px!important}.v-application .mb-sm-16{margin-bottom:64px!important}.v-application .mb-sm-auto{margin-bottom:auto!important}.v-application .ml-sm-0{margin-left:0!important}.v-application .ml-sm-1{margin-left:4px!important}.v-application .ml-sm-2{margin-left:8px!important}.v-application .ml-sm-3{margin-left:12px!important}.v-application .ml-sm-4{margin-left:16px!important}.v-application .ml-sm-5{margin-left:20px!important}.v-application .ml-sm-6{margin-left:24px!important}.v-application .ml-sm-7{margin-left:28px!important}.v-application .ml-sm-8{margin-left:32px!important}.v-application .ml-sm-9{margin-left:36px!important}.v-application .ml-sm-10{margin-left:40px!important}.v-application .ml-sm-11{margin-left:44px!important}.v-application .ml-sm-12{margin-left:48px!important}.v-application .ml-sm-13{margin-left:52px!important}.v-application .ml-sm-14{margin-left:56px!important}.v-application .ml-sm-15{margin-left:60px!important}.v-application .ml-sm-16{margin-left:64px!important}.v-application .ml-sm-auto{margin-left:auto!important}.v-application--is-ltr .ms-sm-0{margin-left:0!important}.v-application--is-rtl .ms-sm-0{margin-right:0!important}.v-application--is-ltr .ms-sm-1{margin-left:4px!important}.v-application--is-rtl .ms-sm-1{margin-right:4px!important}.v-application--is-ltr .ms-sm-2{margin-left:8px!important}.v-application--is-rtl .ms-sm-2{margin-right:8px!important}.v-application--is-ltr .ms-sm-3{margin-left:12px!important}.v-application--is-rtl .ms-sm-3{margin-right:12px!important}.v-application--is-ltr .ms-sm-4{margin-left:16px!important}.v-application--is-rtl .ms-sm-4{margin-right:16px!important}.v-application--is-ltr .ms-sm-5{margin-left:20px!important}.v-application--is-rtl .ms-sm-5{margin-right:20px!important}.v-application--is-ltr .ms-sm-6{margin-left:24px!important}.v-application--is-rtl .ms-sm-6{margin-right:24px!important}.v-application--is-ltr .ms-sm-7{margin-left:28px!important}.v-application--is-rtl .ms-sm-7{margin-right:28px!important}.v-application--is-ltr .ms-sm-8{margin-left:32px!important}.v-application--is-rtl .ms-sm-8{margin-right:32px!important}.v-application--is-ltr .ms-sm-9{margin-left:36px!important}.v-application--is-rtl .ms-sm-9{margin-right:36px!important}.v-application--is-ltr .ms-sm-10{margin-left:40px!important}.v-application--is-rtl .ms-sm-10{margin-right:40px!important}.v-application--is-ltr .ms-sm-11{margin-left:44px!important}.v-application--is-rtl .ms-sm-11{margin-right:44px!important}.v-application--is-ltr .ms-sm-12{margin-left:48px!important}.v-application--is-rtl .ms-sm-12{margin-right:48px!important}.v-application--is-ltr .ms-sm-13{margin-left:52px!important}.v-application--is-rtl .ms-sm-13{margin-right:52px!important}.v-application--is-ltr .ms-sm-14{margin-left:56px!important}.v-application--is-rtl .ms-sm-14{margin-right:56px!important}.v-application--is-ltr .ms-sm-15{margin-left:60px!important}.v-application--is-rtl .ms-sm-15{margin-right:60px!important}.v-application--is-ltr .ms-sm-16{margin-left:64px!important}.v-application--is-rtl .ms-sm-16{margin-right:64px!important}.v-application--is-ltr .ms-sm-auto{margin-left:auto!important}.v-application--is-rtl .ms-sm-auto{margin-right:auto!important}.v-application--is-ltr .me-sm-0{margin-right:0!important}.v-application--is-rtl .me-sm-0{margin-left:0!important}.v-application--is-ltr .me-sm-1{margin-right:4px!important}.v-application--is-rtl .me-sm-1{margin-left:4px!important}.v-application--is-ltr .me-sm-2{margin-right:8px!important}.v-application--is-rtl .me-sm-2{margin-left:8px!important}.v-application--is-ltr .me-sm-3{margin-right:12px!important}.v-application--is-rtl .me-sm-3{margin-left:12px!important}.v-application--is-ltr .me-sm-4{margin-right:16px!important}.v-application--is-rtl .me-sm-4{margin-left:16px!important}.v-application--is-ltr .me-sm-5{margin-right:20px!important}.v-application--is-rtl .me-sm-5{margin-left:20px!important}.v-application--is-ltr .me-sm-6{margin-right:24px!important}.v-application--is-rtl .me-sm-6{margin-left:24px!important}.v-application--is-ltr .me-sm-7{margin-right:28px!important}.v-application--is-rtl .me-sm-7{margin-left:28px!important}.v-application--is-ltr .me-sm-8{margin-right:32px!important}.v-application--is-rtl .me-sm-8{margin-left:32px!important}.v-application--is-ltr .me-sm-9{margin-right:36px!important}.v-application--is-rtl .me-sm-9{margin-left:36px!important}.v-application--is-ltr .me-sm-10{margin-right:40px!important}.v-application--is-rtl .me-sm-10{margin-left:40px!important}.v-application--is-ltr .me-sm-11{margin-right:44px!important}.v-application--is-rtl .me-sm-11{margin-left:44px!important}.v-application--is-ltr .me-sm-12{margin-right:48px!important}.v-application--is-rtl .me-sm-12{margin-left:48px!important}.v-application--is-ltr .me-sm-13{margin-right:52px!important}.v-application--is-rtl .me-sm-13{margin-left:52px!important}.v-application--is-ltr .me-sm-14{margin-right:56px!important}.v-application--is-rtl .me-sm-14{margin-left:56px!important}.v-application--is-ltr .me-sm-15{margin-right:60px!important}.v-application--is-rtl .me-sm-15{margin-left:60px!important}.v-application--is-ltr .me-sm-16{margin-right:64px!important}.v-application--is-rtl .me-sm-16{margin-left:64px!important}.v-application--is-ltr .me-sm-auto{margin-right:auto!important}.v-application--is-rtl .me-sm-auto{margin-left:auto!important}.v-application .ma-sm-n1{margin:-4px!important}.v-application .ma-sm-n2{margin:-8px!important}.v-application .ma-sm-n3{margin:-12px!important}.v-application .ma-sm-n4{margin:-16px!important}.v-application .ma-sm-n5{margin:-20px!important}.v-application .ma-sm-n6{margin:-24px!important}.v-application .ma-sm-n7{margin:-28px!important}.v-application .ma-sm-n8{margin:-32px!important}.v-application .ma-sm-n9{margin:-36px!important}.v-application .ma-sm-n10{margin:-40px!important}.v-application .ma-sm-n11{margin:-44px!important}.v-application .ma-sm-n12{margin:-48px!important}.v-application .ma-sm-n13{margin:-52px!important}.v-application .ma-sm-n14{margin:-56px!important}.v-application .ma-sm-n15{margin:-60px!important}.v-application .ma-sm-n16{margin:-64px!important}.v-application .mx-sm-n1{margin-right:-4px!important;margin-left:-4px!important}.v-application .mx-sm-n2{margin-right:-8px!important;margin-left:-8px!important}.v-application .mx-sm-n3{margin-right:-12px!important;margin-left:-12px!important}.v-application .mx-sm-n4{margin-right:-16px!important;margin-left:-16px!important}.v-application .mx-sm-n5{margin-right:-20px!important;margin-left:-20px!important}.v-application .mx-sm-n6{margin-right:-24px!important;margin-left:-24px!important}.v-application .mx-sm-n7{margin-right:-28px!important;margin-left:-28px!important}.v-application .mx-sm-n8{margin-right:-32px!important;margin-left:-32px!important}.v-application .mx-sm-n9{margin-right:-36px!important;margin-left:-36px!important}.v-application .mx-sm-n10{margin-right:-40px!important;margin-left:-40px!important}.v-application .mx-sm-n11{margin-right:-44px!important;margin-left:-44px!important}.v-application .mx-sm-n12{margin-right:-48px!important;margin-left:-48px!important}.v-application .mx-sm-n13{margin-right:-52px!important;margin-left:-52px!important}.v-application .mx-sm-n14{margin-right:-56px!important;margin-left:-56px!important}.v-application .mx-sm-n15{margin-right:-60px!important;margin-left:-60px!important}.v-application .mx-sm-n16{margin-right:-64px!important;margin-left:-64px!important}.v-application .my-sm-n1{margin-top:-4px!important;margin-bottom:-4px!important}.v-application .my-sm-n2{margin-top:-8px!important;margin-bottom:-8px!important}.v-application .my-sm-n3{margin-top:-12px!important;margin-bottom:-12px!important}.v-application .my-sm-n4{margin-top:-16px!important;margin-bottom:-16px!important}.v-application .my-sm-n5{margin-top:-20px!important;margin-bottom:-20px!important}.v-application .my-sm-n6{margin-top:-24px!important;margin-bottom:-24px!important}.v-application .my-sm-n7{margin-top:-28px!important;margin-bottom:-28px!important}.v-application .my-sm-n8{margin-top:-32px!important;margin-bottom:-32px!important}.v-application .my-sm-n9{margin-top:-36px!important;margin-bottom:-36px!important}.v-application .my-sm-n10{margin-top:-40px!important;margin-bottom:-40px!important}.v-application .my-sm-n11{margin-top:-44px!important;margin-bottom:-44px!important}.v-application .my-sm-n12{margin-top:-48px!important;margin-bottom:-48px!important}.v-application .my-sm-n13{margin-top:-52px!important;margin-bottom:-52px!important}.v-application .my-sm-n14{margin-top:-56px!important;margin-bottom:-56px!important}.v-application .my-sm-n15{margin-top:-60px!important;margin-bottom:-60px!important}.v-application .my-sm-n16{margin-top:-64px!important;margin-bottom:-64px!important}.v-application .mt-sm-n1{margin-top:-4px!important}.v-application .mt-sm-n2{margin-top:-8px!important}.v-application .mt-sm-n3{margin-top:-12px!important}.v-application .mt-sm-n4{margin-top:-16px!important}.v-application .mt-sm-n5{margin-top:-20px!important}.v-application .mt-sm-n6{margin-top:-24px!important}.v-application .mt-sm-n7{margin-top:-28px!important}.v-application .mt-sm-n8{margin-top:-32px!important}.v-application .mt-sm-n9{margin-top:-36px!important}.v-application .mt-sm-n10{margin-top:-40px!important}.v-application .mt-sm-n11{margin-top:-44px!important}.v-application .mt-sm-n12{margin-top:-48px!important}.v-application .mt-sm-n13{margin-top:-52px!important}.v-application .mt-sm-n14{margin-top:-56px!important}.v-application .mt-sm-n15{margin-top:-60px!important}.v-application .mt-sm-n16{margin-top:-64px!important}.v-application .mr-sm-n1{margin-right:-4px!important}.v-application .mr-sm-n2{margin-right:-8px!important}.v-application .mr-sm-n3{margin-right:-12px!important}.v-application .mr-sm-n4{margin-right:-16px!important}.v-application .mr-sm-n5{margin-right:-20px!important}.v-application .mr-sm-n6{margin-right:-24px!important}.v-application .mr-sm-n7{margin-right:-28px!important}.v-application .mr-sm-n8{margin-right:-32px!important}.v-application .mr-sm-n9{margin-right:-36px!important}.v-application .mr-sm-n10{margin-right:-40px!important}.v-application .mr-sm-n11{margin-right:-44px!important}.v-application .mr-sm-n12{margin-right:-48px!important}.v-application .mr-sm-n13{margin-right:-52px!important}.v-application .mr-sm-n14{margin-right:-56px!important}.v-application .mr-sm-n15{margin-right:-60px!important}.v-application .mr-sm-n16{margin-right:-64px!important}.v-application .mb-sm-n1{margin-bottom:-4px!important}.v-application .mb-sm-n2{margin-bottom:-8px!important}.v-application .mb-sm-n3{margin-bottom:-12px!important}.v-application .mb-sm-n4{margin-bottom:-16px!important}.v-application .mb-sm-n5{margin-bottom:-20px!important}.v-application .mb-sm-n6{margin-bottom:-24px!important}.v-application .mb-sm-n7{margin-bottom:-28px!important}.v-application .mb-sm-n8{margin-bottom:-32px!important}.v-application .mb-sm-n9{margin-bottom:-36px!important}.v-application .mb-sm-n10{margin-bottom:-40px!important}.v-application .mb-sm-n11{margin-bottom:-44px!important}.v-application .mb-sm-n12{margin-bottom:-48px!important}.v-application .mb-sm-n13{margin-bottom:-52px!important}.v-application .mb-sm-n14{margin-bottom:-56px!important}.v-application .mb-sm-n15{margin-bottom:-60px!important}.v-application .mb-sm-n16{margin-bottom:-64px!important}.v-application .ml-sm-n1{margin-left:-4px!important}.v-application .ml-sm-n2{margin-left:-8px!important}.v-application .ml-sm-n3{margin-left:-12px!important}.v-application .ml-sm-n4{margin-left:-16px!important}.v-application .ml-sm-n5{margin-left:-20px!important}.v-application .ml-sm-n6{margin-left:-24px!important}.v-application .ml-sm-n7{margin-left:-28px!important}.v-application .ml-sm-n8{margin-left:-32px!important}.v-application .ml-sm-n9{margin-left:-36px!important}.v-application .ml-sm-n10{margin-left:-40px!important}.v-application .ml-sm-n11{margin-left:-44px!important}.v-application .ml-sm-n12{margin-left:-48px!important}.v-application .ml-sm-n13{margin-left:-52px!important}.v-application .ml-sm-n14{margin-left:-56px!important}.v-application .ml-sm-n15{margin-left:-60px!important}.v-application .ml-sm-n16{margin-left:-64px!important}.v-application--is-ltr .ms-sm-n1{margin-left:-4px!important}.v-application--is-rtl .ms-sm-n1{margin-right:-4px!important}.v-application--is-ltr .ms-sm-n2{margin-left:-8px!important}.v-application--is-rtl .ms-sm-n2{margin-right:-8px!important}.v-application--is-ltr .ms-sm-n3{margin-left:-12px!important}.v-application--is-rtl .ms-sm-n3{margin-right:-12px!important}.v-application--is-ltr .ms-sm-n4{margin-left:-16px!important}.v-application--is-rtl .ms-sm-n4{margin-right:-16px!important}.v-application--is-ltr .ms-sm-n5{margin-left:-20px!important}.v-application--is-rtl .ms-sm-n5{margin-right:-20px!important}.v-application--is-ltr .ms-sm-n6{margin-left:-24px!important}.v-application--is-rtl .ms-sm-n6{margin-right:-24px!important}.v-application--is-ltr .ms-sm-n7{margin-left:-28px!important}.v-application--is-rtl .ms-sm-n7{margin-right:-28px!important}.v-application--is-ltr .ms-sm-n8{margin-left:-32px!important}.v-application--is-rtl .ms-sm-n8{margin-right:-32px!important}.v-application--is-ltr .ms-sm-n9{margin-left:-36px!important}.v-application--is-rtl .ms-sm-n9{margin-right:-36px!important}.v-application--is-ltr .ms-sm-n10{margin-left:-40px!important}.v-application--is-rtl .ms-sm-n10{margin-right:-40px!important}.v-application--is-ltr .ms-sm-n11{margin-left:-44px!important}.v-application--is-rtl .ms-sm-n11{margin-right:-44px!important}.v-application--is-ltr .ms-sm-n12{margin-left:-48px!important}.v-application--is-rtl .ms-sm-n12{margin-right:-48px!important}.v-application--is-ltr .ms-sm-n13{margin-left:-52px!important}.v-application--is-rtl .ms-sm-n13{margin-right:-52px!important}.v-application--is-ltr .ms-sm-n14{margin-left:-56px!important}.v-application--is-rtl .ms-sm-n14{margin-right:-56px!important}.v-application--is-ltr .ms-sm-n15{margin-left:-60px!important}.v-application--is-rtl .ms-sm-n15{margin-right:-60px!important}.v-application--is-ltr .ms-sm-n16{margin-left:-64px!important}.v-application--is-rtl .ms-sm-n16{margin-right:-64px!important}.v-application--is-ltr .me-sm-n1{margin-right:-4px!important}.v-application--is-rtl .me-sm-n1{margin-left:-4px!important}.v-application--is-ltr .me-sm-n2{margin-right:-8px!important}.v-application--is-rtl .me-sm-n2{margin-left:-8px!important}.v-application--is-ltr .me-sm-n3{margin-right:-12px!important}.v-application--is-rtl .me-sm-n3{margin-left:-12px!important}.v-application--is-ltr .me-sm-n4{margin-right:-16px!important}.v-application--is-rtl .me-sm-n4{margin-left:-16px!important}.v-application--is-ltr .me-sm-n5{margin-right:-20px!important}.v-application--is-rtl .me-sm-n5{margin-left:-20px!important}.v-application--is-ltr .me-sm-n6{margin-right:-24px!important}.v-application--is-rtl .me-sm-n6{margin-left:-24px!important}.v-application--is-ltr .me-sm-n7{margin-right:-28px!important}.v-application--is-rtl .me-sm-n7{margin-left:-28px!important}.v-application--is-ltr .me-sm-n8{margin-right:-32px!important}.v-application--is-rtl .me-sm-n8{margin-left:-32px!important}.v-application--is-ltr .me-sm-n9{margin-right:-36px!important}.v-application--is-rtl .me-sm-n9{margin-left:-36px!important}.v-application--is-ltr .me-sm-n10{margin-right:-40px!important}.v-application--is-rtl .me-sm-n10{margin-left:-40px!important}.v-application--is-ltr .me-sm-n11{margin-right:-44px!important}.v-application--is-rtl .me-sm-n11{margin-left:-44px!important}.v-application--is-ltr .me-sm-n12{margin-right:-48px!important}.v-application--is-rtl .me-sm-n12{margin-left:-48px!important}.v-application--is-ltr .me-sm-n13{margin-right:-52px!important}.v-application--is-rtl .me-sm-n13{margin-left:-52px!important}.v-application--is-ltr .me-sm-n14{margin-right:-56px!important}.v-application--is-rtl .me-sm-n14{margin-left:-56px!important}.v-application--is-ltr .me-sm-n15{margin-right:-60px!important}.v-application--is-rtl .me-sm-n15{margin-left:-60px!important}.v-application--is-ltr .me-sm-n16{margin-right:-64px!important}.v-application--is-rtl .me-sm-n16{margin-left:-64px!important}.v-application .pa-sm-0{padding:0!important}.v-application .pa-sm-1{padding:4px!important}.v-application .pa-sm-2{padding:8px!important}.v-application .pa-sm-3{padding:12px!important}.v-application .pa-sm-4{padding:16px!important}.v-application .pa-sm-5{padding:20px!important}.v-application .pa-sm-6{padding:24px!important}.v-application .pa-sm-7{padding:28px!important}.v-application .pa-sm-8{padding:32px!important}.v-application .pa-sm-9{padding:36px!important}.v-application .pa-sm-10{padding:40px!important}.v-application .pa-sm-11{padding:44px!important}.v-application .pa-sm-12{padding:48px!important}.v-application .pa-sm-13{padding:52px!important}.v-application .pa-sm-14{padding:56px!important}.v-application .pa-sm-15{padding:60px!important}.v-application .pa-sm-16{padding:64px!important}.v-application .px-sm-0{padding-right:0!important;padding-left:0!important}.v-application .px-sm-1{padding-right:4px!important;padding-left:4px!important}.v-application .px-sm-2{padding-right:8px!important;padding-left:8px!important}.v-application .px-sm-3{padding-right:12px!important;padding-left:12px!important}.v-application .px-sm-4{padding-right:16px!important;padding-left:16px!important}.v-application .px-sm-5{padding-right:20px!important;padding-left:20px!important}.v-application .px-sm-6{padding-right:24px!important;padding-left:24px!important}.v-application .px-sm-7{padding-right:28px!important;padding-left:28px!important}.v-application .px-sm-8{padding-right:32px!important;padding-left:32px!important}.v-application .px-sm-9{padding-right:36px!important;padding-left:36px!important}.v-application .px-sm-10{padding-right:40px!important;padding-left:40px!important}.v-application .px-sm-11{padding-right:44px!important;padding-left:44px!important}.v-application .px-sm-12{padding-right:48px!important;padding-left:48px!important}.v-application .px-sm-13{padding-right:52px!important;padding-left:52px!important}.v-application .px-sm-14{padding-right:56px!important;padding-left:56px!important}.v-application .px-sm-15{padding-right:60px!important;padding-left:60px!important}.v-application .px-sm-16{padding-right:64px!important;padding-left:64px!important}.v-application .py-sm-0{padding-top:0!important;padding-bottom:0!important}.v-application .py-sm-1{padding-top:4px!important;padding-bottom:4px!important}.v-application .py-sm-2{padding-top:8px!important;padding-bottom:8px!important}.v-application .py-sm-3{padding-top:12px!important;padding-bottom:12px!important}.v-application .py-sm-4{padding-top:16px!important;padding-bottom:16px!important}.v-application .py-sm-5{padding-top:20px!important;padding-bottom:20px!important}.v-application .py-sm-6{padding-top:24px!important;padding-bottom:24px!important}.v-application .py-sm-7{padding-top:28px!important;padding-bottom:28px!important}.v-application .py-sm-8{padding-top:32px!important;padding-bottom:32px!important}.v-application .py-sm-9{padding-top:36px!important;padding-bottom:36px!important}.v-application .py-sm-10{padding-top:40px!important;padding-bottom:40px!important}.v-application .py-sm-11{padding-top:44px!important;padding-bottom:44px!important}.v-application .py-sm-12{padding-top:48px!important;padding-bottom:48px!important}.v-application .py-sm-13{padding-top:52px!important;padding-bottom:52px!important}.v-application .py-sm-14{padding-top:56px!important;padding-bottom:56px!important}.v-application .py-sm-15{padding-top:60px!important;padding-bottom:60px!important}.v-application .py-sm-16{padding-top:64px!important;padding-bottom:64px!important}.v-application .pt-sm-0{padding-top:0!important}.v-application .pt-sm-1{padding-top:4px!important}.v-application .pt-sm-2{padding-top:8px!important}.v-application .pt-sm-3{padding-top:12px!important}.v-application .pt-sm-4{padding-top:16px!important}.v-application .pt-sm-5{padding-top:20px!important}.v-application .pt-sm-6{padding-top:24px!important}.v-application .pt-sm-7{padding-top:28px!important}.v-application .pt-sm-8{padding-top:32px!important}.v-application .pt-sm-9{padding-top:36px!important}.v-application .pt-sm-10{padding-top:40px!important}.v-application .pt-sm-11{padding-top:44px!important}.v-application .pt-sm-12{padding-top:48px!important}.v-application .pt-sm-13{padding-top:52px!important}.v-application .pt-sm-14{padding-top:56px!important}.v-application .pt-sm-15{padding-top:60px!important}.v-application .pt-sm-16{padding-top:64px!important}.v-application .pr-sm-0{padding-right:0!important}.v-application .pr-sm-1{padding-right:4px!important}.v-application .pr-sm-2{padding-right:8px!important}.v-application .pr-sm-3{padding-right:12px!important}.v-application .pr-sm-4{padding-right:16px!important}.v-application .pr-sm-5{padding-right:20px!important}.v-application .pr-sm-6{padding-right:24px!important}.v-application .pr-sm-7{padding-right:28px!important}.v-application .pr-sm-8{padding-right:32px!important}.v-application .pr-sm-9{padding-right:36px!important}.v-application .pr-sm-10{padding-right:40px!important}.v-application .pr-sm-11{padding-right:44px!important}.v-application .pr-sm-12{padding-right:48px!important}.v-application .pr-sm-13{padding-right:52px!important}.v-application .pr-sm-14{padding-right:56px!important}.v-application .pr-sm-15{padding-right:60px!important}.v-application .pr-sm-16{padding-right:64px!important}.v-application .pb-sm-0{padding-bottom:0!important}.v-application .pb-sm-1{padding-bottom:4px!important}.v-application .pb-sm-2{padding-bottom:8px!important}.v-application .pb-sm-3{padding-bottom:12px!important}.v-application .pb-sm-4{padding-bottom:16px!important}.v-application .pb-sm-5{padding-bottom:20px!important}.v-application .pb-sm-6{padding-bottom:24px!important}.v-application .pb-sm-7{padding-bottom:28px!important}.v-application .pb-sm-8{padding-bottom:32px!important}.v-application .pb-sm-9{padding-bottom:36px!important}.v-application .pb-sm-10{padding-bottom:40px!important}.v-application .pb-sm-11{padding-bottom:44px!important}.v-application .pb-sm-12{padding-bottom:48px!important}.v-application .pb-sm-13{padding-bottom:52px!important}.v-application .pb-sm-14{padding-bottom:56px!important}.v-application .pb-sm-15{padding-bottom:60px!important}.v-application .pb-sm-16{padding-bottom:64px!important}.v-application .pl-sm-0{padding-left:0!important}.v-application .pl-sm-1{padding-left:4px!important}.v-application .pl-sm-2{padding-left:8px!important}.v-application .pl-sm-3{padding-left:12px!important}.v-application .pl-sm-4{padding-left:16px!important}.v-application .pl-sm-5{padding-left:20px!important}.v-application .pl-sm-6{padding-left:24px!important}.v-application .pl-sm-7{padding-left:28px!important}.v-application .pl-sm-8{padding-left:32px!important}.v-application .pl-sm-9{padding-left:36px!important}.v-application .pl-sm-10{padding-left:40px!important}.v-application .pl-sm-11{padding-left:44px!important}.v-application .pl-sm-12{padding-left:48px!important}.v-application .pl-sm-13{padding-left:52px!important}.v-application .pl-sm-14{padding-left:56px!important}.v-application .pl-sm-15{padding-left:60px!important}.v-application .pl-sm-16{padding-left:64px!important}.v-application--is-ltr .ps-sm-0{padding-left:0!important}.v-application--is-rtl .ps-sm-0{padding-right:0!important}.v-application--is-ltr .ps-sm-1{padding-left:4px!important}.v-application--is-rtl .ps-sm-1{padding-right:4px!important}.v-application--is-ltr .ps-sm-2{padding-left:8px!important}.v-application--is-rtl .ps-sm-2{padding-right:8px!important}.v-application--is-ltr .ps-sm-3{padding-left:12px!important}.v-application--is-rtl .ps-sm-3{padding-right:12px!important}.v-application--is-ltr .ps-sm-4{padding-left:16px!important}.v-application--is-rtl .ps-sm-4{padding-right:16px!important}.v-application--is-ltr .ps-sm-5{padding-left:20px!important}.v-application--is-rtl .ps-sm-5{padding-right:20px!important}.v-application--is-ltr .ps-sm-6{padding-left:24px!important}.v-application--is-rtl .ps-sm-6{padding-right:24px!important}.v-application--is-ltr .ps-sm-7{padding-left:28px!important}.v-application--is-rtl .ps-sm-7{padding-right:28px!important}.v-application--is-ltr .ps-sm-8{padding-left:32px!important}.v-application--is-rtl .ps-sm-8{padding-right:32px!important}.v-application--is-ltr .ps-sm-9{padding-left:36px!important}.v-application--is-rtl .ps-sm-9{padding-right:36px!important}.v-application--is-ltr .ps-sm-10{padding-left:40px!important}.v-application--is-rtl .ps-sm-10{padding-right:40px!important}.v-application--is-ltr .ps-sm-11{padding-left:44px!important}.v-application--is-rtl .ps-sm-11{padding-right:44px!important}.v-application--is-ltr .ps-sm-12{padding-left:48px!important}.v-application--is-rtl .ps-sm-12{padding-right:48px!important}.v-application--is-ltr .ps-sm-13{padding-left:52px!important}.v-application--is-rtl .ps-sm-13{padding-right:52px!important}.v-application--is-ltr .ps-sm-14{padding-left:56px!important}.v-application--is-rtl .ps-sm-14{padding-right:56px!important}.v-application--is-ltr .ps-sm-15{padding-left:60px!important}.v-application--is-rtl .ps-sm-15{padding-right:60px!important}.v-application--is-ltr .ps-sm-16{padding-left:64px!important}.v-application--is-rtl .ps-sm-16{padding-right:64px!important}.v-application--is-ltr .pe-sm-0{padding-right:0!important}.v-application--is-rtl .pe-sm-0{padding-left:0!important}.v-application--is-ltr .pe-sm-1{padding-right:4px!important}.v-application--is-rtl .pe-sm-1{padding-left:4px!important}.v-application--is-ltr .pe-sm-2{padding-right:8px!important}.v-application--is-rtl .pe-sm-2{padding-left:8px!important}.v-application--is-ltr .pe-sm-3{padding-right:12px!important}.v-application--is-rtl .pe-sm-3{padding-left:12px!important}.v-application--is-ltr .pe-sm-4{padding-right:16px!important}.v-application--is-rtl .pe-sm-4{padding-left:16px!important}.v-application--is-ltr .pe-sm-5{padding-right:20px!important}.v-application--is-rtl .pe-sm-5{padding-left:20px!important}.v-application--is-ltr .pe-sm-6{padding-right:24px!important}.v-application--is-rtl .pe-sm-6{padding-left:24px!important}.v-application--is-ltr .pe-sm-7{padding-right:28px!important}.v-application--is-rtl .pe-sm-7{padding-left:28px!important}.v-application--is-ltr .pe-sm-8{padding-right:32px!important}.v-application--is-rtl .pe-sm-8{padding-left:32px!important}.v-application--is-ltr .pe-sm-9{padding-right:36px!important}.v-application--is-rtl .pe-sm-9{padding-left:36px!important}.v-application--is-ltr .pe-sm-10{padding-right:40px!important}.v-application--is-rtl .pe-sm-10{padding-left:40px!important}.v-application--is-ltr .pe-sm-11{padding-right:44px!important}.v-application--is-rtl .pe-sm-11{padding-left:44px!important}.v-application--is-ltr .pe-sm-12{padding-right:48px!important}.v-application--is-rtl .pe-sm-12{padding-left:48px!important}.v-application--is-ltr .pe-sm-13{padding-right:52px!important}.v-application--is-rtl .pe-sm-13{padding-left:52px!important}.v-application--is-ltr .pe-sm-14{padding-right:56px!important}.v-application--is-rtl .pe-sm-14{padding-left:56px!important}.v-application--is-ltr .pe-sm-15{padding-right:60px!important}.v-application--is-rtl .pe-sm-15{padding-left:60px!important}.v-application--is-ltr .pe-sm-16{padding-right:64px!important}.v-application--is-rtl .pe-sm-16{padding-left:64px!important}.v-application .text-sm-left{text-align:left!important}.v-application .text-sm-right{text-align:right!important}.v-application .text-sm-center{text-align:center!important}.v-application .text-sm-justify{text-align:justify!important}.v-application .text-sm-start{text-align:start!important}.v-application .text-sm-end{text-align:end!important}.v-application .text-sm-h1{font-size:6rem!important;line-height:6rem;letter-spacing:-.015625em!important}.v-application .text-sm-h1,.v-application .text-sm-h2{font-weight:300;font-family:Roboto,sans-serif!important}.v-application .text-sm-h2{font-size:3.75rem!important;line-height:3.75rem;letter-spacing:-.0083333333em!important}.v-application .text-sm-h3{font-size:3rem!important;line-height:3.125rem;letter-spacing:normal!important}.v-application .text-sm-h3,.v-application .text-sm-h4{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .text-sm-h4{font-size:2.125rem!important;line-height:2.5rem;letter-spacing:.0073529412em!important}.v-application .text-sm-h5{font-size:1.5rem!important;font-weight:400;letter-spacing:normal!important}.v-application .text-sm-h5,.v-application .text-sm-h6{line-height:2rem;font-family:Roboto,sans-serif!important}.v-application .text-sm-h6{font-size:1.25rem!important;font-weight:500;letter-spacing:.0125em!important}.v-application .text-sm-subtitle-1{font-size:1rem!important;font-weight:400;line-height:1.75rem;letter-spacing:.009375em!important;font-family:Roboto,sans-serif!important}.v-application .text-sm-subtitle-2{font-size:.875rem!important;font-weight:500;line-height:1.375rem;letter-spacing:.0071428571em!important;font-family:Roboto,sans-serif!important}.v-application .text-sm-body-1{font-size:1rem!important;font-weight:400;line-height:1.5rem;letter-spacing:.03125em!important;font-family:Roboto,sans-serif!important}.v-application .text-sm-body-2{font-weight:400;line-height:1.25rem;letter-spacing:.0178571429em!important}.v-application .text-sm-body-2,.v-application .text-sm-button{font-size:.875rem!important;font-family:Roboto,sans-serif!important}.v-application .text-sm-button{font-weight:500;line-height:2.25rem;letter-spacing:.0892857143em!important;text-transform:uppercase!important}.v-application .text-sm-caption{font-weight:400;line-height:1.25rem;letter-spacing:.0333333333em!important}.v-application .text-sm-caption,.v-application .text-sm-overline{font-size:.75rem!important;font-family:Roboto,sans-serif!important}.v-application .text-sm-overline{font-weight:500;line-height:2rem;letter-spacing:.1666666667em!important;text-transform:uppercase!important}}@media(min-width:960px){.v-application .d-md-none{display:none!important}.v-application .d-md-inline{display:inline!important}.v-application .d-md-inline-block{display:inline-block!important}.v-application .d-md-block{display:block!important}.v-application .d-md-table{display:table!important}.v-application .d-md-table-row{display:table-row!important}.v-application .d-md-table-cell{display:table-cell!important}.v-application .d-md-flex{display:flex!important}.v-application .d-md-inline-flex{display:inline-flex!important}.v-application .float-md-none{float:none!important}.v-application .float-md-left{float:left!important}.v-application .float-md-right{float:right!important}.v-application .flex-md-fill{flex:1 1 auto!important}.v-application .flex-md-row{flex-direction:row!important}.v-application .flex-md-column{flex-direction:column!important}.v-application .flex-md-row-reverse{flex-direction:row-reverse!important}.v-application .flex-md-column-reverse{flex-direction:column-reverse!important}.v-application .flex-md-grow-0{flex-grow:0!important}.v-application .flex-md-grow-1{flex-grow:1!important}.v-application .flex-md-shrink-0{flex-shrink:0!important}.v-application .flex-md-shrink-1{flex-shrink:1!important}.v-application .flex-md-wrap{flex-wrap:wrap!important}.v-application .flex-md-nowrap{flex-wrap:nowrap!important}.v-application .flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.v-application .justify-md-start{justify-content:flex-start!important}.v-application .justify-md-end{justify-content:flex-end!important}.v-application .justify-md-center{justify-content:center!important}.v-application .justify-md-space-between{justify-content:space-between!important}.v-application .justify-md-space-around{justify-content:space-around!important}.v-application .align-md-start{align-items:flex-start!important}.v-application .align-md-end{align-items:flex-end!important}.v-application .align-md-center{align-items:center!important}.v-application .align-md-baseline{align-items:baseline!important}.v-application .align-md-stretch{align-items:stretch!important}.v-application .align-content-md-start{align-content:flex-start!important}.v-application .align-content-md-end{align-content:flex-end!important}.v-application .align-content-md-center{align-content:center!important}.v-application .align-content-md-space-between{align-content:space-between!important}.v-application .align-content-md-space-around{align-content:space-around!important}.v-application .align-content-md-stretch{align-content:stretch!important}.v-application .align-self-md-auto{align-self:auto!important}.v-application .align-self-md-start{align-self:flex-start!important}.v-application .align-self-md-end{align-self:flex-end!important}.v-application .align-self-md-center{align-self:center!important}.v-application .align-self-md-baseline{align-self:baseline!important}.v-application .align-self-md-stretch{align-self:stretch!important}.v-application .order-md-first{order:-1!important}.v-application .order-md-0{order:0!important}.v-application .order-md-1{order:1!important}.v-application .order-md-2{order:2!important}.v-application .order-md-3{order:3!important}.v-application .order-md-4{order:4!important}.v-application .order-md-5{order:5!important}.v-application .order-md-6{order:6!important}.v-application .order-md-7{order:7!important}.v-application .order-md-8{order:8!important}.v-application .order-md-9{order:9!important}.v-application .order-md-10{order:10!important}.v-application .order-md-11{order:11!important}.v-application .order-md-12{order:12!important}.v-application .order-md-last{order:13!important}.v-application .ma-md-0{margin:0!important}.v-application .ma-md-1{margin:4px!important}.v-application .ma-md-2{margin:8px!important}.v-application .ma-md-3{margin:12px!important}.v-application .ma-md-4{margin:16px!important}.v-application .ma-md-5{margin:20px!important}.v-application .ma-md-6{margin:24px!important}.v-application .ma-md-7{margin:28px!important}.v-application .ma-md-8{margin:32px!important}.v-application .ma-md-9{margin:36px!important}.v-application .ma-md-10{margin:40px!important}.v-application .ma-md-11{margin:44px!important}.v-application .ma-md-12{margin:48px!important}.v-application .ma-md-13{margin:52px!important}.v-application .ma-md-14{margin:56px!important}.v-application .ma-md-15{margin:60px!important}.v-application .ma-md-16{margin:64px!important}.v-application .ma-md-auto{margin:auto!important}.v-application .mx-md-0{margin-right:0!important;margin-left:0!important}.v-application .mx-md-1{margin-right:4px!important;margin-left:4px!important}.v-application .mx-md-2{margin-right:8px!important;margin-left:8px!important}.v-application .mx-md-3{margin-right:12px!important;margin-left:12px!important}.v-application .mx-md-4{margin-right:16px!important;margin-left:16px!important}.v-application .mx-md-5{margin-right:20px!important;margin-left:20px!important}.v-application .mx-md-6{margin-right:24px!important;margin-left:24px!important}.v-application .mx-md-7{margin-right:28px!important;margin-left:28px!important}.v-application .mx-md-8{margin-right:32px!important;margin-left:32px!important}.v-application .mx-md-9{margin-right:36px!important;margin-left:36px!important}.v-application .mx-md-10{margin-right:40px!important;margin-left:40px!important}.v-application .mx-md-11{margin-right:44px!important;margin-left:44px!important}.v-application .mx-md-12{margin-right:48px!important;margin-left:48px!important}.v-application .mx-md-13{margin-right:52px!important;margin-left:52px!important}.v-application .mx-md-14{margin-right:56px!important;margin-left:56px!important}.v-application .mx-md-15{margin-right:60px!important;margin-left:60px!important}.v-application .mx-md-16{margin-right:64px!important;margin-left:64px!important}.v-application .mx-md-auto{margin-right:auto!important;margin-left:auto!important}.v-application .my-md-0{margin-top:0!important;margin-bottom:0!important}.v-application .my-md-1{margin-top:4px!important;margin-bottom:4px!important}.v-application .my-md-2{margin-top:8px!important;margin-bottom:8px!important}.v-application .my-md-3{margin-top:12px!important;margin-bottom:12px!important}.v-application .my-md-4{margin-top:16px!important;margin-bottom:16px!important}.v-application .my-md-5{margin-top:20px!important;margin-bottom:20px!important}.v-application .my-md-6{margin-top:24px!important;margin-bottom:24px!important}.v-application .my-md-7{margin-top:28px!important;margin-bottom:28px!important}.v-application .my-md-8{margin-top:32px!important;margin-bottom:32px!important}.v-application .my-md-9{margin-top:36px!important;margin-bottom:36px!important}.v-application .my-md-10{margin-top:40px!important;margin-bottom:40px!important}.v-application .my-md-11{margin-top:44px!important;margin-bottom:44px!important}.v-application .my-md-12{margin-top:48px!important;margin-bottom:48px!important}.v-application .my-md-13{margin-top:52px!important;margin-bottom:52px!important}.v-application .my-md-14{margin-top:56px!important;margin-bottom:56px!important}.v-application .my-md-15{margin-top:60px!important;margin-bottom:60px!important}.v-application .my-md-16{margin-top:64px!important;margin-bottom:64px!important}.v-application .my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.v-application .mt-md-0{margin-top:0!important}.v-application .mt-md-1{margin-top:4px!important}.v-application .mt-md-2{margin-top:8px!important}.v-application .mt-md-3{margin-top:12px!important}.v-application .mt-md-4{margin-top:16px!important}.v-application .mt-md-5{margin-top:20px!important}.v-application .mt-md-6{margin-top:24px!important}.v-application .mt-md-7{margin-top:28px!important}.v-application .mt-md-8{margin-top:32px!important}.v-application .mt-md-9{margin-top:36px!important}.v-application .mt-md-10{margin-top:40px!important}.v-application .mt-md-11{margin-top:44px!important}.v-application .mt-md-12{margin-top:48px!important}.v-application .mt-md-13{margin-top:52px!important}.v-application .mt-md-14{margin-top:56px!important}.v-application .mt-md-15{margin-top:60px!important}.v-application .mt-md-16{margin-top:64px!important}.v-application .mt-md-auto{margin-top:auto!important}.v-application .mr-md-0{margin-right:0!important}.v-application .mr-md-1{margin-right:4px!important}.v-application .mr-md-2{margin-right:8px!important}.v-application .mr-md-3{margin-right:12px!important}.v-application .mr-md-4{margin-right:16px!important}.v-application .mr-md-5{margin-right:20px!important}.v-application .mr-md-6{margin-right:24px!important}.v-application .mr-md-7{margin-right:28px!important}.v-application .mr-md-8{margin-right:32px!important}.v-application .mr-md-9{margin-right:36px!important}.v-application .mr-md-10{margin-right:40px!important}.v-application .mr-md-11{margin-right:44px!important}.v-application .mr-md-12{margin-right:48px!important}.v-application .mr-md-13{margin-right:52px!important}.v-application .mr-md-14{margin-right:56px!important}.v-application .mr-md-15{margin-right:60px!important}.v-application .mr-md-16{margin-right:64px!important}.v-application .mr-md-auto{margin-right:auto!important}.v-application .mb-md-0{margin-bottom:0!important}.v-application .mb-md-1{margin-bottom:4px!important}.v-application .mb-md-2{margin-bottom:8px!important}.v-application .mb-md-3{margin-bottom:12px!important}.v-application .mb-md-4{margin-bottom:16px!important}.v-application .mb-md-5{margin-bottom:20px!important}.v-application .mb-md-6{margin-bottom:24px!important}.v-application .mb-md-7{margin-bottom:28px!important}.v-application .mb-md-8{margin-bottom:32px!important}.v-application .mb-md-9{margin-bottom:36px!important}.v-application .mb-md-10{margin-bottom:40px!important}.v-application .mb-md-11{margin-bottom:44px!important}.v-application .mb-md-12{margin-bottom:48px!important}.v-application .mb-md-13{margin-bottom:52px!important}.v-application .mb-md-14{margin-bottom:56px!important}.v-application .mb-md-15{margin-bottom:60px!important}.v-application .mb-md-16{margin-bottom:64px!important}.v-application .mb-md-auto{margin-bottom:auto!important}.v-application .ml-md-0{margin-left:0!important}.v-application .ml-md-1{margin-left:4px!important}.v-application .ml-md-2{margin-left:8px!important}.v-application .ml-md-3{margin-left:12px!important}.v-application .ml-md-4{margin-left:16px!important}.v-application .ml-md-5{margin-left:20px!important}.v-application .ml-md-6{margin-left:24px!important}.v-application .ml-md-7{margin-left:28px!important}.v-application .ml-md-8{margin-left:32px!important}.v-application .ml-md-9{margin-left:36px!important}.v-application .ml-md-10{margin-left:40px!important}.v-application .ml-md-11{margin-left:44px!important}.v-application .ml-md-12{margin-left:48px!important}.v-application .ml-md-13{margin-left:52px!important}.v-application .ml-md-14{margin-left:56px!important}.v-application .ml-md-15{margin-left:60px!important}.v-application .ml-md-16{margin-left:64px!important}.v-application .ml-md-auto{margin-left:auto!important}.v-application--is-ltr .ms-md-0{margin-left:0!important}.v-application--is-rtl .ms-md-0{margin-right:0!important}.v-application--is-ltr .ms-md-1{margin-left:4px!important}.v-application--is-rtl .ms-md-1{margin-right:4px!important}.v-application--is-ltr .ms-md-2{margin-left:8px!important}.v-application--is-rtl .ms-md-2{margin-right:8px!important}.v-application--is-ltr .ms-md-3{margin-left:12px!important}.v-application--is-rtl .ms-md-3{margin-right:12px!important}.v-application--is-ltr .ms-md-4{margin-left:16px!important}.v-application--is-rtl .ms-md-4{margin-right:16px!important}.v-application--is-ltr .ms-md-5{margin-left:20px!important}.v-application--is-rtl .ms-md-5{margin-right:20px!important}.v-application--is-ltr .ms-md-6{margin-left:24px!important}.v-application--is-rtl .ms-md-6{margin-right:24px!important}.v-application--is-ltr .ms-md-7{margin-left:28px!important}.v-application--is-rtl .ms-md-7{margin-right:28px!important}.v-application--is-ltr .ms-md-8{margin-left:32px!important}.v-application--is-rtl .ms-md-8{margin-right:32px!important}.v-application--is-ltr .ms-md-9{margin-left:36px!important}.v-application--is-rtl .ms-md-9{margin-right:36px!important}.v-application--is-ltr .ms-md-10{margin-left:40px!important}.v-application--is-rtl .ms-md-10{margin-right:40px!important}.v-application--is-ltr .ms-md-11{margin-left:44px!important}.v-application--is-rtl .ms-md-11{margin-right:44px!important}.v-application--is-ltr .ms-md-12{margin-left:48px!important}.v-application--is-rtl .ms-md-12{margin-right:48px!important}.v-application--is-ltr .ms-md-13{margin-left:52px!important}.v-application--is-rtl .ms-md-13{margin-right:52px!important}.v-application--is-ltr .ms-md-14{margin-left:56px!important}.v-application--is-rtl .ms-md-14{margin-right:56px!important}.v-application--is-ltr .ms-md-15{margin-left:60px!important}.v-application--is-rtl .ms-md-15{margin-right:60px!important}.v-application--is-ltr .ms-md-16{margin-left:64px!important}.v-application--is-rtl .ms-md-16{margin-right:64px!important}.v-application--is-ltr .ms-md-auto{margin-left:auto!important}.v-application--is-rtl .ms-md-auto{margin-right:auto!important}.v-application--is-ltr .me-md-0{margin-right:0!important}.v-application--is-rtl .me-md-0{margin-left:0!important}.v-application--is-ltr .me-md-1{margin-right:4px!important}.v-application--is-rtl .me-md-1{margin-left:4px!important}.v-application--is-ltr .me-md-2{margin-right:8px!important}.v-application--is-rtl .me-md-2{margin-left:8px!important}.v-application--is-ltr .me-md-3{margin-right:12px!important}.v-application--is-rtl .me-md-3{margin-left:12px!important}.v-application--is-ltr .me-md-4{margin-right:16px!important}.v-application--is-rtl .me-md-4{margin-left:16px!important}.v-application--is-ltr .me-md-5{margin-right:20px!important}.v-application--is-rtl .me-md-5{margin-left:20px!important}.v-application--is-ltr .me-md-6{margin-right:24px!important}.v-application--is-rtl .me-md-6{margin-left:24px!important}.v-application--is-ltr .me-md-7{margin-right:28px!important}.v-application--is-rtl .me-md-7{margin-left:28px!important}.v-application--is-ltr .me-md-8{margin-right:32px!important}.v-application--is-rtl .me-md-8{margin-left:32px!important}.v-application--is-ltr .me-md-9{margin-right:36px!important}.v-application--is-rtl .me-md-9{margin-left:36px!important}.v-application--is-ltr .me-md-10{margin-right:40px!important}.v-application--is-rtl .me-md-10{margin-left:40px!important}.v-application--is-ltr .me-md-11{margin-right:44px!important}.v-application--is-rtl .me-md-11{margin-left:44px!important}.v-application--is-ltr .me-md-12{margin-right:48px!important}.v-application--is-rtl .me-md-12{margin-left:48px!important}.v-application--is-ltr .me-md-13{margin-right:52px!important}.v-application--is-rtl .me-md-13{margin-left:52px!important}.v-application--is-ltr .me-md-14{margin-right:56px!important}.v-application--is-rtl .me-md-14{margin-left:56px!important}.v-application--is-ltr .me-md-15{margin-right:60px!important}.v-application--is-rtl .me-md-15{margin-left:60px!important}.v-application--is-ltr .me-md-16{margin-right:64px!important}.v-application--is-rtl .me-md-16{margin-left:64px!important}.v-application--is-ltr .me-md-auto{margin-right:auto!important}.v-application--is-rtl .me-md-auto{margin-left:auto!important}.v-application .ma-md-n1{margin:-4px!important}.v-application .ma-md-n2{margin:-8px!important}.v-application .ma-md-n3{margin:-12px!important}.v-application .ma-md-n4{margin:-16px!important}.v-application .ma-md-n5{margin:-20px!important}.v-application .ma-md-n6{margin:-24px!important}.v-application .ma-md-n7{margin:-28px!important}.v-application .ma-md-n8{margin:-32px!important}.v-application .ma-md-n9{margin:-36px!important}.v-application .ma-md-n10{margin:-40px!important}.v-application .ma-md-n11{margin:-44px!important}.v-application .ma-md-n12{margin:-48px!important}.v-application .ma-md-n13{margin:-52px!important}.v-application .ma-md-n14{margin:-56px!important}.v-application .ma-md-n15{margin:-60px!important}.v-application .ma-md-n16{margin:-64px!important}.v-application .mx-md-n1{margin-right:-4px!important;margin-left:-4px!important}.v-application .mx-md-n2{margin-right:-8px!important;margin-left:-8px!important}.v-application .mx-md-n3{margin-right:-12px!important;margin-left:-12px!important}.v-application .mx-md-n4{margin-right:-16px!important;margin-left:-16px!important}.v-application .mx-md-n5{margin-right:-20px!important;margin-left:-20px!important}.v-application .mx-md-n6{margin-right:-24px!important;margin-left:-24px!important}.v-application .mx-md-n7{margin-right:-28px!important;margin-left:-28px!important}.v-application .mx-md-n8{margin-right:-32px!important;margin-left:-32px!important}.v-application .mx-md-n9{margin-right:-36px!important;margin-left:-36px!important}.v-application .mx-md-n10{margin-right:-40px!important;margin-left:-40px!important}.v-application .mx-md-n11{margin-right:-44px!important;margin-left:-44px!important}.v-application .mx-md-n12{margin-right:-48px!important;margin-left:-48px!important}.v-application .mx-md-n13{margin-right:-52px!important;margin-left:-52px!important}.v-application .mx-md-n14{margin-right:-56px!important;margin-left:-56px!important}.v-application .mx-md-n15{margin-right:-60px!important;margin-left:-60px!important}.v-application .mx-md-n16{margin-right:-64px!important;margin-left:-64px!important}.v-application .my-md-n1{margin-top:-4px!important;margin-bottom:-4px!important}.v-application .my-md-n2{margin-top:-8px!important;margin-bottom:-8px!important}.v-application .my-md-n3{margin-top:-12px!important;margin-bottom:-12px!important}.v-application .my-md-n4{margin-top:-16px!important;margin-bottom:-16px!important}.v-application .my-md-n5{margin-top:-20px!important;margin-bottom:-20px!important}.v-application .my-md-n6{margin-top:-24px!important;margin-bottom:-24px!important}.v-application .my-md-n7{margin-top:-28px!important;margin-bottom:-28px!important}.v-application .my-md-n8{margin-top:-32px!important;margin-bottom:-32px!important}.v-application .my-md-n9{margin-top:-36px!important;margin-bottom:-36px!important}.v-application .my-md-n10{margin-top:-40px!important;margin-bottom:-40px!important}.v-application .my-md-n11{margin-top:-44px!important;margin-bottom:-44px!important}.v-application .my-md-n12{margin-top:-48px!important;margin-bottom:-48px!important}.v-application .my-md-n13{margin-top:-52px!important;margin-bottom:-52px!important}.v-application .my-md-n14{margin-top:-56px!important;margin-bottom:-56px!important}.v-application .my-md-n15{margin-top:-60px!important;margin-bottom:-60px!important}.v-application .my-md-n16{margin-top:-64px!important;margin-bottom:-64px!important}.v-application .mt-md-n1{margin-top:-4px!important}.v-application .mt-md-n2{margin-top:-8px!important}.v-application .mt-md-n3{margin-top:-12px!important}.v-application .mt-md-n4{margin-top:-16px!important}.v-application .mt-md-n5{margin-top:-20px!important}.v-application .mt-md-n6{margin-top:-24px!important}.v-application .mt-md-n7{margin-top:-28px!important}.v-application .mt-md-n8{margin-top:-32px!important}.v-application .mt-md-n9{margin-top:-36px!important}.v-application .mt-md-n10{margin-top:-40px!important}.v-application .mt-md-n11{margin-top:-44px!important}.v-application .mt-md-n12{margin-top:-48px!important}.v-application .mt-md-n13{margin-top:-52px!important}.v-application .mt-md-n14{margin-top:-56px!important}.v-application .mt-md-n15{margin-top:-60px!important}.v-application .mt-md-n16{margin-top:-64px!important}.v-application .mr-md-n1{margin-right:-4px!important}.v-application .mr-md-n2{margin-right:-8px!important}.v-application .mr-md-n3{margin-right:-12px!important}.v-application .mr-md-n4{margin-right:-16px!important}.v-application .mr-md-n5{margin-right:-20px!important}.v-application .mr-md-n6{margin-right:-24px!important}.v-application .mr-md-n7{margin-right:-28px!important}.v-application .mr-md-n8{margin-right:-32px!important}.v-application .mr-md-n9{margin-right:-36px!important}.v-application .mr-md-n10{margin-right:-40px!important}.v-application .mr-md-n11{margin-right:-44px!important}.v-application .mr-md-n12{margin-right:-48px!important}.v-application .mr-md-n13{margin-right:-52px!important}.v-application .mr-md-n14{margin-right:-56px!important}.v-application .mr-md-n15{margin-right:-60px!important}.v-application .mr-md-n16{margin-right:-64px!important}.v-application .mb-md-n1{margin-bottom:-4px!important}.v-application .mb-md-n2{margin-bottom:-8px!important}.v-application .mb-md-n3{margin-bottom:-12px!important}.v-application .mb-md-n4{margin-bottom:-16px!important}.v-application .mb-md-n5{margin-bottom:-20px!important}.v-application .mb-md-n6{margin-bottom:-24px!important}.v-application .mb-md-n7{margin-bottom:-28px!important}.v-application .mb-md-n8{margin-bottom:-32px!important}.v-application .mb-md-n9{margin-bottom:-36px!important}.v-application .mb-md-n10{margin-bottom:-40px!important}.v-application .mb-md-n11{margin-bottom:-44px!important}.v-application .mb-md-n12{margin-bottom:-48px!important}.v-application .mb-md-n13{margin-bottom:-52px!important}.v-application .mb-md-n14{margin-bottom:-56px!important}.v-application .mb-md-n15{margin-bottom:-60px!important}.v-application .mb-md-n16{margin-bottom:-64px!important}.v-application .ml-md-n1{margin-left:-4px!important}.v-application .ml-md-n2{margin-left:-8px!important}.v-application .ml-md-n3{margin-left:-12px!important}.v-application .ml-md-n4{margin-left:-16px!important}.v-application .ml-md-n5{margin-left:-20px!important}.v-application .ml-md-n6{margin-left:-24px!important}.v-application .ml-md-n7{margin-left:-28px!important}.v-application .ml-md-n8{margin-left:-32px!important}.v-application .ml-md-n9{margin-left:-36px!important}.v-application .ml-md-n10{margin-left:-40px!important}.v-application .ml-md-n11{margin-left:-44px!important}.v-application .ml-md-n12{margin-left:-48px!important}.v-application .ml-md-n13{margin-left:-52px!important}.v-application .ml-md-n14{margin-left:-56px!important}.v-application .ml-md-n15{margin-left:-60px!important}.v-application .ml-md-n16{margin-left:-64px!important}.v-application--is-ltr .ms-md-n1{margin-left:-4px!important}.v-application--is-rtl .ms-md-n1{margin-right:-4px!important}.v-application--is-ltr .ms-md-n2{margin-left:-8px!important}.v-application--is-rtl .ms-md-n2{margin-right:-8px!important}.v-application--is-ltr .ms-md-n3{margin-left:-12px!important}.v-application--is-rtl .ms-md-n3{margin-right:-12px!important}.v-application--is-ltr .ms-md-n4{margin-left:-16px!important}.v-application--is-rtl .ms-md-n4{margin-right:-16px!important}.v-application--is-ltr .ms-md-n5{margin-left:-20px!important}.v-application--is-rtl .ms-md-n5{margin-right:-20px!important}.v-application--is-ltr .ms-md-n6{margin-left:-24px!important}.v-application--is-rtl .ms-md-n6{margin-right:-24px!important}.v-application--is-ltr .ms-md-n7{margin-left:-28px!important}.v-application--is-rtl .ms-md-n7{margin-right:-28px!important}.v-application--is-ltr .ms-md-n8{margin-left:-32px!important}.v-application--is-rtl .ms-md-n8{margin-right:-32px!important}.v-application--is-ltr .ms-md-n9{margin-left:-36px!important}.v-application--is-rtl .ms-md-n9{margin-right:-36px!important}.v-application--is-ltr .ms-md-n10{margin-left:-40px!important}.v-application--is-rtl .ms-md-n10{margin-right:-40px!important}.v-application--is-ltr .ms-md-n11{margin-left:-44px!important}.v-application--is-rtl .ms-md-n11{margin-right:-44px!important}.v-application--is-ltr .ms-md-n12{margin-left:-48px!important}.v-application--is-rtl .ms-md-n12{margin-right:-48px!important}.v-application--is-ltr .ms-md-n13{margin-left:-52px!important}.v-application--is-rtl .ms-md-n13{margin-right:-52px!important}.v-application--is-ltr .ms-md-n14{margin-left:-56px!important}.v-application--is-rtl .ms-md-n14{margin-right:-56px!important}.v-application--is-ltr .ms-md-n15{margin-left:-60px!important}.v-application--is-rtl .ms-md-n15{margin-right:-60px!important}.v-application--is-ltr .ms-md-n16{margin-left:-64px!important}.v-application--is-rtl .ms-md-n16{margin-right:-64px!important}.v-application--is-ltr .me-md-n1{margin-right:-4px!important}.v-application--is-rtl .me-md-n1{margin-left:-4px!important}.v-application--is-ltr .me-md-n2{margin-right:-8px!important}.v-application--is-rtl .me-md-n2{margin-left:-8px!important}.v-application--is-ltr .me-md-n3{margin-right:-12px!important}.v-application--is-rtl .me-md-n3{margin-left:-12px!important}.v-application--is-ltr .me-md-n4{margin-right:-16px!important}.v-application--is-rtl .me-md-n4{margin-left:-16px!important}.v-application--is-ltr .me-md-n5{margin-right:-20px!important}.v-application--is-rtl .me-md-n5{margin-left:-20px!important}.v-application--is-ltr .me-md-n6{margin-right:-24px!important}.v-application--is-rtl .me-md-n6{margin-left:-24px!important}.v-application--is-ltr .me-md-n7{margin-right:-28px!important}.v-application--is-rtl .me-md-n7{margin-left:-28px!important}.v-application--is-ltr .me-md-n8{margin-right:-32px!important}.v-application--is-rtl .me-md-n8{margin-left:-32px!important}.v-application--is-ltr .me-md-n9{margin-right:-36px!important}.v-application--is-rtl .me-md-n9{margin-left:-36px!important}.v-application--is-ltr .me-md-n10{margin-right:-40px!important}.v-application--is-rtl .me-md-n10{margin-left:-40px!important}.v-application--is-ltr .me-md-n11{margin-right:-44px!important}.v-application--is-rtl .me-md-n11{margin-left:-44px!important}.v-application--is-ltr .me-md-n12{margin-right:-48px!important}.v-application--is-rtl .me-md-n12{margin-left:-48px!important}.v-application--is-ltr .me-md-n13{margin-right:-52px!important}.v-application--is-rtl .me-md-n13{margin-left:-52px!important}.v-application--is-ltr .me-md-n14{margin-right:-56px!important}.v-application--is-rtl .me-md-n14{margin-left:-56px!important}.v-application--is-ltr .me-md-n15{margin-right:-60px!important}.v-application--is-rtl .me-md-n15{margin-left:-60px!important}.v-application--is-ltr .me-md-n16{margin-right:-64px!important}.v-application--is-rtl .me-md-n16{margin-left:-64px!important}.v-application .pa-md-0{padding:0!important}.v-application .pa-md-1{padding:4px!important}.v-application .pa-md-2{padding:8px!important}.v-application .pa-md-3{padding:12px!important}.v-application .pa-md-4{padding:16px!important}.v-application .pa-md-5{padding:20px!important}.v-application .pa-md-6{padding:24px!important}.v-application .pa-md-7{padding:28px!important}.v-application .pa-md-8{padding:32px!important}.v-application .pa-md-9{padding:36px!important}.v-application .pa-md-10{padding:40px!important}.v-application .pa-md-11{padding:44px!important}.v-application .pa-md-12{padding:48px!important}.v-application .pa-md-13{padding:52px!important}.v-application .pa-md-14{padding:56px!important}.v-application .pa-md-15{padding:60px!important}.v-application .pa-md-16{padding:64px!important}.v-application .px-md-0{padding-right:0!important;padding-left:0!important}.v-application .px-md-1{padding-right:4px!important;padding-left:4px!important}.v-application .px-md-2{padding-right:8px!important;padding-left:8px!important}.v-application .px-md-3{padding-right:12px!important;padding-left:12px!important}.v-application .px-md-4{padding-right:16px!important;padding-left:16px!important}.v-application .px-md-5{padding-right:20px!important;padding-left:20px!important}.v-application .px-md-6{padding-right:24px!important;padding-left:24px!important}.v-application .px-md-7{padding-right:28px!important;padding-left:28px!important}.v-application .px-md-8{padding-right:32px!important;padding-left:32px!important}.v-application .px-md-9{padding-right:36px!important;padding-left:36px!important}.v-application .px-md-10{padding-right:40px!important;padding-left:40px!important}.v-application .px-md-11{padding-right:44px!important;padding-left:44px!important}.v-application .px-md-12{padding-right:48px!important;padding-left:48px!important}.v-application .px-md-13{padding-right:52px!important;padding-left:52px!important}.v-application .px-md-14{padding-right:56px!important;padding-left:56px!important}.v-application .px-md-15{padding-right:60px!important;padding-left:60px!important}.v-application .px-md-16{padding-right:64px!important;padding-left:64px!important}.v-application .py-md-0{padding-top:0!important;padding-bottom:0!important}.v-application .py-md-1{padding-top:4px!important;padding-bottom:4px!important}.v-application .py-md-2{padding-top:8px!important;padding-bottom:8px!important}.v-application .py-md-3{padding-top:12px!important;padding-bottom:12px!important}.v-application .py-md-4{padding-top:16px!important;padding-bottom:16px!important}.v-application .py-md-5{padding-top:20px!important;padding-bottom:20px!important}.v-application .py-md-6{padding-top:24px!important;padding-bottom:24px!important}.v-application .py-md-7{padding-top:28px!important;padding-bottom:28px!important}.v-application .py-md-8{padding-top:32px!important;padding-bottom:32px!important}.v-application .py-md-9{padding-top:36px!important;padding-bottom:36px!important}.v-application .py-md-10{padding-top:40px!important;padding-bottom:40px!important}.v-application .py-md-11{padding-top:44px!important;padding-bottom:44px!important}.v-application .py-md-12{padding-top:48px!important;padding-bottom:48px!important}.v-application .py-md-13{padding-top:52px!important;padding-bottom:52px!important}.v-application .py-md-14{padding-top:56px!important;padding-bottom:56px!important}.v-application .py-md-15{padding-top:60px!important;padding-bottom:60px!important}.v-application .py-md-16{padding-top:64px!important;padding-bottom:64px!important}.v-application .pt-md-0{padding-top:0!important}.v-application .pt-md-1{padding-top:4px!important}.v-application .pt-md-2{padding-top:8px!important}.v-application .pt-md-3{padding-top:12px!important}.v-application .pt-md-4{padding-top:16px!important}.v-application .pt-md-5{padding-top:20px!important}.v-application .pt-md-6{padding-top:24px!important}.v-application .pt-md-7{padding-top:28px!important}.v-application .pt-md-8{padding-top:32px!important}.v-application .pt-md-9{padding-top:36px!important}.v-application .pt-md-10{padding-top:40px!important}.v-application .pt-md-11{padding-top:44px!important}.v-application .pt-md-12{padding-top:48px!important}.v-application .pt-md-13{padding-top:52px!important}.v-application .pt-md-14{padding-top:56px!important}.v-application .pt-md-15{padding-top:60px!important}.v-application .pt-md-16{padding-top:64px!important}.v-application .pr-md-0{padding-right:0!important}.v-application .pr-md-1{padding-right:4px!important}.v-application .pr-md-2{padding-right:8px!important}.v-application .pr-md-3{padding-right:12px!important}.v-application .pr-md-4{padding-right:16px!important}.v-application .pr-md-5{padding-right:20px!important}.v-application .pr-md-6{padding-right:24px!important}.v-application .pr-md-7{padding-right:28px!important}.v-application .pr-md-8{padding-right:32px!important}.v-application .pr-md-9{padding-right:36px!important}.v-application .pr-md-10{padding-right:40px!important}.v-application .pr-md-11{padding-right:44px!important}.v-application .pr-md-12{padding-right:48px!important}.v-application .pr-md-13{padding-right:52px!important}.v-application .pr-md-14{padding-right:56px!important}.v-application .pr-md-15{padding-right:60px!important}.v-application .pr-md-16{padding-right:64px!important}.v-application .pb-md-0{padding-bottom:0!important}.v-application .pb-md-1{padding-bottom:4px!important}.v-application .pb-md-2{padding-bottom:8px!important}.v-application .pb-md-3{padding-bottom:12px!important}.v-application .pb-md-4{padding-bottom:16px!important}.v-application .pb-md-5{padding-bottom:20px!important}.v-application .pb-md-6{padding-bottom:24px!important}.v-application .pb-md-7{padding-bottom:28px!important}.v-application .pb-md-8{padding-bottom:32px!important}.v-application .pb-md-9{padding-bottom:36px!important}.v-application .pb-md-10{padding-bottom:40px!important}.v-application .pb-md-11{padding-bottom:44px!important}.v-application .pb-md-12{padding-bottom:48px!important}.v-application .pb-md-13{padding-bottom:52px!important}.v-application .pb-md-14{padding-bottom:56px!important}.v-application .pb-md-15{padding-bottom:60px!important}.v-application .pb-md-16{padding-bottom:64px!important}.v-application .pl-md-0{padding-left:0!important}.v-application .pl-md-1{padding-left:4px!important}.v-application .pl-md-2{padding-left:8px!important}.v-application .pl-md-3{padding-left:12px!important}.v-application .pl-md-4{padding-left:16px!important}.v-application .pl-md-5{padding-left:20px!important}.v-application .pl-md-6{padding-left:24px!important}.v-application .pl-md-7{padding-left:28px!important}.v-application .pl-md-8{padding-left:32px!important}.v-application .pl-md-9{padding-left:36px!important}.v-application .pl-md-10{padding-left:40px!important}.v-application .pl-md-11{padding-left:44px!important}.v-application .pl-md-12{padding-left:48px!important}.v-application .pl-md-13{padding-left:52px!important}.v-application .pl-md-14{padding-left:56px!important}.v-application .pl-md-15{padding-left:60px!important}.v-application .pl-md-16{padding-left:64px!important}.v-application--is-ltr .ps-md-0{padding-left:0!important}.v-application--is-rtl .ps-md-0{padding-right:0!important}.v-application--is-ltr .ps-md-1{padding-left:4px!important}.v-application--is-rtl .ps-md-1{padding-right:4px!important}.v-application--is-ltr .ps-md-2{padding-left:8px!important}.v-application--is-rtl .ps-md-2{padding-right:8px!important}.v-application--is-ltr .ps-md-3{padding-left:12px!important}.v-application--is-rtl .ps-md-3{padding-right:12px!important}.v-application--is-ltr .ps-md-4{padding-left:16px!important}.v-application--is-rtl .ps-md-4{padding-right:16px!important}.v-application--is-ltr .ps-md-5{padding-left:20px!important}.v-application--is-rtl .ps-md-5{padding-right:20px!important}.v-application--is-ltr .ps-md-6{padding-left:24px!important}.v-application--is-rtl .ps-md-6{padding-right:24px!important}.v-application--is-ltr .ps-md-7{padding-left:28px!important}.v-application--is-rtl .ps-md-7{padding-right:28px!important}.v-application--is-ltr .ps-md-8{padding-left:32px!important}.v-application--is-rtl .ps-md-8{padding-right:32px!important}.v-application--is-ltr .ps-md-9{padding-left:36px!important}.v-application--is-rtl .ps-md-9{padding-right:36px!important}.v-application--is-ltr .ps-md-10{padding-left:40px!important}.v-application--is-rtl .ps-md-10{padding-right:40px!important}.v-application--is-ltr .ps-md-11{padding-left:44px!important}.v-application--is-rtl .ps-md-11{padding-right:44px!important}.v-application--is-ltr .ps-md-12{padding-left:48px!important}.v-application--is-rtl .ps-md-12{padding-right:48px!important}.v-application--is-ltr .ps-md-13{padding-left:52px!important}.v-application--is-rtl .ps-md-13{padding-right:52px!important}.v-application--is-ltr .ps-md-14{padding-left:56px!important}.v-application--is-rtl .ps-md-14{padding-right:56px!important}.v-application--is-ltr .ps-md-15{padding-left:60px!important}.v-application--is-rtl .ps-md-15{padding-right:60px!important}.v-application--is-ltr .ps-md-16{padding-left:64px!important}.v-application--is-rtl .ps-md-16{padding-right:64px!important}.v-application--is-ltr .pe-md-0{padding-right:0!important}.v-application--is-rtl .pe-md-0{padding-left:0!important}.v-application--is-ltr .pe-md-1{padding-right:4px!important}.v-application--is-rtl .pe-md-1{padding-left:4px!important}.v-application--is-ltr .pe-md-2{padding-right:8px!important}.v-application--is-rtl .pe-md-2{padding-left:8px!important}.v-application--is-ltr .pe-md-3{padding-right:12px!important}.v-application--is-rtl .pe-md-3{padding-left:12px!important}.v-application--is-ltr .pe-md-4{padding-right:16px!important}.v-application--is-rtl .pe-md-4{padding-left:16px!important}.v-application--is-ltr .pe-md-5{padding-right:20px!important}.v-application--is-rtl .pe-md-5{padding-left:20px!important}.v-application--is-ltr .pe-md-6{padding-right:24px!important}.v-application--is-rtl .pe-md-6{padding-left:24px!important}.v-application--is-ltr .pe-md-7{padding-right:28px!important}.v-application--is-rtl .pe-md-7{padding-left:28px!important}.v-application--is-ltr .pe-md-8{padding-right:32px!important}.v-application--is-rtl .pe-md-8{padding-left:32px!important}.v-application--is-ltr .pe-md-9{padding-right:36px!important}.v-application--is-rtl .pe-md-9{padding-left:36px!important}.v-application--is-ltr .pe-md-10{padding-right:40px!important}.v-application--is-rtl .pe-md-10{padding-left:40px!important}.v-application--is-ltr .pe-md-11{padding-right:44px!important}.v-application--is-rtl .pe-md-11{padding-left:44px!important}.v-application--is-ltr .pe-md-12{padding-right:48px!important}.v-application--is-rtl .pe-md-12{padding-left:48px!important}.v-application--is-ltr .pe-md-13{padding-right:52px!important}.v-application--is-rtl .pe-md-13{padding-left:52px!important}.v-application--is-ltr .pe-md-14{padding-right:56px!important}.v-application--is-rtl .pe-md-14{padding-left:56px!important}.v-application--is-ltr .pe-md-15{padding-right:60px!important}.v-application--is-rtl .pe-md-15{padding-left:60px!important}.v-application--is-ltr .pe-md-16{padding-right:64px!important}.v-application--is-rtl .pe-md-16{padding-left:64px!important}.v-application .text-md-left{text-align:left!important}.v-application .text-md-right{text-align:right!important}.v-application .text-md-center{text-align:center!important}.v-application .text-md-justify{text-align:justify!important}.v-application .text-md-start{text-align:start!important}.v-application .text-md-end{text-align:end!important}.v-application .text-md-h1{font-size:6rem!important;line-height:6rem;letter-spacing:-.015625em!important}.v-application .text-md-h1,.v-application .text-md-h2{font-weight:300;font-family:Roboto,sans-serif!important}.v-application .text-md-h2{font-size:3.75rem!important;line-height:3.75rem;letter-spacing:-.0083333333em!important}.v-application .text-md-h3{font-size:3rem!important;line-height:3.125rem;letter-spacing:normal!important}.v-application .text-md-h3,.v-application .text-md-h4{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .text-md-h4{font-size:2.125rem!important;line-height:2.5rem;letter-spacing:.0073529412em!important}.v-application .text-md-h5{font-size:1.5rem!important;font-weight:400;letter-spacing:normal!important}.v-application .text-md-h5,.v-application .text-md-h6{line-height:2rem;font-family:Roboto,sans-serif!important}.v-application .text-md-h6{font-size:1.25rem!important;font-weight:500;letter-spacing:.0125em!important}.v-application .text-md-subtitle-1{font-size:1rem!important;font-weight:400;line-height:1.75rem;letter-spacing:.009375em!important;font-family:Roboto,sans-serif!important}.v-application .text-md-subtitle-2{font-size:.875rem!important;font-weight:500;line-height:1.375rem;letter-spacing:.0071428571em!important;font-family:Roboto,sans-serif!important}.v-application .text-md-body-1{font-size:1rem!important;font-weight:400;line-height:1.5rem;letter-spacing:.03125em!important;font-family:Roboto,sans-serif!important}.v-application .text-md-body-2{font-weight:400;line-height:1.25rem;letter-spacing:.0178571429em!important}.v-application .text-md-body-2,.v-application .text-md-button{font-size:.875rem!important;font-family:Roboto,sans-serif!important}.v-application .text-md-button{font-weight:500;line-height:2.25rem;letter-spacing:.0892857143em!important;text-transform:uppercase!important}.v-application .text-md-caption{font-weight:400;line-height:1.25rem;letter-spacing:.0333333333em!important}.v-application .text-md-caption,.v-application .text-md-overline{font-size:.75rem!important;font-family:Roboto,sans-serif!important}.v-application .text-md-overline{font-weight:500;line-height:2rem;letter-spacing:.1666666667em!important;text-transform:uppercase!important}}@media(min-width:1264px){.v-application .d-lg-none{display:none!important}.v-application .d-lg-inline{display:inline!important}.v-application .d-lg-inline-block{display:inline-block!important}.v-application .d-lg-block{display:block!important}.v-application .d-lg-table{display:table!important}.v-application .d-lg-table-row{display:table-row!important}.v-application .d-lg-table-cell{display:table-cell!important}.v-application .d-lg-flex{display:flex!important}.v-application .d-lg-inline-flex{display:inline-flex!important}.v-application .float-lg-none{float:none!important}.v-application .float-lg-left{float:left!important}.v-application .float-lg-right{float:right!important}.v-application .flex-lg-fill{flex:1 1 auto!important}.v-application .flex-lg-row{flex-direction:row!important}.v-application .flex-lg-column{flex-direction:column!important}.v-application .flex-lg-row-reverse{flex-direction:row-reverse!important}.v-application .flex-lg-column-reverse{flex-direction:column-reverse!important}.v-application .flex-lg-grow-0{flex-grow:0!important}.v-application .flex-lg-grow-1{flex-grow:1!important}.v-application .flex-lg-shrink-0{flex-shrink:0!important}.v-application .flex-lg-shrink-1{flex-shrink:1!important}.v-application .flex-lg-wrap{flex-wrap:wrap!important}.v-application .flex-lg-nowrap{flex-wrap:nowrap!important}.v-application .flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.v-application .justify-lg-start{justify-content:flex-start!important}.v-application .justify-lg-end{justify-content:flex-end!important}.v-application .justify-lg-center{justify-content:center!important}.v-application .justify-lg-space-between{justify-content:space-between!important}.v-application .justify-lg-space-around{justify-content:space-around!important}.v-application .align-lg-start{align-items:flex-start!important}.v-application .align-lg-end{align-items:flex-end!important}.v-application .align-lg-center{align-items:center!important}.v-application .align-lg-baseline{align-items:baseline!important}.v-application .align-lg-stretch{align-items:stretch!important}.v-application .align-content-lg-start{align-content:flex-start!important}.v-application .align-content-lg-end{align-content:flex-end!important}.v-application .align-content-lg-center{align-content:center!important}.v-application .align-content-lg-space-between{align-content:space-between!important}.v-application .align-content-lg-space-around{align-content:space-around!important}.v-application .align-content-lg-stretch{align-content:stretch!important}.v-application .align-self-lg-auto{align-self:auto!important}.v-application .align-self-lg-start{align-self:flex-start!important}.v-application .align-self-lg-end{align-self:flex-end!important}.v-application .align-self-lg-center{align-self:center!important}.v-application .align-self-lg-baseline{align-self:baseline!important}.v-application .align-self-lg-stretch{align-self:stretch!important}.v-application .order-lg-first{order:-1!important}.v-application .order-lg-0{order:0!important}.v-application .order-lg-1{order:1!important}.v-application .order-lg-2{order:2!important}.v-application .order-lg-3{order:3!important}.v-application .order-lg-4{order:4!important}.v-application .order-lg-5{order:5!important}.v-application .order-lg-6{order:6!important}.v-application .order-lg-7{order:7!important}.v-application .order-lg-8{order:8!important}.v-application .order-lg-9{order:9!important}.v-application .order-lg-10{order:10!important}.v-application .order-lg-11{order:11!important}.v-application .order-lg-12{order:12!important}.v-application .order-lg-last{order:13!important}.v-application .ma-lg-0{margin:0!important}.v-application .ma-lg-1{margin:4px!important}.v-application .ma-lg-2{margin:8px!important}.v-application .ma-lg-3{margin:12px!important}.v-application .ma-lg-4{margin:16px!important}.v-application .ma-lg-5{margin:20px!important}.v-application .ma-lg-6{margin:24px!important}.v-application .ma-lg-7{margin:28px!important}.v-application .ma-lg-8{margin:32px!important}.v-application .ma-lg-9{margin:36px!important}.v-application .ma-lg-10{margin:40px!important}.v-application .ma-lg-11{margin:44px!important}.v-application .ma-lg-12{margin:48px!important}.v-application .ma-lg-13{margin:52px!important}.v-application .ma-lg-14{margin:56px!important}.v-application .ma-lg-15{margin:60px!important}.v-application .ma-lg-16{margin:64px!important}.v-application .ma-lg-auto{margin:auto!important}.v-application .mx-lg-0{margin-right:0!important;margin-left:0!important}.v-application .mx-lg-1{margin-right:4px!important;margin-left:4px!important}.v-application .mx-lg-2{margin-right:8px!important;margin-left:8px!important}.v-application .mx-lg-3{margin-right:12px!important;margin-left:12px!important}.v-application .mx-lg-4{margin-right:16px!important;margin-left:16px!important}.v-application .mx-lg-5{margin-right:20px!important;margin-left:20px!important}.v-application .mx-lg-6{margin-right:24px!important;margin-left:24px!important}.v-application .mx-lg-7{margin-right:28px!important;margin-left:28px!important}.v-application .mx-lg-8{margin-right:32px!important;margin-left:32px!important}.v-application .mx-lg-9{margin-right:36px!important;margin-left:36px!important}.v-application .mx-lg-10{margin-right:40px!important;margin-left:40px!important}.v-application .mx-lg-11{margin-right:44px!important;margin-left:44px!important}.v-application .mx-lg-12{margin-right:48px!important;margin-left:48px!important}.v-application .mx-lg-13{margin-right:52px!important;margin-left:52px!important}.v-application .mx-lg-14{margin-right:56px!important;margin-left:56px!important}.v-application .mx-lg-15{margin-right:60px!important;margin-left:60px!important}.v-application .mx-lg-16{margin-right:64px!important;margin-left:64px!important}.v-application .mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.v-application .my-lg-0{margin-top:0!important;margin-bottom:0!important}.v-application .my-lg-1{margin-top:4px!important;margin-bottom:4px!important}.v-application .my-lg-2{margin-top:8px!important;margin-bottom:8px!important}.v-application .my-lg-3{margin-top:12px!important;margin-bottom:12px!important}.v-application .my-lg-4{margin-top:16px!important;margin-bottom:16px!important}.v-application .my-lg-5{margin-top:20px!important;margin-bottom:20px!important}.v-application .my-lg-6{margin-top:24px!important;margin-bottom:24px!important}.v-application .my-lg-7{margin-top:28px!important;margin-bottom:28px!important}.v-application .my-lg-8{margin-top:32px!important;margin-bottom:32px!important}.v-application .my-lg-9{margin-top:36px!important;margin-bottom:36px!important}.v-application .my-lg-10{margin-top:40px!important;margin-bottom:40px!important}.v-application .my-lg-11{margin-top:44px!important;margin-bottom:44px!important}.v-application .my-lg-12{margin-top:48px!important;margin-bottom:48px!important}.v-application .my-lg-13{margin-top:52px!important;margin-bottom:52px!important}.v-application .my-lg-14{margin-top:56px!important;margin-bottom:56px!important}.v-application .my-lg-15{margin-top:60px!important;margin-bottom:60px!important}.v-application .my-lg-16{margin-top:64px!important;margin-bottom:64px!important}.v-application .my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.v-application .mt-lg-0{margin-top:0!important}.v-application .mt-lg-1{margin-top:4px!important}.v-application .mt-lg-2{margin-top:8px!important}.v-application .mt-lg-3{margin-top:12px!important}.v-application .mt-lg-4{margin-top:16px!important}.v-application .mt-lg-5{margin-top:20px!important}.v-application .mt-lg-6{margin-top:24px!important}.v-application .mt-lg-7{margin-top:28px!important}.v-application .mt-lg-8{margin-top:32px!important}.v-application .mt-lg-9{margin-top:36px!important}.v-application .mt-lg-10{margin-top:40px!important}.v-application .mt-lg-11{margin-top:44px!important}.v-application .mt-lg-12{margin-top:48px!important}.v-application .mt-lg-13{margin-top:52px!important}.v-application .mt-lg-14{margin-top:56px!important}.v-application .mt-lg-15{margin-top:60px!important}.v-application .mt-lg-16{margin-top:64px!important}.v-application .mt-lg-auto{margin-top:auto!important}.v-application .mr-lg-0{margin-right:0!important}.v-application .mr-lg-1{margin-right:4px!important}.v-application .mr-lg-2{margin-right:8px!important}.v-application .mr-lg-3{margin-right:12px!important}.v-application .mr-lg-4{margin-right:16px!important}.v-application .mr-lg-5{margin-right:20px!important}.v-application .mr-lg-6{margin-right:24px!important}.v-application .mr-lg-7{margin-right:28px!important}.v-application .mr-lg-8{margin-right:32px!important}.v-application .mr-lg-9{margin-right:36px!important}.v-application .mr-lg-10{margin-right:40px!important}.v-application .mr-lg-11{margin-right:44px!important}.v-application .mr-lg-12{margin-right:48px!important}.v-application .mr-lg-13{margin-right:52px!important}.v-application .mr-lg-14{margin-right:56px!important}.v-application .mr-lg-15{margin-right:60px!important}.v-application .mr-lg-16{margin-right:64px!important}.v-application .mr-lg-auto{margin-right:auto!important}.v-application .mb-lg-0{margin-bottom:0!important}.v-application .mb-lg-1{margin-bottom:4px!important}.v-application .mb-lg-2{margin-bottom:8px!important}.v-application .mb-lg-3{margin-bottom:12px!important}.v-application .mb-lg-4{margin-bottom:16px!important}.v-application .mb-lg-5{margin-bottom:20px!important}.v-application .mb-lg-6{margin-bottom:24px!important}.v-application .mb-lg-7{margin-bottom:28px!important}.v-application .mb-lg-8{margin-bottom:32px!important}.v-application .mb-lg-9{margin-bottom:36px!important}.v-application .mb-lg-10{margin-bottom:40px!important}.v-application .mb-lg-11{margin-bottom:44px!important}.v-application .mb-lg-12{margin-bottom:48px!important}.v-application .mb-lg-13{margin-bottom:52px!important}.v-application .mb-lg-14{margin-bottom:56px!important}.v-application .mb-lg-15{margin-bottom:60px!important}.v-application .mb-lg-16{margin-bottom:64px!important}.v-application .mb-lg-auto{margin-bottom:auto!important}.v-application .ml-lg-0{margin-left:0!important}.v-application .ml-lg-1{margin-left:4px!important}.v-application .ml-lg-2{margin-left:8px!important}.v-application .ml-lg-3{margin-left:12px!important}.v-application .ml-lg-4{margin-left:16px!important}.v-application .ml-lg-5{margin-left:20px!important}.v-application .ml-lg-6{margin-left:24px!important}.v-application .ml-lg-7{margin-left:28px!important}.v-application .ml-lg-8{margin-left:32px!important}.v-application .ml-lg-9{margin-left:36px!important}.v-application .ml-lg-10{margin-left:40px!important}.v-application .ml-lg-11{margin-left:44px!important}.v-application .ml-lg-12{margin-left:48px!important}.v-application .ml-lg-13{margin-left:52px!important}.v-application .ml-lg-14{margin-left:56px!important}.v-application .ml-lg-15{margin-left:60px!important}.v-application .ml-lg-16{margin-left:64px!important}.v-application .ml-lg-auto{margin-left:auto!important}.v-application--is-ltr .ms-lg-0{margin-left:0!important}.v-application--is-rtl .ms-lg-0{margin-right:0!important}.v-application--is-ltr .ms-lg-1{margin-left:4px!important}.v-application--is-rtl .ms-lg-1{margin-right:4px!important}.v-application--is-ltr .ms-lg-2{margin-left:8px!important}.v-application--is-rtl .ms-lg-2{margin-right:8px!important}.v-application--is-ltr .ms-lg-3{margin-left:12px!important}.v-application--is-rtl .ms-lg-3{margin-right:12px!important}.v-application--is-ltr .ms-lg-4{margin-left:16px!important}.v-application--is-rtl .ms-lg-4{margin-right:16px!important}.v-application--is-ltr .ms-lg-5{margin-left:20px!important}.v-application--is-rtl .ms-lg-5{margin-right:20px!important}.v-application--is-ltr .ms-lg-6{margin-left:24px!important}.v-application--is-rtl .ms-lg-6{margin-right:24px!important}.v-application--is-ltr .ms-lg-7{margin-left:28px!important}.v-application--is-rtl .ms-lg-7{margin-right:28px!important}.v-application--is-ltr .ms-lg-8{margin-left:32px!important}.v-application--is-rtl .ms-lg-8{margin-right:32px!important}.v-application--is-ltr .ms-lg-9{margin-left:36px!important}.v-application--is-rtl .ms-lg-9{margin-right:36px!important}.v-application--is-ltr .ms-lg-10{margin-left:40px!important}.v-application--is-rtl .ms-lg-10{margin-right:40px!important}.v-application--is-ltr .ms-lg-11{margin-left:44px!important}.v-application--is-rtl .ms-lg-11{margin-right:44px!important}.v-application--is-ltr .ms-lg-12{margin-left:48px!important}.v-application--is-rtl .ms-lg-12{margin-right:48px!important}.v-application--is-ltr .ms-lg-13{margin-left:52px!important}.v-application--is-rtl .ms-lg-13{margin-right:52px!important}.v-application--is-ltr .ms-lg-14{margin-left:56px!important}.v-application--is-rtl .ms-lg-14{margin-right:56px!important}.v-application--is-ltr .ms-lg-15{margin-left:60px!important}.v-application--is-rtl .ms-lg-15{margin-right:60px!important}.v-application--is-ltr .ms-lg-16{margin-left:64px!important}.v-application--is-rtl .ms-lg-16{margin-right:64px!important}.v-application--is-ltr .ms-lg-auto{margin-left:auto!important}.v-application--is-rtl .ms-lg-auto{margin-right:auto!important}.v-application--is-ltr .me-lg-0{margin-right:0!important}.v-application--is-rtl .me-lg-0{margin-left:0!important}.v-application--is-ltr .me-lg-1{margin-right:4px!important}.v-application--is-rtl .me-lg-1{margin-left:4px!important}.v-application--is-ltr .me-lg-2{margin-right:8px!important}.v-application--is-rtl .me-lg-2{margin-left:8px!important}.v-application--is-ltr .me-lg-3{margin-right:12px!important}.v-application--is-rtl .me-lg-3{margin-left:12px!important}.v-application--is-ltr .me-lg-4{margin-right:16px!important}.v-application--is-rtl .me-lg-4{margin-left:16px!important}.v-application--is-ltr .me-lg-5{margin-right:20px!important}.v-application--is-rtl .me-lg-5{margin-left:20px!important}.v-application--is-ltr .me-lg-6{margin-right:24px!important}.v-application--is-rtl .me-lg-6{margin-left:24px!important}.v-application--is-ltr .me-lg-7{margin-right:28px!important}.v-application--is-rtl .me-lg-7{margin-left:28px!important}.v-application--is-ltr .me-lg-8{margin-right:32px!important}.v-application--is-rtl .me-lg-8{margin-left:32px!important}.v-application--is-ltr .me-lg-9{margin-right:36px!important}.v-application--is-rtl .me-lg-9{margin-left:36px!important}.v-application--is-ltr .me-lg-10{margin-right:40px!important}.v-application--is-rtl .me-lg-10{margin-left:40px!important}.v-application--is-ltr .me-lg-11{margin-right:44px!important}.v-application--is-rtl .me-lg-11{margin-left:44px!important}.v-application--is-ltr .me-lg-12{margin-right:48px!important}.v-application--is-rtl .me-lg-12{margin-left:48px!important}.v-application--is-ltr .me-lg-13{margin-right:52px!important}.v-application--is-rtl .me-lg-13{margin-left:52px!important}.v-application--is-ltr .me-lg-14{margin-right:56px!important}.v-application--is-rtl .me-lg-14{margin-left:56px!important}.v-application--is-ltr .me-lg-15{margin-right:60px!important}.v-application--is-rtl .me-lg-15{margin-left:60px!important}.v-application--is-ltr .me-lg-16{margin-right:64px!important}.v-application--is-rtl .me-lg-16{margin-left:64px!important}.v-application--is-ltr .me-lg-auto{margin-right:auto!important}.v-application--is-rtl .me-lg-auto{margin-left:auto!important}.v-application .ma-lg-n1{margin:-4px!important}.v-application .ma-lg-n2{margin:-8px!important}.v-application .ma-lg-n3{margin:-12px!important}.v-application .ma-lg-n4{margin:-16px!important}.v-application .ma-lg-n5{margin:-20px!important}.v-application .ma-lg-n6{margin:-24px!important}.v-application .ma-lg-n7{margin:-28px!important}.v-application .ma-lg-n8{margin:-32px!important}.v-application .ma-lg-n9{margin:-36px!important}.v-application .ma-lg-n10{margin:-40px!important}.v-application .ma-lg-n11{margin:-44px!important}.v-application .ma-lg-n12{margin:-48px!important}.v-application .ma-lg-n13{margin:-52px!important}.v-application .ma-lg-n14{margin:-56px!important}.v-application .ma-lg-n15{margin:-60px!important}.v-application .ma-lg-n16{margin:-64px!important}.v-application .mx-lg-n1{margin-right:-4px!important;margin-left:-4px!important}.v-application .mx-lg-n2{margin-right:-8px!important;margin-left:-8px!important}.v-application .mx-lg-n3{margin-right:-12px!important;margin-left:-12px!important}.v-application .mx-lg-n4{margin-right:-16px!important;margin-left:-16px!important}.v-application .mx-lg-n5{margin-right:-20px!important;margin-left:-20px!important}.v-application .mx-lg-n6{margin-right:-24px!important;margin-left:-24px!important}.v-application .mx-lg-n7{margin-right:-28px!important;margin-left:-28px!important}.v-application .mx-lg-n8{margin-right:-32px!important;margin-left:-32px!important}.v-application .mx-lg-n9{margin-right:-36px!important;margin-left:-36px!important}.v-application .mx-lg-n10{margin-right:-40px!important;margin-left:-40px!important}.v-application .mx-lg-n11{margin-right:-44px!important;margin-left:-44px!important}.v-application .mx-lg-n12{margin-right:-48px!important;margin-left:-48px!important}.v-application .mx-lg-n13{margin-right:-52px!important;margin-left:-52px!important}.v-application .mx-lg-n14{margin-right:-56px!important;margin-left:-56px!important}.v-application .mx-lg-n15{margin-right:-60px!important;margin-left:-60px!important}.v-application .mx-lg-n16{margin-right:-64px!important;margin-left:-64px!important}.v-application .my-lg-n1{margin-top:-4px!important;margin-bottom:-4px!important}.v-application .my-lg-n2{margin-top:-8px!important;margin-bottom:-8px!important}.v-application .my-lg-n3{margin-top:-12px!important;margin-bottom:-12px!important}.v-application .my-lg-n4{margin-top:-16px!important;margin-bottom:-16px!important}.v-application .my-lg-n5{margin-top:-20px!important;margin-bottom:-20px!important}.v-application .my-lg-n6{margin-top:-24px!important;margin-bottom:-24px!important}.v-application .my-lg-n7{margin-top:-28px!important;margin-bottom:-28px!important}.v-application .my-lg-n8{margin-top:-32px!important;margin-bottom:-32px!important}.v-application .my-lg-n9{margin-top:-36px!important;margin-bottom:-36px!important}.v-application .my-lg-n10{margin-top:-40px!important;margin-bottom:-40px!important}.v-application .my-lg-n11{margin-top:-44px!important;margin-bottom:-44px!important}.v-application .my-lg-n12{margin-top:-48px!important;margin-bottom:-48px!important}.v-application .my-lg-n13{margin-top:-52px!important;margin-bottom:-52px!important}.v-application .my-lg-n14{margin-top:-56px!important;margin-bottom:-56px!important}.v-application .my-lg-n15{margin-top:-60px!important;margin-bottom:-60px!important}.v-application .my-lg-n16{margin-top:-64px!important;margin-bottom:-64px!important}.v-application .mt-lg-n1{margin-top:-4px!important}.v-application .mt-lg-n2{margin-top:-8px!important}.v-application .mt-lg-n3{margin-top:-12px!important}.v-application .mt-lg-n4{margin-top:-16px!important}.v-application .mt-lg-n5{margin-top:-20px!important}.v-application .mt-lg-n6{margin-top:-24px!important}.v-application .mt-lg-n7{margin-top:-28px!important}.v-application .mt-lg-n8{margin-top:-32px!important}.v-application .mt-lg-n9{margin-top:-36px!important}.v-application .mt-lg-n10{margin-top:-40px!important}.v-application .mt-lg-n11{margin-top:-44px!important}.v-application .mt-lg-n12{margin-top:-48px!important}.v-application .mt-lg-n13{margin-top:-52px!important}.v-application .mt-lg-n14{margin-top:-56px!important}.v-application .mt-lg-n15{margin-top:-60px!important}.v-application .mt-lg-n16{margin-top:-64px!important}.v-application .mr-lg-n1{margin-right:-4px!important}.v-application .mr-lg-n2{margin-right:-8px!important}.v-application .mr-lg-n3{margin-right:-12px!important}.v-application .mr-lg-n4{margin-right:-16px!important}.v-application .mr-lg-n5{margin-right:-20px!important}.v-application .mr-lg-n6{margin-right:-24px!important}.v-application .mr-lg-n7{margin-right:-28px!important}.v-application .mr-lg-n8{margin-right:-32px!important}.v-application .mr-lg-n9{margin-right:-36px!important}.v-application .mr-lg-n10{margin-right:-40px!important}.v-application .mr-lg-n11{margin-right:-44px!important}.v-application .mr-lg-n12{margin-right:-48px!important}.v-application .mr-lg-n13{margin-right:-52px!important}.v-application .mr-lg-n14{margin-right:-56px!important}.v-application .mr-lg-n15{margin-right:-60px!important}.v-application .mr-lg-n16{margin-right:-64px!important}.v-application .mb-lg-n1{margin-bottom:-4px!important}.v-application .mb-lg-n2{margin-bottom:-8px!important}.v-application .mb-lg-n3{margin-bottom:-12px!important}.v-application .mb-lg-n4{margin-bottom:-16px!important}.v-application .mb-lg-n5{margin-bottom:-20px!important}.v-application .mb-lg-n6{margin-bottom:-24px!important}.v-application .mb-lg-n7{margin-bottom:-28px!important}.v-application .mb-lg-n8{margin-bottom:-32px!important}.v-application .mb-lg-n9{margin-bottom:-36px!important}.v-application .mb-lg-n10{margin-bottom:-40px!important}.v-application .mb-lg-n11{margin-bottom:-44px!important}.v-application .mb-lg-n12{margin-bottom:-48px!important}.v-application .mb-lg-n13{margin-bottom:-52px!important}.v-application .mb-lg-n14{margin-bottom:-56px!important}.v-application .mb-lg-n15{margin-bottom:-60px!important}.v-application .mb-lg-n16{margin-bottom:-64px!important}.v-application .ml-lg-n1{margin-left:-4px!important}.v-application .ml-lg-n2{margin-left:-8px!important}.v-application .ml-lg-n3{margin-left:-12px!important}.v-application .ml-lg-n4{margin-left:-16px!important}.v-application .ml-lg-n5{margin-left:-20px!important}.v-application .ml-lg-n6{margin-left:-24px!important}.v-application .ml-lg-n7{margin-left:-28px!important}.v-application .ml-lg-n8{margin-left:-32px!important}.v-application .ml-lg-n9{margin-left:-36px!important}.v-application .ml-lg-n10{margin-left:-40px!important}.v-application .ml-lg-n11{margin-left:-44px!important}.v-application .ml-lg-n12{margin-left:-48px!important}.v-application .ml-lg-n13{margin-left:-52px!important}.v-application .ml-lg-n14{margin-left:-56px!important}.v-application .ml-lg-n15{margin-left:-60px!important}.v-application .ml-lg-n16{margin-left:-64px!important}.v-application--is-ltr .ms-lg-n1{margin-left:-4px!important}.v-application--is-rtl .ms-lg-n1{margin-right:-4px!important}.v-application--is-ltr .ms-lg-n2{margin-left:-8px!important}.v-application--is-rtl .ms-lg-n2{margin-right:-8px!important}.v-application--is-ltr .ms-lg-n3{margin-left:-12px!important}.v-application--is-rtl .ms-lg-n3{margin-right:-12px!important}.v-application--is-ltr .ms-lg-n4{margin-left:-16px!important}.v-application--is-rtl .ms-lg-n4{margin-right:-16px!important}.v-application--is-ltr .ms-lg-n5{margin-left:-20px!important}.v-application--is-rtl .ms-lg-n5{margin-right:-20px!important}.v-application--is-ltr .ms-lg-n6{margin-left:-24px!important}.v-application--is-rtl .ms-lg-n6{margin-right:-24px!important}.v-application--is-ltr .ms-lg-n7{margin-left:-28px!important}.v-application--is-rtl .ms-lg-n7{margin-right:-28px!important}.v-application--is-ltr .ms-lg-n8{margin-left:-32px!important}.v-application--is-rtl .ms-lg-n8{margin-right:-32px!important}.v-application--is-ltr .ms-lg-n9{margin-left:-36px!important}.v-application--is-rtl .ms-lg-n9{margin-right:-36px!important}.v-application--is-ltr .ms-lg-n10{margin-left:-40px!important}.v-application--is-rtl .ms-lg-n10{margin-right:-40px!important}.v-application--is-ltr .ms-lg-n11{margin-left:-44px!important}.v-application--is-rtl .ms-lg-n11{margin-right:-44px!important}.v-application--is-ltr .ms-lg-n12{margin-left:-48px!important}.v-application--is-rtl .ms-lg-n12{margin-right:-48px!important}.v-application--is-ltr .ms-lg-n13{margin-left:-52px!important}.v-application--is-rtl .ms-lg-n13{margin-right:-52px!important}.v-application--is-ltr .ms-lg-n14{margin-left:-56px!important}.v-application--is-rtl .ms-lg-n14{margin-right:-56px!important}.v-application--is-ltr .ms-lg-n15{margin-left:-60px!important}.v-application--is-rtl .ms-lg-n15{margin-right:-60px!important}.v-application--is-ltr .ms-lg-n16{margin-left:-64px!important}.v-application--is-rtl .ms-lg-n16{margin-right:-64px!important}.v-application--is-ltr .me-lg-n1{margin-right:-4px!important}.v-application--is-rtl .me-lg-n1{margin-left:-4px!important}.v-application--is-ltr .me-lg-n2{margin-right:-8px!important}.v-application--is-rtl .me-lg-n2{margin-left:-8px!important}.v-application--is-ltr .me-lg-n3{margin-right:-12px!important}.v-application--is-rtl .me-lg-n3{margin-left:-12px!important}.v-application--is-ltr .me-lg-n4{margin-right:-16px!important}.v-application--is-rtl .me-lg-n4{margin-left:-16px!important}.v-application--is-ltr .me-lg-n5{margin-right:-20px!important}.v-application--is-rtl .me-lg-n5{margin-left:-20px!important}.v-application--is-ltr .me-lg-n6{margin-right:-24px!important}.v-application--is-rtl .me-lg-n6{margin-left:-24px!important}.v-application--is-ltr .me-lg-n7{margin-right:-28px!important}.v-application--is-rtl .me-lg-n7{margin-left:-28px!important}.v-application--is-ltr .me-lg-n8{margin-right:-32px!important}.v-application--is-rtl .me-lg-n8{margin-left:-32px!important}.v-application--is-ltr .me-lg-n9{margin-right:-36px!important}.v-application--is-rtl .me-lg-n9{margin-left:-36px!important}.v-application--is-ltr .me-lg-n10{margin-right:-40px!important}.v-application--is-rtl .me-lg-n10{margin-left:-40px!important}.v-application--is-ltr .me-lg-n11{margin-right:-44px!important}.v-application--is-rtl .me-lg-n11{margin-left:-44px!important}.v-application--is-ltr .me-lg-n12{margin-right:-48px!important}.v-application--is-rtl .me-lg-n12{margin-left:-48px!important}.v-application--is-ltr .me-lg-n13{margin-right:-52px!important}.v-application--is-rtl .me-lg-n13{margin-left:-52px!important}.v-application--is-ltr .me-lg-n14{margin-right:-56px!important}.v-application--is-rtl .me-lg-n14{margin-left:-56px!important}.v-application--is-ltr .me-lg-n15{margin-right:-60px!important}.v-application--is-rtl .me-lg-n15{margin-left:-60px!important}.v-application--is-ltr .me-lg-n16{margin-right:-64px!important}.v-application--is-rtl .me-lg-n16{margin-left:-64px!important}.v-application .pa-lg-0{padding:0!important}.v-application .pa-lg-1{padding:4px!important}.v-application .pa-lg-2{padding:8px!important}.v-application .pa-lg-3{padding:12px!important}.v-application .pa-lg-4{padding:16px!important}.v-application .pa-lg-5{padding:20px!important}.v-application .pa-lg-6{padding:24px!important}.v-application .pa-lg-7{padding:28px!important}.v-application .pa-lg-8{padding:32px!important}.v-application .pa-lg-9{padding:36px!important}.v-application .pa-lg-10{padding:40px!important}.v-application .pa-lg-11{padding:44px!important}.v-application .pa-lg-12{padding:48px!important}.v-application .pa-lg-13{padding:52px!important}.v-application .pa-lg-14{padding:56px!important}.v-application .pa-lg-15{padding:60px!important}.v-application .pa-lg-16{padding:64px!important}.v-application .px-lg-0{padding-right:0!important;padding-left:0!important}.v-application .px-lg-1{padding-right:4px!important;padding-left:4px!important}.v-application .px-lg-2{padding-right:8px!important;padding-left:8px!important}.v-application .px-lg-3{padding-right:12px!important;padding-left:12px!important}.v-application .px-lg-4{padding-right:16px!important;padding-left:16px!important}.v-application .px-lg-5{padding-right:20px!important;padding-left:20px!important}.v-application .px-lg-6{padding-right:24px!important;padding-left:24px!important}.v-application .px-lg-7{padding-right:28px!important;padding-left:28px!important}.v-application .px-lg-8{padding-right:32px!important;padding-left:32px!important}.v-application .px-lg-9{padding-right:36px!important;padding-left:36px!important}.v-application .px-lg-10{padding-right:40px!important;padding-left:40px!important}.v-application .px-lg-11{padding-right:44px!important;padding-left:44px!important}.v-application .px-lg-12{padding-right:48px!important;padding-left:48px!important}.v-application .px-lg-13{padding-right:52px!important;padding-left:52px!important}.v-application .px-lg-14{padding-right:56px!important;padding-left:56px!important}.v-application .px-lg-15{padding-right:60px!important;padding-left:60px!important}.v-application .px-lg-16{padding-right:64px!important;padding-left:64px!important}.v-application .py-lg-0{padding-top:0!important;padding-bottom:0!important}.v-application .py-lg-1{padding-top:4px!important;padding-bottom:4px!important}.v-application .py-lg-2{padding-top:8px!important;padding-bottom:8px!important}.v-application .py-lg-3{padding-top:12px!important;padding-bottom:12px!important}.v-application .py-lg-4{padding-top:16px!important;padding-bottom:16px!important}.v-application .py-lg-5{padding-top:20px!important;padding-bottom:20px!important}.v-application .py-lg-6{padding-top:24px!important;padding-bottom:24px!important}.v-application .py-lg-7{padding-top:28px!important;padding-bottom:28px!important}.v-application .py-lg-8{padding-top:32px!important;padding-bottom:32px!important}.v-application .py-lg-9{padding-top:36px!important;padding-bottom:36px!important}.v-application .py-lg-10{padding-top:40px!important;padding-bottom:40px!important}.v-application .py-lg-11{padding-top:44px!important;padding-bottom:44px!important}.v-application .py-lg-12{padding-top:48px!important;padding-bottom:48px!important}.v-application .py-lg-13{padding-top:52px!important;padding-bottom:52px!important}.v-application .py-lg-14{padding-top:56px!important;padding-bottom:56px!important}.v-application .py-lg-15{padding-top:60px!important;padding-bottom:60px!important}.v-application .py-lg-16{padding-top:64px!important;padding-bottom:64px!important}.v-application .pt-lg-0{padding-top:0!important}.v-application .pt-lg-1{padding-top:4px!important}.v-application .pt-lg-2{padding-top:8px!important}.v-application .pt-lg-3{padding-top:12px!important}.v-application .pt-lg-4{padding-top:16px!important}.v-application .pt-lg-5{padding-top:20px!important}.v-application .pt-lg-6{padding-top:24px!important}.v-application .pt-lg-7{padding-top:28px!important}.v-application .pt-lg-8{padding-top:32px!important}.v-application .pt-lg-9{padding-top:36px!important}.v-application .pt-lg-10{padding-top:40px!important}.v-application .pt-lg-11{padding-top:44px!important}.v-application .pt-lg-12{padding-top:48px!important}.v-application .pt-lg-13{padding-top:52px!important}.v-application .pt-lg-14{padding-top:56px!important}.v-application .pt-lg-15{padding-top:60px!important}.v-application .pt-lg-16{padding-top:64px!important}.v-application .pr-lg-0{padding-right:0!important}.v-application .pr-lg-1{padding-right:4px!important}.v-application .pr-lg-2{padding-right:8px!important}.v-application .pr-lg-3{padding-right:12px!important}.v-application .pr-lg-4{padding-right:16px!important}.v-application .pr-lg-5{padding-right:20px!important}.v-application .pr-lg-6{padding-right:24px!important}.v-application .pr-lg-7{padding-right:28px!important}.v-application .pr-lg-8{padding-right:32px!important}.v-application .pr-lg-9{padding-right:36px!important}.v-application .pr-lg-10{padding-right:40px!important}.v-application .pr-lg-11{padding-right:44px!important}.v-application .pr-lg-12{padding-right:48px!important}.v-application .pr-lg-13{padding-right:52px!important}.v-application .pr-lg-14{padding-right:56px!important}.v-application .pr-lg-15{padding-right:60px!important}.v-application .pr-lg-16{padding-right:64px!important}.v-application .pb-lg-0{padding-bottom:0!important}.v-application .pb-lg-1{padding-bottom:4px!important}.v-application .pb-lg-2{padding-bottom:8px!important}.v-application .pb-lg-3{padding-bottom:12px!important}.v-application .pb-lg-4{padding-bottom:16px!important}.v-application .pb-lg-5{padding-bottom:20px!important}.v-application .pb-lg-6{padding-bottom:24px!important}.v-application .pb-lg-7{padding-bottom:28px!important}.v-application .pb-lg-8{padding-bottom:32px!important}.v-application .pb-lg-9{padding-bottom:36px!important}.v-application .pb-lg-10{padding-bottom:40px!important}.v-application .pb-lg-11{padding-bottom:44px!important}.v-application .pb-lg-12{padding-bottom:48px!important}.v-application .pb-lg-13{padding-bottom:52px!important}.v-application .pb-lg-14{padding-bottom:56px!important}.v-application .pb-lg-15{padding-bottom:60px!important}.v-application .pb-lg-16{padding-bottom:64px!important}.v-application .pl-lg-0{padding-left:0!important}.v-application .pl-lg-1{padding-left:4px!important}.v-application .pl-lg-2{padding-left:8px!important}.v-application .pl-lg-3{padding-left:12px!important}.v-application .pl-lg-4{padding-left:16px!important}.v-application .pl-lg-5{padding-left:20px!important}.v-application .pl-lg-6{padding-left:24px!important}.v-application .pl-lg-7{padding-left:28px!important}.v-application .pl-lg-8{padding-left:32px!important}.v-application .pl-lg-9{padding-left:36px!important}.v-application .pl-lg-10{padding-left:40px!important}.v-application .pl-lg-11{padding-left:44px!important}.v-application .pl-lg-12{padding-left:48px!important}.v-application .pl-lg-13{padding-left:52px!important}.v-application .pl-lg-14{padding-left:56px!important}.v-application .pl-lg-15{padding-left:60px!important}.v-application .pl-lg-16{padding-left:64px!important}.v-application--is-ltr .ps-lg-0{padding-left:0!important}.v-application--is-rtl .ps-lg-0{padding-right:0!important}.v-application--is-ltr .ps-lg-1{padding-left:4px!important}.v-application--is-rtl .ps-lg-1{padding-right:4px!important}.v-application--is-ltr .ps-lg-2{padding-left:8px!important}.v-application--is-rtl .ps-lg-2{padding-right:8px!important}.v-application--is-ltr .ps-lg-3{padding-left:12px!important}.v-application--is-rtl .ps-lg-3{padding-right:12px!important}.v-application--is-ltr .ps-lg-4{padding-left:16px!important}.v-application--is-rtl .ps-lg-4{padding-right:16px!important}.v-application--is-ltr .ps-lg-5{padding-left:20px!important}.v-application--is-rtl .ps-lg-5{padding-right:20px!important}.v-application--is-ltr .ps-lg-6{padding-left:24px!important}.v-application--is-rtl .ps-lg-6{padding-right:24px!important}.v-application--is-ltr .ps-lg-7{padding-left:28px!important}.v-application--is-rtl .ps-lg-7{padding-right:28px!important}.v-application--is-ltr .ps-lg-8{padding-left:32px!important}.v-application--is-rtl .ps-lg-8{padding-right:32px!important}.v-application--is-ltr .ps-lg-9{padding-left:36px!important}.v-application--is-rtl .ps-lg-9{padding-right:36px!important}.v-application--is-ltr .ps-lg-10{padding-left:40px!important}.v-application--is-rtl .ps-lg-10{padding-right:40px!important}.v-application--is-ltr .ps-lg-11{padding-left:44px!important}.v-application--is-rtl .ps-lg-11{padding-right:44px!important}.v-application--is-ltr .ps-lg-12{padding-left:48px!important}.v-application--is-rtl .ps-lg-12{padding-right:48px!important}.v-application--is-ltr .ps-lg-13{padding-left:52px!important}.v-application--is-rtl .ps-lg-13{padding-right:52px!important}.v-application--is-ltr .ps-lg-14{padding-left:56px!important}.v-application--is-rtl .ps-lg-14{padding-right:56px!important}.v-application--is-ltr .ps-lg-15{padding-left:60px!important}.v-application--is-rtl .ps-lg-15{padding-right:60px!important}.v-application--is-ltr .ps-lg-16{padding-left:64px!important}.v-application--is-rtl .ps-lg-16{padding-right:64px!important}.v-application--is-ltr .pe-lg-0{padding-right:0!important}.v-application--is-rtl .pe-lg-0{padding-left:0!important}.v-application--is-ltr .pe-lg-1{padding-right:4px!important}.v-application--is-rtl .pe-lg-1{padding-left:4px!important}.v-application--is-ltr .pe-lg-2{padding-right:8px!important}.v-application--is-rtl .pe-lg-2{padding-left:8px!important}.v-application--is-ltr .pe-lg-3{padding-right:12px!important}.v-application--is-rtl .pe-lg-3{padding-left:12px!important}.v-application--is-ltr .pe-lg-4{padding-right:16px!important}.v-application--is-rtl .pe-lg-4{padding-left:16px!important}.v-application--is-ltr .pe-lg-5{padding-right:20px!important}.v-application--is-rtl .pe-lg-5{padding-left:20px!important}.v-application--is-ltr .pe-lg-6{padding-right:24px!important}.v-application--is-rtl .pe-lg-6{padding-left:24px!important}.v-application--is-ltr .pe-lg-7{padding-right:28px!important}.v-application--is-rtl .pe-lg-7{padding-left:28px!important}.v-application--is-ltr .pe-lg-8{padding-right:32px!important}.v-application--is-rtl .pe-lg-8{padding-left:32px!important}.v-application--is-ltr .pe-lg-9{padding-right:36px!important}.v-application--is-rtl .pe-lg-9{padding-left:36px!important}.v-application--is-ltr .pe-lg-10{padding-right:40px!important}.v-application--is-rtl .pe-lg-10{padding-left:40px!important}.v-application--is-ltr .pe-lg-11{padding-right:44px!important}.v-application--is-rtl .pe-lg-11{padding-left:44px!important}.v-application--is-ltr .pe-lg-12{padding-right:48px!important}.v-application--is-rtl .pe-lg-12{padding-left:48px!important}.v-application--is-ltr .pe-lg-13{padding-right:52px!important}.v-application--is-rtl .pe-lg-13{padding-left:52px!important}.v-application--is-ltr .pe-lg-14{padding-right:56px!important}.v-application--is-rtl .pe-lg-14{padding-left:56px!important}.v-application--is-ltr .pe-lg-15{padding-right:60px!important}.v-application--is-rtl .pe-lg-15{padding-left:60px!important}.v-application--is-ltr .pe-lg-16{padding-right:64px!important}.v-application--is-rtl .pe-lg-16{padding-left:64px!important}.v-application .text-lg-left{text-align:left!important}.v-application .text-lg-right{text-align:right!important}.v-application .text-lg-center{text-align:center!important}.v-application .text-lg-justify{text-align:justify!important}.v-application .text-lg-start{text-align:start!important}.v-application .text-lg-end{text-align:end!important}.v-application .text-lg-h1{font-size:6rem!important;line-height:6rem;letter-spacing:-.015625em!important}.v-application .text-lg-h1,.v-application .text-lg-h2{font-weight:300;font-family:Roboto,sans-serif!important}.v-application .text-lg-h2{font-size:3.75rem!important;line-height:3.75rem;letter-spacing:-.0083333333em!important}.v-application .text-lg-h3{font-size:3rem!important;line-height:3.125rem;letter-spacing:normal!important}.v-application .text-lg-h3,.v-application .text-lg-h4{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .text-lg-h4{font-size:2.125rem!important;line-height:2.5rem;letter-spacing:.0073529412em!important}.v-application .text-lg-h5{font-size:1.5rem!important;font-weight:400;letter-spacing:normal!important}.v-application .text-lg-h5,.v-application .text-lg-h6{line-height:2rem;font-family:Roboto,sans-serif!important}.v-application .text-lg-h6{font-size:1.25rem!important;font-weight:500;letter-spacing:.0125em!important}.v-application .text-lg-subtitle-1{font-size:1rem!important;font-weight:400;line-height:1.75rem;letter-spacing:.009375em!important;font-family:Roboto,sans-serif!important}.v-application .text-lg-subtitle-2{font-size:.875rem!important;font-weight:500;line-height:1.375rem;letter-spacing:.0071428571em!important;font-family:Roboto,sans-serif!important}.v-application .text-lg-body-1{font-size:1rem!important;font-weight:400;line-height:1.5rem;letter-spacing:.03125em!important;font-family:Roboto,sans-serif!important}.v-application .text-lg-body-2{font-weight:400;line-height:1.25rem;letter-spacing:.0178571429em!important}.v-application .text-lg-body-2,.v-application .text-lg-button{font-size:.875rem!important;font-family:Roboto,sans-serif!important}.v-application .text-lg-button{font-weight:500;line-height:2.25rem;letter-spacing:.0892857143em!important;text-transform:uppercase!important}.v-application .text-lg-caption{font-weight:400;line-height:1.25rem;letter-spacing:.0333333333em!important}.v-application .text-lg-caption,.v-application .text-lg-overline{font-size:.75rem!important;font-family:Roboto,sans-serif!important}.v-application .text-lg-overline{font-weight:500;line-height:2rem;letter-spacing:.1666666667em!important;text-transform:uppercase!important}}@media(min-width:1904px){.v-application .d-xl-none{display:none!important}.v-application .d-xl-inline{display:inline!important}.v-application .d-xl-inline-block{display:inline-block!important}.v-application .d-xl-block{display:block!important}.v-application .d-xl-table{display:table!important}.v-application .d-xl-table-row{display:table-row!important}.v-application .d-xl-table-cell{display:table-cell!important}.v-application .d-xl-flex{display:flex!important}.v-application .d-xl-inline-flex{display:inline-flex!important}.v-application .float-xl-none{float:none!important}.v-application .float-xl-left{float:left!important}.v-application .float-xl-right{float:right!important}.v-application .flex-xl-fill{flex:1 1 auto!important}.v-application .flex-xl-row{flex-direction:row!important}.v-application .flex-xl-column{flex-direction:column!important}.v-application .flex-xl-row-reverse{flex-direction:row-reverse!important}.v-application .flex-xl-column-reverse{flex-direction:column-reverse!important}.v-application .flex-xl-grow-0{flex-grow:0!important}.v-application .flex-xl-grow-1{flex-grow:1!important}.v-application .flex-xl-shrink-0{flex-shrink:0!important}.v-application .flex-xl-shrink-1{flex-shrink:1!important}.v-application .flex-xl-wrap{flex-wrap:wrap!important}.v-application .flex-xl-nowrap{flex-wrap:nowrap!important}.v-application .flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.v-application .justify-xl-start{justify-content:flex-start!important}.v-application .justify-xl-end{justify-content:flex-end!important}.v-application .justify-xl-center{justify-content:center!important}.v-application .justify-xl-space-between{justify-content:space-between!important}.v-application .justify-xl-space-around{justify-content:space-around!important}.v-application .align-xl-start{align-items:flex-start!important}.v-application .align-xl-end{align-items:flex-end!important}.v-application .align-xl-center{align-items:center!important}.v-application .align-xl-baseline{align-items:baseline!important}.v-application .align-xl-stretch{align-items:stretch!important}.v-application .align-content-xl-start{align-content:flex-start!important}.v-application .align-content-xl-end{align-content:flex-end!important}.v-application .align-content-xl-center{align-content:center!important}.v-application .align-content-xl-space-between{align-content:space-between!important}.v-application .align-content-xl-space-around{align-content:space-around!important}.v-application .align-content-xl-stretch{align-content:stretch!important}.v-application .align-self-xl-auto{align-self:auto!important}.v-application .align-self-xl-start{align-self:flex-start!important}.v-application .align-self-xl-end{align-self:flex-end!important}.v-application .align-self-xl-center{align-self:center!important}.v-application .align-self-xl-baseline{align-self:baseline!important}.v-application .align-self-xl-stretch{align-self:stretch!important}.v-application .order-xl-first{order:-1!important}.v-application .order-xl-0{order:0!important}.v-application .order-xl-1{order:1!important}.v-application .order-xl-2{order:2!important}.v-application .order-xl-3{order:3!important}.v-application .order-xl-4{order:4!important}.v-application .order-xl-5{order:5!important}.v-application .order-xl-6{order:6!important}.v-application .order-xl-7{order:7!important}.v-application .order-xl-8{order:8!important}.v-application .order-xl-9{order:9!important}.v-application .order-xl-10{order:10!important}.v-application .order-xl-11{order:11!important}.v-application .order-xl-12{order:12!important}.v-application .order-xl-last{order:13!important}.v-application .ma-xl-0{margin:0!important}.v-application .ma-xl-1{margin:4px!important}.v-application .ma-xl-2{margin:8px!important}.v-application .ma-xl-3{margin:12px!important}.v-application .ma-xl-4{margin:16px!important}.v-application .ma-xl-5{margin:20px!important}.v-application .ma-xl-6{margin:24px!important}.v-application .ma-xl-7{margin:28px!important}.v-application .ma-xl-8{margin:32px!important}.v-application .ma-xl-9{margin:36px!important}.v-application .ma-xl-10{margin:40px!important}.v-application .ma-xl-11{margin:44px!important}.v-application .ma-xl-12{margin:48px!important}.v-application .ma-xl-13{margin:52px!important}.v-application .ma-xl-14{margin:56px!important}.v-application .ma-xl-15{margin:60px!important}.v-application .ma-xl-16{margin:64px!important}.v-application .ma-xl-auto{margin:auto!important}.v-application .mx-xl-0{margin-right:0!important;margin-left:0!important}.v-application .mx-xl-1{margin-right:4px!important;margin-left:4px!important}.v-application .mx-xl-2{margin-right:8px!important;margin-left:8px!important}.v-application .mx-xl-3{margin-right:12px!important;margin-left:12px!important}.v-application .mx-xl-4{margin-right:16px!important;margin-left:16px!important}.v-application .mx-xl-5{margin-right:20px!important;margin-left:20px!important}.v-application .mx-xl-6{margin-right:24px!important;margin-left:24px!important}.v-application .mx-xl-7{margin-right:28px!important;margin-left:28px!important}.v-application .mx-xl-8{margin-right:32px!important;margin-left:32px!important}.v-application .mx-xl-9{margin-right:36px!important;margin-left:36px!important}.v-application .mx-xl-10{margin-right:40px!important;margin-left:40px!important}.v-application .mx-xl-11{margin-right:44px!important;margin-left:44px!important}.v-application .mx-xl-12{margin-right:48px!important;margin-left:48px!important}.v-application .mx-xl-13{margin-right:52px!important;margin-left:52px!important}.v-application .mx-xl-14{margin-right:56px!important;margin-left:56px!important}.v-application .mx-xl-15{margin-right:60px!important;margin-left:60px!important}.v-application .mx-xl-16{margin-right:64px!important;margin-left:64px!important}.v-application .mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.v-application .my-xl-0{margin-top:0!important;margin-bottom:0!important}.v-application .my-xl-1{margin-top:4px!important;margin-bottom:4px!important}.v-application .my-xl-2{margin-top:8px!important;margin-bottom:8px!important}.v-application .my-xl-3{margin-top:12px!important;margin-bottom:12px!important}.v-application .my-xl-4{margin-top:16px!important;margin-bottom:16px!important}.v-application .my-xl-5{margin-top:20px!important;margin-bottom:20px!important}.v-application .my-xl-6{margin-top:24px!important;margin-bottom:24px!important}.v-application .my-xl-7{margin-top:28px!important;margin-bottom:28px!important}.v-application .my-xl-8{margin-top:32px!important;margin-bottom:32px!important}.v-application .my-xl-9{margin-top:36px!important;margin-bottom:36px!important}.v-application .my-xl-10{margin-top:40px!important;margin-bottom:40px!important}.v-application .my-xl-11{margin-top:44px!important;margin-bottom:44px!important}.v-application .my-xl-12{margin-top:48px!important;margin-bottom:48px!important}.v-application .my-xl-13{margin-top:52px!important;margin-bottom:52px!important}.v-application .my-xl-14{margin-top:56px!important;margin-bottom:56px!important}.v-application .my-xl-15{margin-top:60px!important;margin-bottom:60px!important}.v-application .my-xl-16{margin-top:64px!important;margin-bottom:64px!important}.v-application .my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.v-application .mt-xl-0{margin-top:0!important}.v-application .mt-xl-1{margin-top:4px!important}.v-application .mt-xl-2{margin-top:8px!important}.v-application .mt-xl-3{margin-top:12px!important}.v-application .mt-xl-4{margin-top:16px!important}.v-application .mt-xl-5{margin-top:20px!important}.v-application .mt-xl-6{margin-top:24px!important}.v-application .mt-xl-7{margin-top:28px!important}.v-application .mt-xl-8{margin-top:32px!important}.v-application .mt-xl-9{margin-top:36px!important}.v-application .mt-xl-10{margin-top:40px!important}.v-application .mt-xl-11{margin-top:44px!important}.v-application .mt-xl-12{margin-top:48px!important}.v-application .mt-xl-13{margin-top:52px!important}.v-application .mt-xl-14{margin-top:56px!important}.v-application .mt-xl-15{margin-top:60px!important}.v-application .mt-xl-16{margin-top:64px!important}.v-application .mt-xl-auto{margin-top:auto!important}.v-application .mr-xl-0{margin-right:0!important}.v-application .mr-xl-1{margin-right:4px!important}.v-application .mr-xl-2{margin-right:8px!important}.v-application .mr-xl-3{margin-right:12px!important}.v-application .mr-xl-4{margin-right:16px!important}.v-application .mr-xl-5{margin-right:20px!important}.v-application .mr-xl-6{margin-right:24px!important}.v-application .mr-xl-7{margin-right:28px!important}.v-application .mr-xl-8{margin-right:32px!important}.v-application .mr-xl-9{margin-right:36px!important}.v-application .mr-xl-10{margin-right:40px!important}.v-application .mr-xl-11{margin-right:44px!important}.v-application .mr-xl-12{margin-right:48px!important}.v-application .mr-xl-13{margin-right:52px!important}.v-application .mr-xl-14{margin-right:56px!important}.v-application .mr-xl-15{margin-right:60px!important}.v-application .mr-xl-16{margin-right:64px!important}.v-application .mr-xl-auto{margin-right:auto!important}.v-application .mb-xl-0{margin-bottom:0!important}.v-application .mb-xl-1{margin-bottom:4px!important}.v-application .mb-xl-2{margin-bottom:8px!important}.v-application .mb-xl-3{margin-bottom:12px!important}.v-application .mb-xl-4{margin-bottom:16px!important}.v-application .mb-xl-5{margin-bottom:20px!important}.v-application .mb-xl-6{margin-bottom:24px!important}.v-application .mb-xl-7{margin-bottom:28px!important}.v-application .mb-xl-8{margin-bottom:32px!important}.v-application .mb-xl-9{margin-bottom:36px!important}.v-application .mb-xl-10{margin-bottom:40px!important}.v-application .mb-xl-11{margin-bottom:44px!important}.v-application .mb-xl-12{margin-bottom:48px!important}.v-application .mb-xl-13{margin-bottom:52px!important}.v-application .mb-xl-14{margin-bottom:56px!important}.v-application .mb-xl-15{margin-bottom:60px!important}.v-application .mb-xl-16{margin-bottom:64px!important}.v-application .mb-xl-auto{margin-bottom:auto!important}.v-application .ml-xl-0{margin-left:0!important}.v-application .ml-xl-1{margin-left:4px!important}.v-application .ml-xl-2{margin-left:8px!important}.v-application .ml-xl-3{margin-left:12px!important}.v-application .ml-xl-4{margin-left:16px!important}.v-application .ml-xl-5{margin-left:20px!important}.v-application .ml-xl-6{margin-left:24px!important}.v-application .ml-xl-7{margin-left:28px!important}.v-application .ml-xl-8{margin-left:32px!important}.v-application .ml-xl-9{margin-left:36px!important}.v-application .ml-xl-10{margin-left:40px!important}.v-application .ml-xl-11{margin-left:44px!important}.v-application .ml-xl-12{margin-left:48px!important}.v-application .ml-xl-13{margin-left:52px!important}.v-application .ml-xl-14{margin-left:56px!important}.v-application .ml-xl-15{margin-left:60px!important}.v-application .ml-xl-16{margin-left:64px!important}.v-application .ml-xl-auto{margin-left:auto!important}.v-application--is-ltr .ms-xl-0{margin-left:0!important}.v-application--is-rtl .ms-xl-0{margin-right:0!important}.v-application--is-ltr .ms-xl-1{margin-left:4px!important}.v-application--is-rtl .ms-xl-1{margin-right:4px!important}.v-application--is-ltr .ms-xl-2{margin-left:8px!important}.v-application--is-rtl .ms-xl-2{margin-right:8px!important}.v-application--is-ltr .ms-xl-3{margin-left:12px!important}.v-application--is-rtl .ms-xl-3{margin-right:12px!important}.v-application--is-ltr .ms-xl-4{margin-left:16px!important}.v-application--is-rtl .ms-xl-4{margin-right:16px!important}.v-application--is-ltr .ms-xl-5{margin-left:20px!important}.v-application--is-rtl .ms-xl-5{margin-right:20px!important}.v-application--is-ltr .ms-xl-6{margin-left:24px!important}.v-application--is-rtl .ms-xl-6{margin-right:24px!important}.v-application--is-ltr .ms-xl-7{margin-left:28px!important}.v-application--is-rtl .ms-xl-7{margin-right:28px!important}.v-application--is-ltr .ms-xl-8{margin-left:32px!important}.v-application--is-rtl .ms-xl-8{margin-right:32px!important}.v-application--is-ltr .ms-xl-9{margin-left:36px!important}.v-application--is-rtl .ms-xl-9{margin-right:36px!important}.v-application--is-ltr .ms-xl-10{margin-left:40px!important}.v-application--is-rtl .ms-xl-10{margin-right:40px!important}.v-application--is-ltr .ms-xl-11{margin-left:44px!important}.v-application--is-rtl .ms-xl-11{margin-right:44px!important}.v-application--is-ltr .ms-xl-12{margin-left:48px!important}.v-application--is-rtl .ms-xl-12{margin-right:48px!important}.v-application--is-ltr .ms-xl-13{margin-left:52px!important}.v-application--is-rtl .ms-xl-13{margin-right:52px!important}.v-application--is-ltr .ms-xl-14{margin-left:56px!important}.v-application--is-rtl .ms-xl-14{margin-right:56px!important}.v-application--is-ltr .ms-xl-15{margin-left:60px!important}.v-application--is-rtl .ms-xl-15{margin-right:60px!important}.v-application--is-ltr .ms-xl-16{margin-left:64px!important}.v-application--is-rtl .ms-xl-16{margin-right:64px!important}.v-application--is-ltr .ms-xl-auto{margin-left:auto!important}.v-application--is-rtl .ms-xl-auto{margin-right:auto!important}.v-application--is-ltr .me-xl-0{margin-right:0!important}.v-application--is-rtl .me-xl-0{margin-left:0!important}.v-application--is-ltr .me-xl-1{margin-right:4px!important}.v-application--is-rtl .me-xl-1{margin-left:4px!important}.v-application--is-ltr .me-xl-2{margin-right:8px!important}.v-application--is-rtl .me-xl-2{margin-left:8px!important}.v-application--is-ltr .me-xl-3{margin-right:12px!important}.v-application--is-rtl .me-xl-3{margin-left:12px!important}.v-application--is-ltr .me-xl-4{margin-right:16px!important}.v-application--is-rtl .me-xl-4{margin-left:16px!important}.v-application--is-ltr .me-xl-5{margin-right:20px!important}.v-application--is-rtl .me-xl-5{margin-left:20px!important}.v-application--is-ltr .me-xl-6{margin-right:24px!important}.v-application--is-rtl .me-xl-6{margin-left:24px!important}.v-application--is-ltr .me-xl-7{margin-right:28px!important}.v-application--is-rtl .me-xl-7{margin-left:28px!important}.v-application--is-ltr .me-xl-8{margin-right:32px!important}.v-application--is-rtl .me-xl-8{margin-left:32px!important}.v-application--is-ltr .me-xl-9{margin-right:36px!important}.v-application--is-rtl .me-xl-9{margin-left:36px!important}.v-application--is-ltr .me-xl-10{margin-right:40px!important}.v-application--is-rtl .me-xl-10{margin-left:40px!important}.v-application--is-ltr .me-xl-11{margin-right:44px!important}.v-application--is-rtl .me-xl-11{margin-left:44px!important}.v-application--is-ltr .me-xl-12{margin-right:48px!important}.v-application--is-rtl .me-xl-12{margin-left:48px!important}.v-application--is-ltr .me-xl-13{margin-right:52px!important}.v-application--is-rtl .me-xl-13{margin-left:52px!important}.v-application--is-ltr .me-xl-14{margin-right:56px!important}.v-application--is-rtl .me-xl-14{margin-left:56px!important}.v-application--is-ltr .me-xl-15{margin-right:60px!important}.v-application--is-rtl .me-xl-15{margin-left:60px!important}.v-application--is-ltr .me-xl-16{margin-right:64px!important}.v-application--is-rtl .me-xl-16{margin-left:64px!important}.v-application--is-ltr .me-xl-auto{margin-right:auto!important}.v-application--is-rtl .me-xl-auto{margin-left:auto!important}.v-application .ma-xl-n1{margin:-4px!important}.v-application .ma-xl-n2{margin:-8px!important}.v-application .ma-xl-n3{margin:-12px!important}.v-application .ma-xl-n4{margin:-16px!important}.v-application .ma-xl-n5{margin:-20px!important}.v-application .ma-xl-n6{margin:-24px!important}.v-application .ma-xl-n7{margin:-28px!important}.v-application .ma-xl-n8{margin:-32px!important}.v-application .ma-xl-n9{margin:-36px!important}.v-application .ma-xl-n10{margin:-40px!important}.v-application .ma-xl-n11{margin:-44px!important}.v-application .ma-xl-n12{margin:-48px!important}.v-application .ma-xl-n13{margin:-52px!important}.v-application .ma-xl-n14{margin:-56px!important}.v-application .ma-xl-n15{margin:-60px!important}.v-application .ma-xl-n16{margin:-64px!important}.v-application .mx-xl-n1{margin-right:-4px!important;margin-left:-4px!important}.v-application .mx-xl-n2{margin-right:-8px!important;margin-left:-8px!important}.v-application .mx-xl-n3{margin-right:-12px!important;margin-left:-12px!important}.v-application .mx-xl-n4{margin-right:-16px!important;margin-left:-16px!important}.v-application .mx-xl-n5{margin-right:-20px!important;margin-left:-20px!important}.v-application .mx-xl-n6{margin-right:-24px!important;margin-left:-24px!important}.v-application .mx-xl-n7{margin-right:-28px!important;margin-left:-28px!important}.v-application .mx-xl-n8{margin-right:-32px!important;margin-left:-32px!important}.v-application .mx-xl-n9{margin-right:-36px!important;margin-left:-36px!important}.v-application .mx-xl-n10{margin-right:-40px!important;margin-left:-40px!important}.v-application .mx-xl-n11{margin-right:-44px!important;margin-left:-44px!important}.v-application .mx-xl-n12{margin-right:-48px!important;margin-left:-48px!important}.v-application .mx-xl-n13{margin-right:-52px!important;margin-left:-52px!important}.v-application .mx-xl-n14{margin-right:-56px!important;margin-left:-56px!important}.v-application .mx-xl-n15{margin-right:-60px!important;margin-left:-60px!important}.v-application .mx-xl-n16{margin-right:-64px!important;margin-left:-64px!important}.v-application .my-xl-n1{margin-top:-4px!important;margin-bottom:-4px!important}.v-application .my-xl-n2{margin-top:-8px!important;margin-bottom:-8px!important}.v-application .my-xl-n3{margin-top:-12px!important;margin-bottom:-12px!important}.v-application .my-xl-n4{margin-top:-16px!important;margin-bottom:-16px!important}.v-application .my-xl-n5{margin-top:-20px!important;margin-bottom:-20px!important}.v-application .my-xl-n6{margin-top:-24px!important;margin-bottom:-24px!important}.v-application .my-xl-n7{margin-top:-28px!important;margin-bottom:-28px!important}.v-application .my-xl-n8{margin-top:-32px!important;margin-bottom:-32px!important}.v-application .my-xl-n9{margin-top:-36px!important;margin-bottom:-36px!important}.v-application .my-xl-n10{margin-top:-40px!important;margin-bottom:-40px!important}.v-application .my-xl-n11{margin-top:-44px!important;margin-bottom:-44px!important}.v-application .my-xl-n12{margin-top:-48px!important;margin-bottom:-48px!important}.v-application .my-xl-n13{margin-top:-52px!important;margin-bottom:-52px!important}.v-application .my-xl-n14{margin-top:-56px!important;margin-bottom:-56px!important}.v-application .my-xl-n15{margin-top:-60px!important;margin-bottom:-60px!important}.v-application .my-xl-n16{margin-top:-64px!important;margin-bottom:-64px!important}.v-application .mt-xl-n1{margin-top:-4px!important}.v-application .mt-xl-n2{margin-top:-8px!important}.v-application .mt-xl-n3{margin-top:-12px!important}.v-application .mt-xl-n4{margin-top:-16px!important}.v-application .mt-xl-n5{margin-top:-20px!important}.v-application .mt-xl-n6{margin-top:-24px!important}.v-application .mt-xl-n7{margin-top:-28px!important}.v-application .mt-xl-n8{margin-top:-32px!important}.v-application .mt-xl-n9{margin-top:-36px!important}.v-application .mt-xl-n10{margin-top:-40px!important}.v-application .mt-xl-n11{margin-top:-44px!important}.v-application .mt-xl-n12{margin-top:-48px!important}.v-application .mt-xl-n13{margin-top:-52px!important}.v-application .mt-xl-n14{margin-top:-56px!important}.v-application .mt-xl-n15{margin-top:-60px!important}.v-application .mt-xl-n16{margin-top:-64px!important}.v-application .mr-xl-n1{margin-right:-4px!important}.v-application .mr-xl-n2{margin-right:-8px!important}.v-application .mr-xl-n3{margin-right:-12px!important}.v-application .mr-xl-n4{margin-right:-16px!important}.v-application .mr-xl-n5{margin-right:-20px!important}.v-application .mr-xl-n6{margin-right:-24px!important}.v-application .mr-xl-n7{margin-right:-28px!important}.v-application .mr-xl-n8{margin-right:-32px!important}.v-application .mr-xl-n9{margin-right:-36px!important}.v-application .mr-xl-n10{margin-right:-40px!important}.v-application .mr-xl-n11{margin-right:-44px!important}.v-application .mr-xl-n12{margin-right:-48px!important}.v-application .mr-xl-n13{margin-right:-52px!important}.v-application .mr-xl-n14{margin-right:-56px!important}.v-application .mr-xl-n15{margin-right:-60px!important}.v-application .mr-xl-n16{margin-right:-64px!important}.v-application .mb-xl-n1{margin-bottom:-4px!important}.v-application .mb-xl-n2{margin-bottom:-8px!important}.v-application .mb-xl-n3{margin-bottom:-12px!important}.v-application .mb-xl-n4{margin-bottom:-16px!important}.v-application .mb-xl-n5{margin-bottom:-20px!important}.v-application .mb-xl-n6{margin-bottom:-24px!important}.v-application .mb-xl-n7{margin-bottom:-28px!important}.v-application .mb-xl-n8{margin-bottom:-32px!important}.v-application .mb-xl-n9{margin-bottom:-36px!important}.v-application .mb-xl-n10{margin-bottom:-40px!important}.v-application .mb-xl-n11{margin-bottom:-44px!important}.v-application .mb-xl-n12{margin-bottom:-48px!important}.v-application .mb-xl-n13{margin-bottom:-52px!important}.v-application .mb-xl-n14{margin-bottom:-56px!important}.v-application .mb-xl-n15{margin-bottom:-60px!important}.v-application .mb-xl-n16{margin-bottom:-64px!important}.v-application .ml-xl-n1{margin-left:-4px!important}.v-application .ml-xl-n2{margin-left:-8px!important}.v-application .ml-xl-n3{margin-left:-12px!important}.v-application .ml-xl-n4{margin-left:-16px!important}.v-application .ml-xl-n5{margin-left:-20px!important}.v-application .ml-xl-n6{margin-left:-24px!important}.v-application .ml-xl-n7{margin-left:-28px!important}.v-application .ml-xl-n8{margin-left:-32px!important}.v-application .ml-xl-n9{margin-left:-36px!important}.v-application .ml-xl-n10{margin-left:-40px!important}.v-application .ml-xl-n11{margin-left:-44px!important}.v-application .ml-xl-n12{margin-left:-48px!important}.v-application .ml-xl-n13{margin-left:-52px!important}.v-application .ml-xl-n14{margin-left:-56px!important}.v-application .ml-xl-n15{margin-left:-60px!important}.v-application .ml-xl-n16{margin-left:-64px!important}.v-application--is-ltr .ms-xl-n1{margin-left:-4px!important}.v-application--is-rtl .ms-xl-n1{margin-right:-4px!important}.v-application--is-ltr .ms-xl-n2{margin-left:-8px!important}.v-application--is-rtl .ms-xl-n2{margin-right:-8px!important}.v-application--is-ltr .ms-xl-n3{margin-left:-12px!important}.v-application--is-rtl .ms-xl-n3{margin-right:-12px!important}.v-application--is-ltr .ms-xl-n4{margin-left:-16px!important}.v-application--is-rtl .ms-xl-n4{margin-right:-16px!important}.v-application--is-ltr .ms-xl-n5{margin-left:-20px!important}.v-application--is-rtl .ms-xl-n5{margin-right:-20px!important}.v-application--is-ltr .ms-xl-n6{margin-left:-24px!important}.v-application--is-rtl .ms-xl-n6{margin-right:-24px!important}.v-application--is-ltr .ms-xl-n7{margin-left:-28px!important}.v-application--is-rtl .ms-xl-n7{margin-right:-28px!important}.v-application--is-ltr .ms-xl-n8{margin-left:-32px!important}.v-application--is-rtl .ms-xl-n8{margin-right:-32px!important}.v-application--is-ltr .ms-xl-n9{margin-left:-36px!important}.v-application--is-rtl .ms-xl-n9{margin-right:-36px!important}.v-application--is-ltr .ms-xl-n10{margin-left:-40px!important}.v-application--is-rtl .ms-xl-n10{margin-right:-40px!important}.v-application--is-ltr .ms-xl-n11{margin-left:-44px!important}.v-application--is-rtl .ms-xl-n11{margin-right:-44px!important}.v-application--is-ltr .ms-xl-n12{margin-left:-48px!important}.v-application--is-rtl .ms-xl-n12{margin-right:-48px!important}.v-application--is-ltr .ms-xl-n13{margin-left:-52px!important}.v-application--is-rtl .ms-xl-n13{margin-right:-52px!important}.v-application--is-ltr .ms-xl-n14{margin-left:-56px!important}.v-application--is-rtl .ms-xl-n14{margin-right:-56px!important}.v-application--is-ltr .ms-xl-n15{margin-left:-60px!important}.v-application--is-rtl .ms-xl-n15{margin-right:-60px!important}.v-application--is-ltr .ms-xl-n16{margin-left:-64px!important}.v-application--is-rtl .ms-xl-n16{margin-right:-64px!important}.v-application--is-ltr .me-xl-n1{margin-right:-4px!important}.v-application--is-rtl .me-xl-n1{margin-left:-4px!important}.v-application--is-ltr .me-xl-n2{margin-right:-8px!important}.v-application--is-rtl .me-xl-n2{margin-left:-8px!important}.v-application--is-ltr .me-xl-n3{margin-right:-12px!important}.v-application--is-rtl .me-xl-n3{margin-left:-12px!important}.v-application--is-ltr .me-xl-n4{margin-right:-16px!important}.v-application--is-rtl .me-xl-n4{margin-left:-16px!important}.v-application--is-ltr .me-xl-n5{margin-right:-20px!important}.v-application--is-rtl .me-xl-n5{margin-left:-20px!important}.v-application--is-ltr .me-xl-n6{margin-right:-24px!important}.v-application--is-rtl .me-xl-n6{margin-left:-24px!important}.v-application--is-ltr .me-xl-n7{margin-right:-28px!important}.v-application--is-rtl .me-xl-n7{margin-left:-28px!important}.v-application--is-ltr .me-xl-n8{margin-right:-32px!important}.v-application--is-rtl .me-xl-n8{margin-left:-32px!important}.v-application--is-ltr .me-xl-n9{margin-right:-36px!important}.v-application--is-rtl .me-xl-n9{margin-left:-36px!important}.v-application--is-ltr .me-xl-n10{margin-right:-40px!important}.v-application--is-rtl .me-xl-n10{margin-left:-40px!important}.v-application--is-ltr .me-xl-n11{margin-right:-44px!important}.v-application--is-rtl .me-xl-n11{margin-left:-44px!important}.v-application--is-ltr .me-xl-n12{margin-right:-48px!important}.v-application--is-rtl .me-xl-n12{margin-left:-48px!important}.v-application--is-ltr .me-xl-n13{margin-right:-52px!important}.v-application--is-rtl .me-xl-n13{margin-left:-52px!important}.v-application--is-ltr .me-xl-n14{margin-right:-56px!important}.v-application--is-rtl .me-xl-n14{margin-left:-56px!important}.v-application--is-ltr .me-xl-n15{margin-right:-60px!important}.v-application--is-rtl .me-xl-n15{margin-left:-60px!important}.v-application--is-ltr .me-xl-n16{margin-right:-64px!important}.v-application--is-rtl .me-xl-n16{margin-left:-64px!important}.v-application .pa-xl-0{padding:0!important}.v-application .pa-xl-1{padding:4px!important}.v-application .pa-xl-2{padding:8px!important}.v-application .pa-xl-3{padding:12px!important}.v-application .pa-xl-4{padding:16px!important}.v-application .pa-xl-5{padding:20px!important}.v-application .pa-xl-6{padding:24px!important}.v-application .pa-xl-7{padding:28px!important}.v-application .pa-xl-8{padding:32px!important}.v-application .pa-xl-9{padding:36px!important}.v-application .pa-xl-10{padding:40px!important}.v-application .pa-xl-11{padding:44px!important}.v-application .pa-xl-12{padding:48px!important}.v-application .pa-xl-13{padding:52px!important}.v-application .pa-xl-14{padding:56px!important}.v-application .pa-xl-15{padding:60px!important}.v-application .pa-xl-16{padding:64px!important}.v-application .px-xl-0{padding-right:0!important;padding-left:0!important}.v-application .px-xl-1{padding-right:4px!important;padding-left:4px!important}.v-application .px-xl-2{padding-right:8px!important;padding-left:8px!important}.v-application .px-xl-3{padding-right:12px!important;padding-left:12px!important}.v-application .px-xl-4{padding-right:16px!important;padding-left:16px!important}.v-application .px-xl-5{padding-right:20px!important;padding-left:20px!important}.v-application .px-xl-6{padding-right:24px!important;padding-left:24px!important}.v-application .px-xl-7{padding-right:28px!important;padding-left:28px!important}.v-application .px-xl-8{padding-right:32px!important;padding-left:32px!important}.v-application .px-xl-9{padding-right:36px!important;padding-left:36px!important}.v-application .px-xl-10{padding-right:40px!important;padding-left:40px!important}.v-application .px-xl-11{padding-right:44px!important;padding-left:44px!important}.v-application .px-xl-12{padding-right:48px!important;padding-left:48px!important}.v-application .px-xl-13{padding-right:52px!important;padding-left:52px!important}.v-application .px-xl-14{padding-right:56px!important;padding-left:56px!important}.v-application .px-xl-15{padding-right:60px!important;padding-left:60px!important}.v-application .px-xl-16{padding-right:64px!important;padding-left:64px!important}.v-application .py-xl-0{padding-top:0!important;padding-bottom:0!important}.v-application .py-xl-1{padding-top:4px!important;padding-bottom:4px!important}.v-application .py-xl-2{padding-top:8px!important;padding-bottom:8px!important}.v-application .py-xl-3{padding-top:12px!important;padding-bottom:12px!important}.v-application .py-xl-4{padding-top:16px!important;padding-bottom:16px!important}.v-application .py-xl-5{padding-top:20px!important;padding-bottom:20px!important}.v-application .py-xl-6{padding-top:24px!important;padding-bottom:24px!important}.v-application .py-xl-7{padding-top:28px!important;padding-bottom:28px!important}.v-application .py-xl-8{padding-top:32px!important;padding-bottom:32px!important}.v-application .py-xl-9{padding-top:36px!important;padding-bottom:36px!important}.v-application .py-xl-10{padding-top:40px!important;padding-bottom:40px!important}.v-application .py-xl-11{padding-top:44px!important;padding-bottom:44px!important}.v-application .py-xl-12{padding-top:48px!important;padding-bottom:48px!important}.v-application .py-xl-13{padding-top:52px!important;padding-bottom:52px!important}.v-application .py-xl-14{padding-top:56px!important;padding-bottom:56px!important}.v-application .py-xl-15{padding-top:60px!important;padding-bottom:60px!important}.v-application .py-xl-16{padding-top:64px!important;padding-bottom:64px!important}.v-application .pt-xl-0{padding-top:0!important}.v-application .pt-xl-1{padding-top:4px!important}.v-application .pt-xl-2{padding-top:8px!important}.v-application .pt-xl-3{padding-top:12px!important}.v-application .pt-xl-4{padding-top:16px!important}.v-application .pt-xl-5{padding-top:20px!important}.v-application .pt-xl-6{padding-top:24px!important}.v-application .pt-xl-7{padding-top:28px!important}.v-application .pt-xl-8{padding-top:32px!important}.v-application .pt-xl-9{padding-top:36px!important}.v-application .pt-xl-10{padding-top:40px!important}.v-application .pt-xl-11{padding-top:44px!important}.v-application .pt-xl-12{padding-top:48px!important}.v-application .pt-xl-13{padding-top:52px!important}.v-application .pt-xl-14{padding-top:56px!important}.v-application .pt-xl-15{padding-top:60px!important}.v-application .pt-xl-16{padding-top:64px!important}.v-application .pr-xl-0{padding-right:0!important}.v-application .pr-xl-1{padding-right:4px!important}.v-application .pr-xl-2{padding-right:8px!important}.v-application .pr-xl-3{padding-right:12px!important}.v-application .pr-xl-4{padding-right:16px!important}.v-application .pr-xl-5{padding-right:20px!important}.v-application .pr-xl-6{padding-right:24px!important}.v-application .pr-xl-7{padding-right:28px!important}.v-application .pr-xl-8{padding-right:32px!important}.v-application .pr-xl-9{padding-right:36px!important}.v-application .pr-xl-10{padding-right:40px!important}.v-application .pr-xl-11{padding-right:44px!important}.v-application .pr-xl-12{padding-right:48px!important}.v-application .pr-xl-13{padding-right:52px!important}.v-application .pr-xl-14{padding-right:56px!important}.v-application .pr-xl-15{padding-right:60px!important}.v-application .pr-xl-16{padding-right:64px!important}.v-application .pb-xl-0{padding-bottom:0!important}.v-application .pb-xl-1{padding-bottom:4px!important}.v-application .pb-xl-2{padding-bottom:8px!important}.v-application .pb-xl-3{padding-bottom:12px!important}.v-application .pb-xl-4{padding-bottom:16px!important}.v-application .pb-xl-5{padding-bottom:20px!important}.v-application .pb-xl-6{padding-bottom:24px!important}.v-application .pb-xl-7{padding-bottom:28px!important}.v-application .pb-xl-8{padding-bottom:32px!important}.v-application .pb-xl-9{padding-bottom:36px!important}.v-application .pb-xl-10{padding-bottom:40px!important}.v-application .pb-xl-11{padding-bottom:44px!important}.v-application .pb-xl-12{padding-bottom:48px!important}.v-application .pb-xl-13{padding-bottom:52px!important}.v-application .pb-xl-14{padding-bottom:56px!important}.v-application .pb-xl-15{padding-bottom:60px!important}.v-application .pb-xl-16{padding-bottom:64px!important}.v-application .pl-xl-0{padding-left:0!important}.v-application .pl-xl-1{padding-left:4px!important}.v-application .pl-xl-2{padding-left:8px!important}.v-application .pl-xl-3{padding-left:12px!important}.v-application .pl-xl-4{padding-left:16px!important}.v-application .pl-xl-5{padding-left:20px!important}.v-application .pl-xl-6{padding-left:24px!important}.v-application .pl-xl-7{padding-left:28px!important}.v-application .pl-xl-8{padding-left:32px!important}.v-application .pl-xl-9{padding-left:36px!important}.v-application .pl-xl-10{padding-left:40px!important}.v-application .pl-xl-11{padding-left:44px!important}.v-application .pl-xl-12{padding-left:48px!important}.v-application .pl-xl-13{padding-left:52px!important}.v-application .pl-xl-14{padding-left:56px!important}.v-application .pl-xl-15{padding-left:60px!important}.v-application .pl-xl-16{padding-left:64px!important}.v-application--is-ltr .ps-xl-0{padding-left:0!important}.v-application--is-rtl .ps-xl-0{padding-right:0!important}.v-application--is-ltr .ps-xl-1{padding-left:4px!important}.v-application--is-rtl .ps-xl-1{padding-right:4px!important}.v-application--is-ltr .ps-xl-2{padding-left:8px!important}.v-application--is-rtl .ps-xl-2{padding-right:8px!important}.v-application--is-ltr .ps-xl-3{padding-left:12px!important}.v-application--is-rtl .ps-xl-3{padding-right:12px!important}.v-application--is-ltr .ps-xl-4{padding-left:16px!important}.v-application--is-rtl .ps-xl-4{padding-right:16px!important}.v-application--is-ltr .ps-xl-5{padding-left:20px!important}.v-application--is-rtl .ps-xl-5{padding-right:20px!important}.v-application--is-ltr .ps-xl-6{padding-left:24px!important}.v-application--is-rtl .ps-xl-6{padding-right:24px!important}.v-application--is-ltr .ps-xl-7{padding-left:28px!important}.v-application--is-rtl .ps-xl-7{padding-right:28px!important}.v-application--is-ltr .ps-xl-8{padding-left:32px!important}.v-application--is-rtl .ps-xl-8{padding-right:32px!important}.v-application--is-ltr .ps-xl-9{padding-left:36px!important}.v-application--is-rtl .ps-xl-9{padding-right:36px!important}.v-application--is-ltr .ps-xl-10{padding-left:40px!important}.v-application--is-rtl .ps-xl-10{padding-right:40px!important}.v-application--is-ltr .ps-xl-11{padding-left:44px!important}.v-application--is-rtl .ps-xl-11{padding-right:44px!important}.v-application--is-ltr .ps-xl-12{padding-left:48px!important}.v-application--is-rtl .ps-xl-12{padding-right:48px!important}.v-application--is-ltr .ps-xl-13{padding-left:52px!important}.v-application--is-rtl .ps-xl-13{padding-right:52px!important}.v-application--is-ltr .ps-xl-14{padding-left:56px!important}.v-application--is-rtl .ps-xl-14{padding-right:56px!important}.v-application--is-ltr .ps-xl-15{padding-left:60px!important}.v-application--is-rtl .ps-xl-15{padding-right:60px!important}.v-application--is-ltr .ps-xl-16{padding-left:64px!important}.v-application--is-rtl .ps-xl-16{padding-right:64px!important}.v-application--is-ltr .pe-xl-0{padding-right:0!important}.v-application--is-rtl .pe-xl-0{padding-left:0!important}.v-application--is-ltr .pe-xl-1{padding-right:4px!important}.v-application--is-rtl .pe-xl-1{padding-left:4px!important}.v-application--is-ltr .pe-xl-2{padding-right:8px!important}.v-application--is-rtl .pe-xl-2{padding-left:8px!important}.v-application--is-ltr .pe-xl-3{padding-right:12px!important}.v-application--is-rtl .pe-xl-3{padding-left:12px!important}.v-application--is-ltr .pe-xl-4{padding-right:16px!important}.v-application--is-rtl .pe-xl-4{padding-left:16px!important}.v-application--is-ltr .pe-xl-5{padding-right:20px!important}.v-application--is-rtl .pe-xl-5{padding-left:20px!important}.v-application--is-ltr .pe-xl-6{padding-right:24px!important}.v-application--is-rtl .pe-xl-6{padding-left:24px!important}.v-application--is-ltr .pe-xl-7{padding-right:28px!important}.v-application--is-rtl .pe-xl-7{padding-left:28px!important}.v-application--is-ltr .pe-xl-8{padding-right:32px!important}.v-application--is-rtl .pe-xl-8{padding-left:32px!important}.v-application--is-ltr .pe-xl-9{padding-right:36px!important}.v-application--is-rtl .pe-xl-9{padding-left:36px!important}.v-application--is-ltr .pe-xl-10{padding-right:40px!important}.v-application--is-rtl .pe-xl-10{padding-left:40px!important}.v-application--is-ltr .pe-xl-11{padding-right:44px!important}.v-application--is-rtl .pe-xl-11{padding-left:44px!important}.v-application--is-ltr .pe-xl-12{padding-right:48px!important}.v-application--is-rtl .pe-xl-12{padding-left:48px!important}.v-application--is-ltr .pe-xl-13{padding-right:52px!important}.v-application--is-rtl .pe-xl-13{padding-left:52px!important}.v-application--is-ltr .pe-xl-14{padding-right:56px!important}.v-application--is-rtl .pe-xl-14{padding-left:56px!important}.v-application--is-ltr .pe-xl-15{padding-right:60px!important}.v-application--is-rtl .pe-xl-15{padding-left:60px!important}.v-application--is-ltr .pe-xl-16{padding-right:64px!important}.v-application--is-rtl .pe-xl-16{padding-left:64px!important}.v-application .text-xl-left{text-align:left!important}.v-application .text-xl-right{text-align:right!important}.v-application .text-xl-center{text-align:center!important}.v-application .text-xl-justify{text-align:justify!important}.v-application .text-xl-start{text-align:start!important}.v-application .text-xl-end{text-align:end!important}.v-application .text-xl-h1{font-size:6rem!important;line-height:6rem;letter-spacing:-.015625em!important}.v-application .text-xl-h1,.v-application .text-xl-h2{font-weight:300;font-family:Roboto,sans-serif!important}.v-application .text-xl-h2{font-size:3.75rem!important;line-height:3.75rem;letter-spacing:-.0083333333em!important}.v-application .text-xl-h3{font-size:3rem!important;line-height:3.125rem;letter-spacing:normal!important}.v-application .text-xl-h3,.v-application .text-xl-h4{font-weight:400;font-family:Roboto,sans-serif!important}.v-application .text-xl-h4{font-size:2.125rem!important;line-height:2.5rem;letter-spacing:.0073529412em!important}.v-application .text-xl-h5{font-size:1.5rem!important;font-weight:400;letter-spacing:normal!important}.v-application .text-xl-h5,.v-application .text-xl-h6{line-height:2rem;font-family:Roboto,sans-serif!important}.v-application .text-xl-h6{font-size:1.25rem!important;font-weight:500;letter-spacing:.0125em!important}.v-application .text-xl-subtitle-1{font-size:1rem!important;font-weight:400;line-height:1.75rem;letter-spacing:.009375em!important;font-family:Roboto,sans-serif!important}.v-application .text-xl-subtitle-2{font-size:.875rem!important;font-weight:500;line-height:1.375rem;letter-spacing:.0071428571em!important;font-family:Roboto,sans-serif!important}.v-application .text-xl-body-1{font-size:1rem!important;font-weight:400;line-height:1.5rem;letter-spacing:.03125em!important;font-family:Roboto,sans-serif!important}.v-application .text-xl-body-2{font-weight:400;line-height:1.25rem;letter-spacing:.0178571429em!important}.v-application .text-xl-body-2,.v-application .text-xl-button{font-size:.875rem!important;font-family:Roboto,sans-serif!important}.v-application .text-xl-button{font-weight:500;line-height:2.25rem;letter-spacing:.0892857143em!important;text-transform:uppercase!important}.v-application .text-xl-caption{font-weight:400;line-height:1.25rem;letter-spacing:.0333333333em!important}.v-application .text-xl-caption,.v-application .text-xl-overline{font-size:.75rem!important;font-family:Roboto,sans-serif!important}.v-application .text-xl-overline{font-weight:500;line-height:2rem;letter-spacing:.1666666667em!important;text-transform:uppercase!important}}@media print{.v-application .d-print-none{display:none!important}.v-application .d-print-inline{display:inline!important}.v-application .d-print-inline-block{display:inline-block!important}.v-application .d-print-block{display:block!important}.v-application .d-print-table{display:table!important}.v-application .d-print-table-row{display:table-row!important}.v-application .d-print-table-cell{display:table-cell!important}.v-application .d-print-flex{display:flex!important}.v-application .d-print-inline-flex{display:inline-flex!important}.v-application .float-print-none{float:none!important}.v-application .float-print-left{float:left!important}.v-application .float-print-right{float:right!important}}.v-ripple__container{border-radius:inherit;width:100%;height:100%;z-index:0;contain:strict}.v-ripple__animation,.v-ripple__container{color:inherit;position:absolute;left:0;top:0;overflow:hidden;pointer-events:none}.v-ripple__animation{border-radius:50%;background:currentColor;opacity:0;will-change:transform,opacity}.v-ripple__animation--enter{transition:none}.v-ripple__animation--in{transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .1s cubic-bezier(.4,0,.2,1)}.v-ripple__animation--out{transition:opacity .3s cubic-bezier(.4,0,.2,1)}.v-progress-circular{position:relative;display:inline-flex;vertical-align:middle;justify-content:center;align-items:center}.v-progress-circular svg{width:100%;height:100%;margin:auto;position:absolute;top:0;bottom:0;left:0;right:0;z-index:0}.v-progress-circular--indeterminate svg{-webkit-animation:progress-circular-rotate 1.4s linear infinite;animation:progress-circular-rotate 1.4s linear infinite;transform-origin:center center;transition:all .2s ease-in-out}.v-progress-circular--indeterminate .v-progress-circular__overlay{-webkit-animation:progress-circular-dash 1.4s ease-in-out infinite;animation:progress-circular-dash 1.4s ease-in-out infinite;stroke-linecap:round;stroke-dasharray:80,200;stroke-dashoffset:0px}.v-progress-circular__info{align-items:center;display:flex;justify-content:center}.v-progress-circular__underlay{stroke:rgba(0,0,0,.1);z-index:1}.v-progress-circular__overlay{stroke:currentColor;z-index:2;transition:all .6s ease-in-out}@-webkit-keyframes progress-circular-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0px}50%{stroke-dasharray:100,200;stroke-dashoffset:-15px}to{stroke-dasharray:100,200;stroke-dashoffset:-125px}}@keyframes progress-circular-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0px}50%{stroke-dasharray:100,200;stroke-dashoffset:-15px}to{stroke-dasharray:100,200;stroke-dashoffset:-125px}}@-webkit-keyframes progress-circular-rotate{to{transform:rotate(1turn)}}@keyframes progress-circular-rotate{to{transform:rotate(1turn)}}.container.grow-shrink-0{flex-grow:0;flex-shrink:0}.container.fill-height{align-items:center;display:flex;flex-wrap:wrap}.container.fill-height>.row{flex:1 1 100%;max-width:calc(100% + 24px)}.container.fill-height>.layout{height:100%;flex:1 1 auto}.container.fill-height>.layout.grow-shrink-0{flex-grow:0;flex-shrink:0}.container.grid-list-xs .layout .flex{padding:1px}.container.grid-list-xs .layout:only-child{margin:-1px}.container.grid-list-xs .layout:not(:only-child){margin:auto -1px}.container.grid-list-xs :not(:only-child) .layout:first-child{margin-top:-1px}.container.grid-list-xs :not(:only-child) .layout:last-child{margin-bottom:-1px}.container.grid-list-sm .layout .flex{padding:2px}.container.grid-list-sm .layout:only-child{margin:-2px}.container.grid-list-sm .layout:not(:only-child){margin:auto -2px}.container.grid-list-sm :not(:only-child) .layout:first-child{margin-top:-2px}.container.grid-list-sm :not(:only-child) .layout:last-child{margin-bottom:-2px}.container.grid-list-md .layout .flex{padding:4px}.container.grid-list-md .layout:only-child{margin:-4px}.container.grid-list-md .layout:not(:only-child){margin:auto -4px}.container.grid-list-md :not(:only-child) .layout:first-child{margin-top:-4px}.container.grid-list-md :not(:only-child) .layout:last-child{margin-bottom:-4px}.container.grid-list-lg .layout .flex{padding:8px}.container.grid-list-lg .layout:only-child{margin:-8px}.container.grid-list-lg .layout:not(:only-child){margin:auto -8px}.container.grid-list-lg :not(:only-child) .layout:first-child{margin-top:-8px}.container.grid-list-lg :not(:only-child) .layout:last-child{margin-bottom:-8px}.container.grid-list-xl .layout .flex{padding:12px}.container.grid-list-xl .layout:only-child{margin:-12px}.container.grid-list-xl .layout:not(:only-child){margin:auto -12px}.container.grid-list-xl :not(:only-child) .layout:first-child{margin-top:-12px}.container.grid-list-xl :not(:only-child) .layout:last-child{margin-bottom:-12px}.layout{display:flex;flex:1 1 auto;flex-wrap:nowrap;min-width:0}.layout.reverse{flex-direction:row-reverse}.layout.column{flex-direction:column}.layout.column.reverse{flex-direction:column-reverse}.layout.column>.flex{max-width:100%}.layout.wrap{flex-wrap:wrap}.layout.grow-shrink-0{flex-grow:0;flex-shrink:0}@media (min-width:0){.flex.xs12{flex-basis:100%;flex-grow:0;max-width:100%}.flex.order-xs12{order:12}.flex.xs11{flex-basis:91.6666666667%;flex-grow:0;max-width:91.6666666667%}.flex.order-xs11{order:11}.flex.xs10{flex-basis:83.3333333333%;flex-grow:0;max-width:83.3333333333%}.flex.order-xs10{order:10}.flex.xs9{flex-basis:75%;flex-grow:0;max-width:75%}.flex.order-xs9{order:9}.flex.xs8{flex-basis:66.6666666667%;flex-grow:0;max-width:66.6666666667%}.flex.order-xs8{order:8}.flex.xs7{flex-basis:58.3333333333%;flex-grow:0;max-width:58.3333333333%}.flex.order-xs7{order:7}.flex.xs6{flex-basis:50%;flex-grow:0;max-width:50%}.flex.order-xs6{order:6}.flex.xs5{flex-basis:41.6666666667%;flex-grow:0;max-width:41.6666666667%}.flex.order-xs5{order:5}.flex.xs4{flex-basis:33.3333333333%;flex-grow:0;max-width:33.3333333333%}.flex.order-xs4{order:4}.flex.xs3{flex-basis:25%;flex-grow:0;max-width:25%}.flex.order-xs3{order:3}.flex.xs2{flex-basis:16.6666666667%;flex-grow:0;max-width:16.6666666667%}.flex.order-xs2{order:2}.flex.xs1{flex-basis:8.3333333333%;flex-grow:0;max-width:8.3333333333%}.flex.order-xs1{order:1}.v-application--is-ltr .flex.offset-xs12{margin-left:100%}.v-application--is-rtl .flex.offset-xs12{margin-right:100%}.v-application--is-ltr .flex.offset-xs11{margin-left:91.6666666667%}.v-application--is-rtl .flex.offset-xs11{margin-right:91.6666666667%}.v-application--is-ltr .flex.offset-xs10{margin-left:83.3333333333%}.v-application--is-rtl .flex.offset-xs10{margin-right:83.3333333333%}.v-application--is-ltr .flex.offset-xs9{margin-left:75%}.v-application--is-rtl .flex.offset-xs9{margin-right:75%}.v-application--is-ltr .flex.offset-xs8{margin-left:66.6666666667%}.v-application--is-rtl .flex.offset-xs8{margin-right:66.6666666667%}.v-application--is-ltr .flex.offset-xs7{margin-left:58.3333333333%}.v-application--is-rtl .flex.offset-xs7{margin-right:58.3333333333%}.v-application--is-ltr .flex.offset-xs6{margin-left:50%}.v-application--is-rtl .flex.offset-xs6{margin-right:50%}.v-application--is-ltr .flex.offset-xs5{margin-left:41.6666666667%}.v-application--is-rtl .flex.offset-xs5{margin-right:41.6666666667%}.v-application--is-ltr .flex.offset-xs4{margin-left:33.3333333333%}.v-application--is-rtl .flex.offset-xs4{margin-right:33.3333333333%}.v-application--is-ltr .flex.offset-xs3{margin-left:25%}.v-application--is-rtl .flex.offset-xs3{margin-right:25%}.v-application--is-ltr .flex.offset-xs2{margin-left:16.6666666667%}.v-application--is-rtl .flex.offset-xs2{margin-right:16.6666666667%}.v-application--is-ltr .flex.offset-xs1{margin-left:8.3333333333%}.v-application--is-rtl .flex.offset-xs1{margin-right:8.3333333333%}.v-application--is-ltr .flex.offset-xs0{margin-left:0}.v-application--is-rtl .flex.offset-xs0{margin-right:0}}@media (min-width:600px){.flex.sm12{flex-basis:100%;flex-grow:0;max-width:100%}.flex.order-sm12{order:12}.flex.sm11{flex-basis:91.6666666667%;flex-grow:0;max-width:91.6666666667%}.flex.order-sm11{order:11}.flex.sm10{flex-basis:83.3333333333%;flex-grow:0;max-width:83.3333333333%}.flex.order-sm10{order:10}.flex.sm9{flex-basis:75%;flex-grow:0;max-width:75%}.flex.order-sm9{order:9}.flex.sm8{flex-basis:66.6666666667%;flex-grow:0;max-width:66.6666666667%}.flex.order-sm8{order:8}.flex.sm7{flex-basis:58.3333333333%;flex-grow:0;max-width:58.3333333333%}.flex.order-sm7{order:7}.flex.sm6{flex-basis:50%;flex-grow:0;max-width:50%}.flex.order-sm6{order:6}.flex.sm5{flex-basis:41.6666666667%;flex-grow:0;max-width:41.6666666667%}.flex.order-sm5{order:5}.flex.sm4{flex-basis:33.3333333333%;flex-grow:0;max-width:33.3333333333%}.flex.order-sm4{order:4}.flex.sm3{flex-basis:25%;flex-grow:0;max-width:25%}.flex.order-sm3{order:3}.flex.sm2{flex-basis:16.6666666667%;flex-grow:0;max-width:16.6666666667%}.flex.order-sm2{order:2}.flex.sm1{flex-basis:8.3333333333%;flex-grow:0;max-width:8.3333333333%}.flex.order-sm1{order:1}.v-application--is-ltr .flex.offset-sm12{margin-left:100%}.v-application--is-rtl .flex.offset-sm12{margin-right:100%}.v-application--is-ltr .flex.offset-sm11{margin-left:91.6666666667%}.v-application--is-rtl .flex.offset-sm11{margin-right:91.6666666667%}.v-application--is-ltr .flex.offset-sm10{margin-left:83.3333333333%}.v-application--is-rtl .flex.offset-sm10{margin-right:83.3333333333%}.v-application--is-ltr .flex.offset-sm9{margin-left:75%}.v-application--is-rtl .flex.offset-sm9{margin-right:75%}.v-application--is-ltr .flex.offset-sm8{margin-left:66.6666666667%}.v-application--is-rtl .flex.offset-sm8{margin-right:66.6666666667%}.v-application--is-ltr .flex.offset-sm7{margin-left:58.3333333333%}.v-application--is-rtl .flex.offset-sm7{margin-right:58.3333333333%}.v-application--is-ltr .flex.offset-sm6{margin-left:50%}.v-application--is-rtl .flex.offset-sm6{margin-right:50%}.v-application--is-ltr .flex.offset-sm5{margin-left:41.6666666667%}.v-application--is-rtl .flex.offset-sm5{margin-right:41.6666666667%}.v-application--is-ltr .flex.offset-sm4{margin-left:33.3333333333%}.v-application--is-rtl .flex.offset-sm4{margin-right:33.3333333333%}.v-application--is-ltr .flex.offset-sm3{margin-left:25%}.v-application--is-rtl .flex.offset-sm3{margin-right:25%}.v-application--is-ltr .flex.offset-sm2{margin-left:16.6666666667%}.v-application--is-rtl .flex.offset-sm2{margin-right:16.6666666667%}.v-application--is-ltr .flex.offset-sm1{margin-left:8.3333333333%}.v-application--is-rtl .flex.offset-sm1{margin-right:8.3333333333%}.v-application--is-ltr .flex.offset-sm0{margin-left:0}.v-application--is-rtl .flex.offset-sm0{margin-right:0}}@media (min-width:960px){.flex.md12{flex-basis:100%;flex-grow:0;max-width:100%}.flex.order-md12{order:12}.flex.md11{flex-basis:91.6666666667%;flex-grow:0;max-width:91.6666666667%}.flex.order-md11{order:11}.flex.md10{flex-basis:83.3333333333%;flex-grow:0;max-width:83.3333333333%}.flex.order-md10{order:10}.flex.md9{flex-basis:75%;flex-grow:0;max-width:75%}.flex.order-md9{order:9}.flex.md8{flex-basis:66.6666666667%;flex-grow:0;max-width:66.6666666667%}.flex.order-md8{order:8}.flex.md7{flex-basis:58.3333333333%;flex-grow:0;max-width:58.3333333333%}.flex.order-md7{order:7}.flex.md6{flex-basis:50%;flex-grow:0;max-width:50%}.flex.order-md6{order:6}.flex.md5{flex-basis:41.6666666667%;flex-grow:0;max-width:41.6666666667%}.flex.order-md5{order:5}.flex.md4{flex-basis:33.3333333333%;flex-grow:0;max-width:33.3333333333%}.flex.order-md4{order:4}.flex.md3{flex-basis:25%;flex-grow:0;max-width:25%}.flex.order-md3{order:3}.flex.md2{flex-basis:16.6666666667%;flex-grow:0;max-width:16.6666666667%}.flex.order-md2{order:2}.flex.md1{flex-basis:8.3333333333%;flex-grow:0;max-width:8.3333333333%}.flex.order-md1{order:1}.v-application--is-ltr .flex.offset-md12{margin-left:100%}.v-application--is-rtl .flex.offset-md12{margin-right:100%}.v-application--is-ltr .flex.offset-md11{margin-left:91.6666666667%}.v-application--is-rtl .flex.offset-md11{margin-right:91.6666666667%}.v-application--is-ltr .flex.offset-md10{margin-left:83.3333333333%}.v-application--is-rtl .flex.offset-md10{margin-right:83.3333333333%}.v-application--is-ltr .flex.offset-md9{margin-left:75%}.v-application--is-rtl .flex.offset-md9{margin-right:75%}.v-application--is-ltr .flex.offset-md8{margin-left:66.6666666667%}.v-application--is-rtl .flex.offset-md8{margin-right:66.6666666667%}.v-application--is-ltr .flex.offset-md7{margin-left:58.3333333333%}.v-application--is-rtl .flex.offset-md7{margin-right:58.3333333333%}.v-application--is-ltr .flex.offset-md6{margin-left:50%}.v-application--is-rtl .flex.offset-md6{margin-right:50%}.v-application--is-ltr .flex.offset-md5{margin-left:41.6666666667%}.v-application--is-rtl .flex.offset-md5{margin-right:41.6666666667%}.v-application--is-ltr .flex.offset-md4{margin-left:33.3333333333%}.v-application--is-rtl .flex.offset-md4{margin-right:33.3333333333%}.v-application--is-ltr .flex.offset-md3{margin-left:25%}.v-application--is-rtl .flex.offset-md3{margin-right:25%}.v-application--is-ltr .flex.offset-md2{margin-left:16.6666666667%}.v-application--is-rtl .flex.offset-md2{margin-right:16.6666666667%}.v-application--is-ltr .flex.offset-md1{margin-left:8.3333333333%}.v-application--is-rtl .flex.offset-md1{margin-right:8.3333333333%}.v-application--is-ltr .flex.offset-md0{margin-left:0}.v-application--is-rtl .flex.offset-md0{margin-right:0}}@media (min-width:1264px){.flex.lg12{flex-basis:100%;flex-grow:0;max-width:100%}.flex.order-lg12{order:12}.flex.lg11{flex-basis:91.6666666667%;flex-grow:0;max-width:91.6666666667%}.flex.order-lg11{order:11}.flex.lg10{flex-basis:83.3333333333%;flex-grow:0;max-width:83.3333333333%}.flex.order-lg10{order:10}.flex.lg9{flex-basis:75%;flex-grow:0;max-width:75%}.flex.order-lg9{order:9}.flex.lg8{flex-basis:66.6666666667%;flex-grow:0;max-width:66.6666666667%}.flex.order-lg8{order:8}.flex.lg7{flex-basis:58.3333333333%;flex-grow:0;max-width:58.3333333333%}.flex.order-lg7{order:7}.flex.lg6{flex-basis:50%;flex-grow:0;max-width:50%}.flex.order-lg6{order:6}.flex.lg5{flex-basis:41.6666666667%;flex-grow:0;max-width:41.6666666667%}.flex.order-lg5{order:5}.flex.lg4{flex-basis:33.3333333333%;flex-grow:0;max-width:33.3333333333%}.flex.order-lg4{order:4}.flex.lg3{flex-basis:25%;flex-grow:0;max-width:25%}.flex.order-lg3{order:3}.flex.lg2{flex-basis:16.6666666667%;flex-grow:0;max-width:16.6666666667%}.flex.order-lg2{order:2}.flex.lg1{flex-basis:8.3333333333%;flex-grow:0;max-width:8.3333333333%}.flex.order-lg1{order:1}.v-application--is-ltr .flex.offset-lg12{margin-left:100%}.v-application--is-rtl .flex.offset-lg12{margin-right:100%}.v-application--is-ltr .flex.offset-lg11{margin-left:91.6666666667%}.v-application--is-rtl .flex.offset-lg11{margin-right:91.6666666667%}.v-application--is-ltr .flex.offset-lg10{margin-left:83.3333333333%}.v-application--is-rtl .flex.offset-lg10{margin-right:83.3333333333%}.v-application--is-ltr .flex.offset-lg9{margin-left:75%}.v-application--is-rtl .flex.offset-lg9{margin-right:75%}.v-application--is-ltr .flex.offset-lg8{margin-left:66.6666666667%}.v-application--is-rtl .flex.offset-lg8{margin-right:66.6666666667%}.v-application--is-ltr .flex.offset-lg7{margin-left:58.3333333333%}.v-application--is-rtl .flex.offset-lg7{margin-right:58.3333333333%}.v-application--is-ltr .flex.offset-lg6{margin-left:50%}.v-application--is-rtl .flex.offset-lg6{margin-right:50%}.v-application--is-ltr .flex.offset-lg5{margin-left:41.6666666667%}.v-application--is-rtl .flex.offset-lg5{margin-right:41.6666666667%}.v-application--is-ltr .flex.offset-lg4{margin-left:33.3333333333%}.v-application--is-rtl .flex.offset-lg4{margin-right:33.3333333333%}.v-application--is-ltr .flex.offset-lg3{margin-left:25%}.v-application--is-rtl .flex.offset-lg3{margin-right:25%}.v-application--is-ltr .flex.offset-lg2{margin-left:16.6666666667%}.v-application--is-rtl .flex.offset-lg2{margin-right:16.6666666667%}.v-application--is-ltr .flex.offset-lg1{margin-left:8.3333333333%}.v-application--is-rtl .flex.offset-lg1{margin-right:8.3333333333%}.v-application--is-ltr .flex.offset-lg0{margin-left:0}.v-application--is-rtl .flex.offset-lg0{margin-right:0}}@media (min-width:1904px){.flex.xl12{flex-basis:100%;flex-grow:0;max-width:100%}.flex.order-xl12{order:12}.flex.xl11{flex-basis:91.6666666667%;flex-grow:0;max-width:91.6666666667%}.flex.order-xl11{order:11}.flex.xl10{flex-basis:83.3333333333%;flex-grow:0;max-width:83.3333333333%}.flex.order-xl10{order:10}.flex.xl9{flex-basis:75%;flex-grow:0;max-width:75%}.flex.order-xl9{order:9}.flex.xl8{flex-basis:66.6666666667%;flex-grow:0;max-width:66.6666666667%}.flex.order-xl8{order:8}.flex.xl7{flex-basis:58.3333333333%;flex-grow:0;max-width:58.3333333333%}.flex.order-xl7{order:7}.flex.xl6{flex-basis:50%;flex-grow:0;max-width:50%}.flex.order-xl6{order:6}.flex.xl5{flex-basis:41.6666666667%;flex-grow:0;max-width:41.6666666667%}.flex.order-xl5{order:5}.flex.xl4{flex-basis:33.3333333333%;flex-grow:0;max-width:33.3333333333%}.flex.order-xl4{order:4}.flex.xl3{flex-basis:25%;flex-grow:0;max-width:25%}.flex.order-xl3{order:3}.flex.xl2{flex-basis:16.6666666667%;flex-grow:0;max-width:16.6666666667%}.flex.order-xl2{order:2}.flex.xl1{flex-basis:8.3333333333%;flex-grow:0;max-width:8.3333333333%}.flex.order-xl1{order:1}.v-application--is-ltr .flex.offset-xl12{margin-left:100%}.v-application--is-rtl .flex.offset-xl12{margin-right:100%}.v-application--is-ltr .flex.offset-xl11{margin-left:91.6666666667%}.v-application--is-rtl .flex.offset-xl11{margin-right:91.6666666667%}.v-application--is-ltr .flex.offset-xl10{margin-left:83.3333333333%}.v-application--is-rtl .flex.offset-xl10{margin-right:83.3333333333%}.v-application--is-ltr .flex.offset-xl9{margin-left:75%}.v-application--is-rtl .flex.offset-xl9{margin-right:75%}.v-application--is-ltr .flex.offset-xl8{margin-left:66.6666666667%}.v-application--is-rtl .flex.offset-xl8{margin-right:66.6666666667%}.v-application--is-ltr .flex.offset-xl7{margin-left:58.3333333333%}.v-application--is-rtl .flex.offset-xl7{margin-right:58.3333333333%}.v-application--is-ltr .flex.offset-xl6{margin-left:50%}.v-application--is-rtl .flex.offset-xl6{margin-right:50%}.v-application--is-ltr .flex.offset-xl5{margin-left:41.6666666667%}.v-application--is-rtl .flex.offset-xl5{margin-right:41.6666666667%}.v-application--is-ltr .flex.offset-xl4{margin-left:33.3333333333%}.v-application--is-rtl .flex.offset-xl4{margin-right:33.3333333333%}.v-application--is-ltr .flex.offset-xl3{margin-left:25%}.v-application--is-rtl .flex.offset-xl3{margin-right:25%}.v-application--is-ltr .flex.offset-xl2{margin-left:16.6666666667%}.v-application--is-rtl .flex.offset-xl2{margin-right:16.6666666667%}.v-application--is-ltr .flex.offset-xl1{margin-left:8.3333333333%}.v-application--is-rtl .flex.offset-xl1{margin-right:8.3333333333%}.v-application--is-ltr .flex.offset-xl0{margin-left:0}.v-application--is-rtl .flex.offset-xl0{margin-right:0}}.child-flex>*,.flex{flex:1 1 auto;max-width:100%}.child-flex>.grow-shrink-0,.flex.grow-shrink-0{flex-grow:0;flex-shrink:0}.grow,.spacer{flex-grow:1!important}.grow{flex-shrink:0!important}.shrink{flex-grow:0!important;flex-shrink:1!important}.fill-height{height:100%}.container{width:100%;padding:12px;margin-right:auto;margin-left:auto}@media(min-width:960px){.container{max-width:900px}}@media(min-width:1264px){.container{max-width:1185px}}@media(min-width:1904px){.container{max-width:1785px}}.container--fluid{max-width:100%}.row{display:flex;flex-wrap:wrap;flex:1 1 auto;margin-right:-12px;margin-left:-12px}.row--dense{margin-right:-4px;margin-left:-4px}.row--dense>.col,.row--dense>[class*=col-]{padding:4px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{width:100%;padding:12px}.col{flex-basis:0;flex-grow:1;max-width:100%}.col-auto{flex:0 0 auto;width:auto;max-width:100%}.col-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-12{flex:0 0 100%;max-width:100%}.v-application--is-ltr .offset-1{margin-left:8.3333333333%}.v-application--is-rtl .offset-1{margin-right:8.3333333333%}.v-application--is-ltr .offset-2{margin-left:16.6666666667%}.v-application--is-rtl .offset-2{margin-right:16.6666666667%}.v-application--is-ltr .offset-3{margin-left:25%}.v-application--is-rtl .offset-3{margin-right:25%}.v-application--is-ltr .offset-4{margin-left:33.3333333333%}.v-application--is-rtl .offset-4{margin-right:33.3333333333%}.v-application--is-ltr .offset-5{margin-left:41.6666666667%}.v-application--is-rtl .offset-5{margin-right:41.6666666667%}.v-application--is-ltr .offset-6{margin-left:50%}.v-application--is-rtl .offset-6{margin-right:50%}.v-application--is-ltr .offset-7{margin-left:58.3333333333%}.v-application--is-rtl .offset-7{margin-right:58.3333333333%}.v-application--is-ltr .offset-8{margin-left:66.6666666667%}.v-application--is-rtl .offset-8{margin-right:66.6666666667%}.v-application--is-ltr .offset-9{margin-left:75%}.v-application--is-rtl .offset-9{margin-right:75%}.v-application--is-ltr .offset-10{margin-left:83.3333333333%}.v-application--is-rtl .offset-10{margin-right:83.3333333333%}.v-application--is-ltr .offset-11{margin-left:91.6666666667%}.v-application--is-rtl .offset-11{margin-right:91.6666666667%}@media(min-width:600px){.col-sm{flex-basis:0;flex-grow:1;max-width:100%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-sm-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-sm-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-sm-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-sm-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-sm-12{flex:0 0 100%;max-width:100%}.v-application--is-ltr .offset-sm-0{margin-left:0}.v-application--is-rtl .offset-sm-0{margin-right:0}.v-application--is-ltr .offset-sm-1{margin-left:8.3333333333%}.v-application--is-rtl .offset-sm-1{margin-right:8.3333333333%}.v-application--is-ltr .offset-sm-2{margin-left:16.6666666667%}.v-application--is-rtl .offset-sm-2{margin-right:16.6666666667%}.v-application--is-ltr .offset-sm-3{margin-left:25%}.v-application--is-rtl .offset-sm-3{margin-right:25%}.v-application--is-ltr .offset-sm-4{margin-left:33.3333333333%}.v-application--is-rtl .offset-sm-4{margin-right:33.3333333333%}.v-application--is-ltr .offset-sm-5{margin-left:41.6666666667%}.v-application--is-rtl .offset-sm-5{margin-right:41.6666666667%}.v-application--is-ltr .offset-sm-6{margin-left:50%}.v-application--is-rtl .offset-sm-6{margin-right:50%}.v-application--is-ltr .offset-sm-7{margin-left:58.3333333333%}.v-application--is-rtl .offset-sm-7{margin-right:58.3333333333%}.v-application--is-ltr .offset-sm-8{margin-left:66.6666666667%}.v-application--is-rtl .offset-sm-8{margin-right:66.6666666667%}.v-application--is-ltr .offset-sm-9{margin-left:75%}.v-application--is-rtl .offset-sm-9{margin-right:75%}.v-application--is-ltr .offset-sm-10{margin-left:83.3333333333%}.v-application--is-rtl .offset-sm-10{margin-right:83.3333333333%}.v-application--is-ltr .offset-sm-11{margin-left:91.6666666667%}.v-application--is-rtl .offset-sm-11{margin-right:91.6666666667%}}@media(min-width:960px){.col-md{flex-basis:0;flex-grow:1;max-width:100%}.col-md-auto{flex:0 0 auto;width:auto;max-width:100%}.col-md-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-md-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-md-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-md-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-md-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-md-12{flex:0 0 100%;max-width:100%}.v-application--is-ltr .offset-md-0{margin-left:0}.v-application--is-rtl .offset-md-0{margin-right:0}.v-application--is-ltr .offset-md-1{margin-left:8.3333333333%}.v-application--is-rtl .offset-md-1{margin-right:8.3333333333%}.v-application--is-ltr .offset-md-2{margin-left:16.6666666667%}.v-application--is-rtl .offset-md-2{margin-right:16.6666666667%}.v-application--is-ltr .offset-md-3{margin-left:25%}.v-application--is-rtl .offset-md-3{margin-right:25%}.v-application--is-ltr .offset-md-4{margin-left:33.3333333333%}.v-application--is-rtl .offset-md-4{margin-right:33.3333333333%}.v-application--is-ltr .offset-md-5{margin-left:41.6666666667%}.v-application--is-rtl .offset-md-5{margin-right:41.6666666667%}.v-application--is-ltr .offset-md-6{margin-left:50%}.v-application--is-rtl .offset-md-6{margin-right:50%}.v-application--is-ltr .offset-md-7{margin-left:58.3333333333%}.v-application--is-rtl .offset-md-7{margin-right:58.3333333333%}.v-application--is-ltr .offset-md-8{margin-left:66.6666666667%}.v-application--is-rtl .offset-md-8{margin-right:66.6666666667%}.v-application--is-ltr .offset-md-9{margin-left:75%}.v-application--is-rtl .offset-md-9{margin-right:75%}.v-application--is-ltr .offset-md-10{margin-left:83.3333333333%}.v-application--is-rtl .offset-md-10{margin-right:83.3333333333%}.v-application--is-ltr .offset-md-11{margin-left:91.6666666667%}.v-application--is-rtl .offset-md-11{margin-right:91.6666666667%}}@media(min-width:1264px){.col-lg{flex-basis:0;flex-grow:1;max-width:100%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-lg-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-lg-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-lg-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-lg-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-lg-12{flex:0 0 100%;max-width:100%}.v-application--is-ltr .offset-lg-0{margin-left:0}.v-application--is-rtl .offset-lg-0{margin-right:0}.v-application--is-ltr .offset-lg-1{margin-left:8.3333333333%}.v-application--is-rtl .offset-lg-1{margin-right:8.3333333333%}.v-application--is-ltr .offset-lg-2{margin-left:16.6666666667%}.v-application--is-rtl .offset-lg-2{margin-right:16.6666666667%}.v-application--is-ltr .offset-lg-3{margin-left:25%}.v-application--is-rtl .offset-lg-3{margin-right:25%}.v-application--is-ltr .offset-lg-4{margin-left:33.3333333333%}.v-application--is-rtl .offset-lg-4{margin-right:33.3333333333%}.v-application--is-ltr .offset-lg-5{margin-left:41.6666666667%}.v-application--is-rtl .offset-lg-5{margin-right:41.6666666667%}.v-application--is-ltr .offset-lg-6{margin-left:50%}.v-application--is-rtl .offset-lg-6{margin-right:50%}.v-application--is-ltr .offset-lg-7{margin-left:58.3333333333%}.v-application--is-rtl .offset-lg-7{margin-right:58.3333333333%}.v-application--is-ltr .offset-lg-8{margin-left:66.6666666667%}.v-application--is-rtl .offset-lg-8{margin-right:66.6666666667%}.v-application--is-ltr .offset-lg-9{margin-left:75%}.v-application--is-rtl .offset-lg-9{margin-right:75%}.v-application--is-ltr .offset-lg-10{margin-left:83.3333333333%}.v-application--is-rtl .offset-lg-10{margin-right:83.3333333333%}.v-application--is-ltr .offset-lg-11{margin-left:91.6666666667%}.v-application--is-rtl .offset-lg-11{margin-right:91.6666666667%}}@media(min-width:1904px){.col-xl{flex-basis:0;flex-grow:1;max-width:100%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-xl-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-xl-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-xl-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-xl-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-xl-12{flex:0 0 100%;max-width:100%}.v-application--is-ltr .offset-xl-0{margin-left:0}.v-application--is-rtl .offset-xl-0{margin-right:0}.v-application--is-ltr .offset-xl-1{margin-left:8.3333333333%}.v-application--is-rtl .offset-xl-1{margin-right:8.3333333333%}.v-application--is-ltr .offset-xl-2{margin-left:16.6666666667%}.v-application--is-rtl .offset-xl-2{margin-right:16.6666666667%}.v-application--is-ltr .offset-xl-3{margin-left:25%}.v-application--is-rtl .offset-xl-3{margin-right:25%}.v-application--is-ltr .offset-xl-4{margin-left:33.3333333333%}.v-application--is-rtl .offset-xl-4{margin-right:33.3333333333%}.v-application--is-ltr .offset-xl-5{margin-left:41.6666666667%}.v-application--is-rtl .offset-xl-5{margin-right:41.6666666667%}.v-application--is-ltr .offset-xl-6{margin-left:50%}.v-application--is-rtl .offset-xl-6{margin-right:50%}.v-application--is-ltr .offset-xl-7{margin-left:58.3333333333%}.v-application--is-rtl .offset-xl-7{margin-right:58.3333333333%}.v-application--is-ltr .offset-xl-8{margin-left:66.6666666667%}.v-application--is-rtl .offset-xl-8{margin-right:66.6666666667%}.v-application--is-ltr .offset-xl-9{margin-left:75%}.v-application--is-rtl .offset-xl-9{margin-right:75%}.v-application--is-ltr .offset-xl-10{margin-left:83.3333333333%}.v-application--is-rtl .offset-xl-10{margin-right:83.3333333333%}.v-application--is-ltr .offset-xl-11{margin-left:91.6666666667%}.v-application--is-rtl .offset-xl-11{margin-right:91.6666666667%}}.theme--light.v-icon{color:rgba(0,0,0,.54)}.theme--light.v-icon:focus:after{opacity:.12}.theme--light.v-icon.v-icon.v-icon--disabled{color:rgba(0,0,0,.38)!important}.theme--dark.v-icon{color:#fff}.theme--dark.v-icon:focus:after{opacity:.24}.theme--dark.v-icon.v-icon.v-icon--disabled{color:hsla(0,0%,100%,.5)!important}.v-icon.v-icon{align-items:center;display:inline-flex;font-feature-settings:"liga";font-size:24px;justify-content:center;letter-spacing:normal;line-height:1;position:relative;text-indent:0;transition:.3s cubic-bezier(.25,.8,.5,1),visibility 0s;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-icon.v-icon:after{background-color:currentColor;border-radius:50%;content:"";display:inline-block;height:100%;opacity:0;pointer-events:none;position:absolute;transform:scale(1.3);width:100%;transition:opacity .2s cubic-bezier(.4,0,.6,1)}.v-icon.v-icon--dense{font-size:20px}.v-icon--right{margin-left:8px}.v-icon--left{margin-right:8px}.v-icon.v-icon.v-icon--link{cursor:pointer;outline:none}.v-icon--disabled{pointer-events:none}.v-icon--is-component,.v-icon--svg{height:24px;width:24px}.v-icon--svg{fill:currentColor}.v-icon--dense--is-component{height:20px}.v-item-group{flex:0 1 auto;position:relative;max-width:100%;transition:.3s cubic-bezier(.25,.8,.5,1)}.v-list.accent>.v-list-item,.v-list.error>.v-list-item,.v-list.info>.v-list-item,.v-list.primary>.v-list-item,.v-list.secondary>.v-list-item,.v-list.success>.v-list-item,.v-list.warning>.v-list-item{color:#fff}.theme--light.v-list{background:#fff;color:rgba(0,0,0,.87)}.theme--light.v-list .v-list--disabled{color:rgba(0,0,0,.38)}.theme--light.v-list .v-list-group--active:after,.theme--light.v-list .v-list-group--active:before{background:rgba(0,0,0,.12)}.theme--dark.v-list{background:#1e1e1e;color:#fff}.theme--dark.v-list .v-list--disabled{color:hsla(0,0%,100%,.5)}.theme--dark.v-list .v-list-group--active:after,.theme--dark.v-list .v-list-group--active:before{background:hsla(0,0%,100%,.12)}.v-sheet.v-list{border-radius:0}.v-sheet.v-list:not(.v-sheet--outlined){box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)}.v-sheet.v-list.v-sheet--shaped{border-radius:0}.v-list{display:block;padding:8px 0;position:static;transition:box-shadow .28s cubic-bezier(.4,0,.2,1);will-change:box-shadow}.v-list--disabled{pointer-events:none}.v-list--flat .v-list-item:before{display:none}.v-list--dense .v-subheader{font-size:.75rem;height:40px;padding:0 8px}.v-list--nav .v-list-item:not(:last-child):not(:only-child),.v-list--rounded .v-list-item:not(:last-child):not(:only-child){margin-bottom:8px}.v-list--nav.v-list--dense .v-list-item:not(:last-child):not(:only-child),.v-list--nav .v-list-item--dense:not(:last-child):not(:only-child),.v-list--rounded.v-list--dense .v-list-item:not(:last-child):not(:only-child),.v-list--rounded .v-list-item--dense:not(:last-child):not(:only-child){margin-bottom:4px}.v-list--nav{padding-left:8px;padding-right:8px}.v-list--nav .v-list-item{padding:0 8px}.v-list--nav .v-list-item,.v-list--nav .v-list-item:before{border-radius:4px}.v-application--is-ltr .v-list.v-sheet--shaped .v-list-item,.v-application--is-ltr .v-list.v-sheet--shaped .v-list-item:before,.v-application--is-ltr .v-list.v-sheet--shaped .v-list-item>.v-ripple__container{border-bottom-right-radius:32px!important;border-top-right-radius:32px!important}.v-application--is-rtl .v-list.v-sheet--shaped .v-list-item,.v-application--is-rtl .v-list.v-sheet--shaped .v-list-item:before,.v-application--is-rtl .v-list.v-sheet--shaped .v-list-item>.v-ripple__container{border-bottom-left-radius:32px!important;border-top-left-radius:32px!important}.v-application--is-ltr .v-list.v-sheet--shaped.v-list--two-line .v-list-item,.v-application--is-ltr .v-list.v-sheet--shaped.v-list--two-line .v-list-item:before,.v-application--is-ltr .v-list.v-sheet--shaped.v-list--two-line .v-list-item>.v-ripple__container{border-bottom-right-radius:42.6666666667px!important;border-top-right-radius:42.6666666667px!important}.v-application--is-rtl .v-list.v-sheet--shaped.v-list--two-line .v-list-item,.v-application--is-rtl .v-list.v-sheet--shaped.v-list--two-line .v-list-item:before,.v-application--is-rtl .v-list.v-sheet--shaped.v-list--two-line .v-list-item>.v-ripple__container{border-bottom-left-radius:42.6666666667px!important;border-top-left-radius:42.6666666667px!important}.v-application--is-ltr .v-list.v-sheet--shaped.v-list--three-line .v-list-item,.v-application--is-ltr .v-list.v-sheet--shaped.v-list--three-line .v-list-item:before,.v-application--is-ltr .v-list.v-sheet--shaped.v-list--three-line .v-list-item>.v-ripple__container{border-bottom-right-radius:58.6666666667px!important;border-top-right-radius:58.6666666667px!important}.v-application--is-rtl .v-list.v-sheet--shaped.v-list--three-line .v-list-item,.v-application--is-rtl .v-list.v-sheet--shaped.v-list--three-line .v-list-item:before,.v-application--is-rtl .v-list.v-sheet--shaped.v-list--three-line .v-list-item>.v-ripple__container{border-bottom-left-radius:58.6666666667px!important;border-top-left-radius:58.6666666667px!important}.v-application--is-ltr .v-list.v-sheet--shaped{padding-right:8px}.v-application--is-rtl .v-list.v-sheet--shaped{padding-left:8px}.v-list--rounded{padding:8px}.v-list--rounded .v-list-item,.v-list--rounded .v-list-item:before,.v-list--rounded .v-list-item>.v-ripple__container{border-radius:32px!important}.v-list--rounded.v-list--two-line .v-list-item,.v-list--rounded.v-list--two-line .v-list-item:before,.v-list--rounded.v-list--two-line .v-list-item>.v-ripple__container{border-radius:42.6666666667px!important}.v-list--rounded.v-list--three-line .v-list-item,.v-list--rounded.v-list--three-line .v-list-item:before,.v-list--rounded.v-list--three-line .v-list-item>.v-ripple__container{border-radius:58.6666666667px!important}.v-list--subheader{padding-top:0}.v-list-group .v-list-group__header .v-list-item__icon.v-list-group__header__append-icon{align-self:center;margin:0;min-width:48px;justify-content:flex-end}.v-list-group--sub-group{align-items:center;display:flex;flex-wrap:wrap}.v-list-group__header.v-list-item--active:not(:hover):not(:focus):before{opacity:0}.v-list-group__items{flex:1 1 auto}.v-list-group__items .v-list-group__items,.v-list-group__items .v-list-item{overflow:hidden}.v-list-group--active>.v-list-group__header.v-list-group__header--sub-group>.v-list-group__header__prepend-icon .v-icon,.v-list-group--active>.v-list-group__header>.v-list-group__header__append-icon .v-icon{transform:rotate(-180deg)}.v-list-group--active>.v-list-group__header .v-list-group__header__prepend-icon .v-icon,.v-list-group--active>.v-list-group__header .v-list-item,.v-list-group--active>.v-list-group__header .v-list-item__content{color:inherit}.v-application--is-ltr .v-list-group--sub-group .v-list-item__action:first-child,.v-application--is-ltr .v-list-group--sub-group .v-list-item__avatar:first-child,.v-application--is-ltr .v-list-group--sub-group .v-list-item__icon:first-child{margin-right:16px}.v-application--is-rtl .v-list-group--sub-group .v-list-item__action:first-child,.v-application--is-rtl .v-list-group--sub-group .v-list-item__avatar:first-child,.v-application--is-rtl .v-list-group--sub-group .v-list-item__icon:first-child{margin-left:16px}.v-application--is-ltr .v-list-group--sub-group .v-list-group__header{padding-left:32px}.v-application--is-rtl .v-list-group--sub-group .v-list-group__header{padding-right:32px}.v-application--is-ltr .v-list-group--sub-group .v-list-group__items .v-list-item{padding-left:40px}.v-application--is-rtl .v-list-group--sub-group .v-list-group__items .v-list-item{padding-right:40px}.v-list-group--sub-group.v-list-group--active .v-list-item__icon.v-list-group__header__prepend-icon .v-icon{transform:rotate(-180deg)}.v-application--is-ltr .v-list-group--no-action>.v-list-group__items>.v-list-item{padding-left:72px}.v-application--is-rtl .v-list-group--no-action>.v-list-group__items>.v-list-item{padding-right:72px}.v-application--is-ltr .v-list-group--no-action.v-list-group--sub-group>.v-list-group__items>.v-list-item{padding-left:88px}.v-application--is-rtl .v-list-group--no-action.v-list-group--sub-group>.v-list-group__items>.v-list-item{padding-right:88px}.v-application--is-ltr .v-list--dense .v-list-group--sub-group .v-list-group__header{padding-left:24px}.v-application--is-rtl .v-list--dense .v-list-group--sub-group .v-list-group__header{padding-right:24px}.v-application--is-ltr .v-list--dense.v-list--nav .v-list-group--no-action>.v-list-group__items>.v-list-item{padding-left:64px}.v-application--is-rtl .v-list--dense.v-list--nav .v-list-group--no-action>.v-list-group__items>.v-list-item{padding-right:64px}.v-application--is-ltr .v-list--dense.v-list--nav .v-list-group--no-action.v-list-group--sub-group>.v-list-group__items>.v-list-item{padding-left:80px}.v-application--is-rtl .v-list--dense.v-list--nav .v-list-group--no-action.v-list-group--sub-group>.v-list-group__items>.v-list-item{padding-right:80px}.theme--light.v-list-item--disabled{color:rgba(0,0,0,.38)}.theme--light.v-list-item:not(.v-list-item--active):not(.v-list-item--disabled){color:rgba(0,0,0,.87)!important}.theme--light.v-list-item .v-list-item__mask{color:rgba(0,0,0,.38);background:#eee}.theme--light.v-list-item .v-list-item__action-text,.theme--light.v-list-item .v-list-item__subtitle{color:rgba(0,0,0,.6)}.theme--light.v-list-item:hover:before{opacity:.04}.theme--light.v-list-item--active:before,.theme--light.v-list-item--active:hover:before,.theme--light.v-list-item:focus:before{opacity:.12}.theme--light.v-list-item--active:focus:before,.theme--light.v-list-item.v-list-item--highlighted:before{opacity:.16}.theme--dark.v-list-item--disabled{color:hsla(0,0%,100%,.5)}.theme--dark.v-list-item:not(.v-list-item--active):not(.v-list-item--disabled){color:#fff!important}.theme--dark.v-list-item .v-list-item__mask{color:hsla(0,0%,100%,.5);background:#494949}.theme--dark.v-list-item .v-list-item__action-text,.theme--dark.v-list-item .v-list-item__subtitle{color:hsla(0,0%,100%,.7)}.theme--dark.v-list-item:hover:before{opacity:.08}.theme--dark.v-list-item--active:before,.theme--dark.v-list-item--active:hover:before,.theme--dark.v-list-item:focus:before{opacity:.24}.theme--dark.v-list-item--active:focus:before,.theme--dark.v-list-item.v-list-item--highlighted:before{opacity:.32}.v-list-item{align-items:center;display:flex;flex:1 1 100%;letter-spacing:normal;min-height:48px;outline:none;padding:0 16px;position:relative;text-decoration:none}.v-list-item--disabled{pointer-events:none}.v-list-item--selectable{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.v-list-item:after{content:"";min-height:inherit;font-size:0}.v-list-item__action{align-self:center;margin:12px 0}.v-list-item__action .v-input,.v-list-item__action .v-input--selection-controls__input,.v-list-item__action .v-input__control,.v-list-item__action .v-input__slot{margin:0!important}.v-list-item__action .v-input{padding:0}.v-list-item__action .v-input .v-messages{display:none}.v-list-item__action-text{font-size:.75rem}.v-list-item__avatar{align-self:center;justify-content:flex-start}.v-list-item__avatar,.v-list-item__avatar.v-list-item__avatar--horizontal{margin-bottom:8px;margin-top:8px}.v-application--is-ltr .v-list-item__avatar.v-list-item__avatar--horizontal:first-child{margin-left:-16px}.v-application--is-rtl .v-list-item__avatar.v-list-item__avatar--horizontal:first-child{margin-right:-16px}.v-application--is-ltr .v-list-item__avatar.v-list-item__avatar--horizontal:last-child{margin-left:-16px}.v-application--is-rtl .v-list-item__avatar.v-list-item__avatar--horizontal:last-child{margin-right:-16px}.v-list-item__content{align-items:center;align-self:center;display:flex;flex-wrap:wrap;flex:1 1;overflow:hidden;padding:12px 0}.v-list-item__content>*{line-height:1.1;flex:1 0 100%}.v-list-item__content>:not(:last-child){margin-bottom:2px}.v-list-item__icon{align-self:flex-start;margin:16px 0}.v-application--is-ltr .v-list-item__action:last-of-type:not(:only-child),.v-application--is-ltr .v-list-item__avatar:last-of-type:not(:only-child),.v-application--is-ltr .v-list-item__icon:last-of-type:not(:only-child){margin-left:16px}.v-application--is-ltr .v-list-item__avatar:first-child,.v-application--is-rtl .v-list-item__action:last-of-type:not(:only-child),.v-application--is-rtl .v-list-item__avatar:last-of-type:not(:only-child),.v-application--is-rtl .v-list-item__icon:last-of-type:not(:only-child){margin-right:16px}.v-application--is-rtl .v-list-item__avatar:first-child{margin-left:16px}.v-application--is-ltr .v-list-item__action:first-child,.v-application--is-ltr .v-list-item__icon:first-child{margin-right:32px}.v-application--is-rtl .v-list-item__action:first-child,.v-application--is-rtl .v-list-item__icon:first-child{margin-left:32px}.v-list-item__action,.v-list-item__avatar,.v-list-item__icon{display:inline-flex;min-width:24px}.v-list-item .v-list-item__subtitle,.v-list-item .v-list-item__title{line-height:1.2}.v-list-item__subtitle,.v-list-item__title{flex:1 1 100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.v-list-item__title{align-self:center;font-size:1rem}.v-list-item__title>.v-badge{margin-top:16px}.v-list-item__subtitle{font-size:.875rem}.v-list--dense .v-list-item,.v-list-item--dense{min-height:40px}.v-list--dense .v-list-item .v-list-item__icon,.v-list-item--dense .v-list-item__icon{height:24px;margin-top:8px;margin-bottom:8px}.v-list--dense .v-list-item .v-list-item__content,.v-list-item--dense .v-list-item__content{padding:8px 0}.v-list--dense .v-list-item .v-list-item__subtitle,.v-list--dense .v-list-item .v-list-item__title,.v-list-item--dense .v-list-item__subtitle,.v-list-item--dense .v-list-item__title{font-size:.8125rem;font-weight:500;line-height:1rem}.v-list--dense .v-list-item.v-list-item--two-line,.v-list-item--dense.v-list-item--two-line{min-height:60px}.v-list--dense .v-list-item.v-list-item--three-line,.v-list-item--dense.v-list-item--three-line{min-height:76px}.v-list-item--link{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-list-item--link:before{background-color:currentColor;bottom:0;content:"";left:0;opacity:0;pointer-events:none;position:absolute;right:0;top:0;transition:.3s cubic-bezier(.25,.8,.5,1)}.v-list .v-list-item--active,.v-list .v-list-item--active .v-icon{color:inherit}.v-list-item__action--stack{align-items:flex-end;align-self:stretch;justify-content:space-between;white-space:nowrap;flex-direction:column}.v-list--three-line .v-list-item .v-list-item__avatar:not(.v-list-item__avatar--horizontal),.v-list--three-line .v-list-item .v-list-item__icon,.v-list--two-line .v-list-item .v-list-item__avatar:not(.v-list-item__avatar--horizontal),.v-list--two-line .v-list-item .v-list-item__icon,.v-list-item--three-line .v-list-item__avatar:not(.v-list-item__avatar--horizontal),.v-list-item--three-line .v-list-item__icon,.v-list-item--two-line .v-list-item__avatar:not(.v-list-item__avatar--horizontal),.v-list-item--two-line .v-list-item__icon{margin-bottom:16px;margin-top:16px}.v-list--two-line .v-list-item,.v-list-item--two-line{min-height:64px}.v-list--two-line .v-list-item .v-list-item__icon,.v-list-item--two-line .v-list-item__icon{margin-bottom:32px}.v-list--three-line .v-list-item,.v-list-item--three-line{min-height:88px}.v-list--three-line .v-list-item .v-list-item__action,.v-list--three-line .v-list-item .v-list-item__avatar,.v-list-item--three-line .v-list-item__action,.v-list-item--three-line .v-list-item__avatar{align-self:flex-start;margin-top:16px;margin-bottom:16px}.v-list--three-line .v-list-item .v-list-item__content,.v-list-item--three-line .v-list-item__content{align-self:stretch}.v-list--three-line .v-list-item .v-list-item__subtitle,.v-list-item--three-line .v-list-item__subtitle{white-space:normal;-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box}.v-avatar{align-items:center;border-radius:50%;display:inline-flex;justify-content:center;line-height:normal;position:relative;text-align:center;vertical-align:middle;overflow:hidden}.v-avatar .v-icon,.v-avatar .v-image,.v-avatar .v-responsive__content,.v-avatar img,.v-avatar svg{border-radius:inherit;display:inline-flex;height:inherit;width:inherit}.v-list-item-group .v-list-item--active{color:inherit}.v-menu{display:none}.v-menu--attached{display:inline}.v-menu__content{position:absolute;display:inline-block;max-width:80%;overflow-y:auto;overflow-x:hidden;contain:content;will-change:transform;box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12);border-radius:4px}.v-menu__content--active{pointer-events:none}.v-menu__content--auto .v-list-item{transition-property:transform,opacity;transition-duration:.3s;transition-timing-function:cubic-bezier(.25,.8,.25,1)}.v-menu__content--fixed{position:fixed}.v-menu__content>.card{contain:content;-webkit-backface-visibility:hidden;backface-visibility:hidden}.v-menu>.v-menu__content{max-width:none}.v-menu-transition-enter .v-list-item{min-width:0;pointer-events:none}.v-menu-transition-enter-to .v-list-item{pointer-events:auto;transition-delay:.1s}.v-menu-transition-leave-active,.v-menu-transition-leave-to{pointer-events:none}.v-menu-transition-enter,.v-menu-transition-leave-to{opacity:0}.v-menu-transition-enter-active,.v-menu-transition-leave-active{transition:all .3s cubic-bezier(.25,.8,.25,1)}.v-menu-transition-enter.v-menu__content--auto{transition:none!important}.v-menu-transition-enter.v-menu__content--auto .v-list-item{opacity:0;transform:translateY(-15px)}.v-menu-transition-enter.v-menu__content--auto .v-list-item--active{opacity:1;transform:none!important;pointer-events:auto}.theme--light.v-application{background:#fff;color:rgba(0,0,0,.87)}.theme--light.v-application .text--primary{color:rgba(0,0,0,.87)!important}.theme--light.v-application .text--secondary{color:rgba(0,0,0,.6)!important}.theme--light.v-application .text--disabled{color:rgba(0,0,0,.38)!important}.theme--dark.v-application{background:#121212;color:#fff}.theme--dark.v-application .text--primary{color:#fff!important}.theme--dark.v-application .text--secondary{color:hsla(0,0%,100%,.7)!important}.theme--dark.v-application .text--disabled{color:hsla(0,0%,100%,.5)!important}.v-application{display:flex}.v-application a{cursor:pointer}.v-application--is-rtl{direction:rtl}.v-application--wrap{flex:1 1 auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;display:flex;flex-direction:column;min-height:100vh;max-width:100%;position:relative}@-moz-document url-prefix(){@media print{.v-application,.v-application--wrap{display:block}}}.theme--light.v-app-bar.v-toolbar.v-sheet{background-color:#f5f5f5}.theme--dark.v-app-bar.v-toolbar.v-sheet{background-color:#272727}.v-sheet.v-app-bar.v-toolbar{border-radius:0}.v-sheet.v-app-bar.v-toolbar:not(.v-sheet--outlined){box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.v-sheet.v-app-bar.v-toolbar.v-sheet--shaped{border-radius:24px 0}.v-app-bar:not([data-booted=true]){transition:none!important}.v-app-bar.v-app-bar--fixed{position:fixed;top:0;z-index:5}.v-app-bar.v-app-bar--hide-shadow{box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)!important}.v-app-bar--fade-img-on-scroll .v-toolbar__image .v-image__image{transition:opacity .4s cubic-bezier(.4,0,.2,1)}.v-app-bar.v-toolbar--prominent.v-app-bar--shrink-on-scroll .v-toolbar__content{will-change:height}.v-app-bar.v-toolbar--prominent.v-app-bar--shrink-on-scroll .v-toolbar__image{will-change:opacity}.v-app-bar.v-toolbar--prominent.v-app-bar--shrink-on-scroll.v-app-bar--collapse-on-scroll .v-toolbar__extension{display:none}.v-app-bar.v-toolbar--prominent.v-app-bar--shrink-on-scroll.v-app-bar--is-scrolled .v-toolbar__title{padding-top:9px}.v-app-bar.v-toolbar--prominent.v-app-bar--shrink-on-scroll.v-app-bar--is-scrolled:not(.v-app-bar--bottom) .v-toolbar__title{padding-bottom:9px}.v-app-bar.v-app-bar--shrink-on-scroll .v-toolbar__title{font-size:inherit}.theme--light.v-toolbar.v-sheet{background-color:#fff}.theme--dark.v-toolbar.v-sheet{background-color:#272727}.v-sheet.v-toolbar{border-radius:0}.v-sheet.v-toolbar:not(.v-sheet--outlined){box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.v-sheet.v-toolbar.v-sheet--shaped{border-radius:24px 0}.v-toolbar{contain:layout;display:block;flex:1 1 auto;max-width:100%;transition:transform .2s cubic-bezier(.4,0,.2,1),background-color .2s cubic-bezier(.4,0,.2,1),left .2s cubic-bezier(.4,0,.2,1),right .2s cubic-bezier(.4,0,.2,1),box-shadow .28s cubic-bezier(.4,0,.2,1),max-width .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1);box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.v-toolbar .v-input{padding-top:0;margin-top:0}.v-toolbar__content,.v-toolbar__extension{padding:4px 16px}.v-toolbar__content .v-btn.v-btn--icon.v-size--default,.v-toolbar__extension .v-btn.v-btn--icon.v-size--default{height:48px;width:48px}.v-application--is-ltr .v-toolbar__content>.v-btn.v-btn--icon:first-child,.v-application--is-ltr .v-toolbar__extension>.v-btn.v-btn--icon:first-child{margin-left:-12px}.v-application--is-rtl .v-toolbar__content>.v-btn.v-btn--icon:first-child,.v-application--is-rtl .v-toolbar__extension>.v-btn.v-btn--icon:first-child{margin-right:-12px}.v-application--is-ltr .v-toolbar__content>.v-btn.v-btn--icon:first-child+.v-toolbar__title,.v-application--is-ltr .v-toolbar__extension>.v-btn.v-btn--icon:first-child+.v-toolbar__title{padding-left:20px}.v-application--is-rtl .v-toolbar__content>.v-btn.v-btn--icon:first-child+.v-toolbar__title,.v-application--is-rtl .v-toolbar__extension>.v-btn.v-btn--icon:first-child+.v-toolbar__title{padding-right:20px}.v-application--is-ltr .v-toolbar__content>.v-btn.v-btn--icon:last-child,.v-application--is-ltr .v-toolbar__extension>.v-btn.v-btn--icon:last-child{margin-right:-12px}.v-application--is-rtl .v-toolbar__content>.v-btn.v-btn--icon:last-child,.v-application--is-rtl .v-toolbar__extension>.v-btn.v-btn--icon:last-child{margin-left:-12px}.v-toolbar__content>.v-tabs,.v-toolbar__extension>.v-tabs{height:inherit;margin-top:-4px;margin-bottom:-4px}.v-toolbar__content>.v-tabs>.v-slide-group.v-tabs-bar,.v-toolbar__extension>.v-tabs>.v-slide-group.v-tabs-bar{background-color:inherit;height:inherit}.v-toolbar__content>.v-tabs:first-child,.v-toolbar__extension>.v-tabs:first-child{margin-left:-16px}.v-toolbar__content>.v-tabs:last-child,.v-toolbar__extension>.v-tabs:last-child{margin-right:-16px}.v-toolbar__content,.v-toolbar__extension{align-items:center;display:flex;position:relative;z-index:0}.v-toolbar__image{position:absolute;top:0;bottom:0;width:100%;z-index:0;contain:strict}.v-toolbar__image,.v-toolbar__image .v-image{border-radius:inherit}.v-toolbar__items{display:flex;height:inherit}.v-toolbar__items>.v-btn{border-radius:0;height:100%!important;max-height:none}.v-toolbar__title{font-size:1.25rem;line-height:1.5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.v-toolbar.v-toolbar--absolute{position:absolute;top:0;z-index:1}.v-toolbar.v-toolbar--bottom{top:auto;bottom:0}.v-toolbar.v-toolbar--collapse .v-toolbar__title{white-space:nowrap}.v-toolbar.v-toolbar--collapsed{max-width:112px;overflow:hidden}.v-application--is-ltr .v-toolbar.v-toolbar--collapsed{border-bottom-right-radius:24px}.v-application--is-rtl .v-toolbar.v-toolbar--collapsed{border-bottom-left-radius:24px}.v-toolbar.v-toolbar--collapsed .v-toolbar__extension,.v-toolbar.v-toolbar--collapsed .v-toolbar__title{display:none}.v-toolbar--dense .v-toolbar__content,.v-toolbar--dense .v-toolbar__extension{padding-top:0;padding-bottom:0}.v-toolbar--flat{box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)!important}.v-toolbar--floating{display:inline-flex}.v-toolbar--prominent .v-toolbar__content{align-items:flex-start}.v-toolbar--prominent .v-toolbar__title{font-size:1.5rem;padding-top:6px}.v-toolbar--prominent:not(.v-toolbar--bottom) .v-toolbar__title{align-self:flex-end;padding-bottom:6px;padding-top:0}.theme--light.v-image{color:rgba(0,0,0,.87)}.theme--dark.v-image{color:#fff}.v-image{z-index:0}.v-image__image,.v-image__placeholder{z-index:-1;position:absolute;top:0;left:0;width:100%;height:100%}.v-image__image{background-repeat:no-repeat}.v-image__image--preload{-webkit-filter:blur(2px);filter:blur(2px)}.v-image__image--contain{background-size:contain}.v-image__image--cover{background-size:cover}.v-responsive{position:relative;overflow:hidden;flex:1 0 auto;max-width:100%;display:flex}.v-responsive__content{flex:1 0 0px;max-width:100%}.v-application--is-ltr .v-responsive__sizer~.v-responsive__content{margin-left:-100%}.v-application--is-rtl .v-responsive__sizer~.v-responsive__content{margin-right:-100%}.v-responsive__sizer{transition:padding-bottom .2s cubic-bezier(.25,.8,.5,1);flex:1 0 0px}.theme--light.v-badge .v-badge__badge:after{border-color:#fff}.theme--dark.v-badge .v-badge__badge:after{border-color:#1e1e1e}.v-badge{position:relative}.v-badge,.v-badge__badge{display:inline-block;line-height:1}.v-badge__badge{border-radius:10px;color:#fff;font-size:12px;height:20px;letter-spacing:0;min-width:20px;padding:4px 6px;pointer-events:auto;position:absolute;text-align:center;text-indent:0;top:auto;transition:.3s cubic-bezier(.25,.8,.5,1);white-space:nowrap}.v-application--is-ltr .v-badge__badge{right:auto}.v-application--is-rtl .v-badge__badge{left:auto}.v-badge__badge .v-icon{color:inherit;font-size:12px;margin:0 -2px}.v-badge__badge .v-img{height:12px;width:12px}.v-badge__wrapper{flex:0 1;height:100%;left:0;pointer-events:none;position:absolute;top:0;width:100%}.v-badge--avatar .v-badge__badge{padding:0}.v-badge--avatar .v-badge__badge .v-avatar{height:20px!important;min-width:0!important;max-width:20px!important}.v-badge--bordered .v-badge__badge:after{border-radius:inherit;border-width:2px;border-style:solid;bottom:0;content:"";left:0;position:absolute;right:0;top:0;transform:scale(1.15)}.v-badge--dot .v-badge__badge{border-radius:4.5px;height:9px;min-width:0;padding:0;width:9px}.v-badge--dot .v-badge__badge:after{border-width:1.5px}.v-badge--icon .v-badge__badge{padding:4px 6px}.v-badge--inline{align-items:center;display:inline-flex;justify-content:center}.v-badge--inline .v-badge__badge,.v-badge--inline .v-badge__wrapper{position:relative}.v-badge--inline .v-badge__wrapper{margin:0 4px}.v-badge--tile .v-badge__badge{border-radius:0}.theme--light.v-card{background-color:#fff;color:rgba(0,0,0,.87)}.theme--light.v-card .v-card__subtitle,.theme--light.v-card>.v-card__text{color:rgba(0,0,0,.6)}.theme--dark.v-card{background-color:#1e1e1e;color:#fff}.theme--dark.v-card .v-card__subtitle,.theme--dark.v-card>.v-card__text{color:hsla(0,0%,100%,.7)}.v-sheet.v-card{border-radius:4px}.v-sheet.v-card:not(.v-sheet--outlined){box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.v-sheet.v-card.v-sheet--shaped{border-radius:24px 4px}.v-card{border-width:thin;display:block;max-width:100%;outline:none;text-decoration:none;transition-property:box-shadow,opacity;overflow-wrap:break-word;position:relative;white-space:normal}.v-card>.v-card__progress+:not(.v-btn):not(.v-chip),.v-card>:first-child:not(.v-btn):not(.v-chip){border-top-left-radius:inherit;border-top-right-radius:inherit}.v-card>:last-child:not(.v-btn):not(.v-chip){border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.v-card__progress{top:0;left:0;right:0;overflow:hidden}.v-card__subtitle+.v-card__text{padding-top:0}.v-card__subtitle,.v-card__text{font-size:.875rem;font-weight:400;line-height:1.375rem;letter-spacing:.0071428571em}.v-card__subtitle,.v-card__text,.v-card__title{padding:16px}.v-card__title{align-items:center;display:flex;flex-wrap:wrap;font-size:1.25rem;font-weight:500;letter-spacing:.0125em;line-height:2rem;word-break:break-all}.v-card__title+.v-card__subtitle,.v-card__title+.v-card__text{padding-top:0}.v-card__title+.v-card__subtitle{margin-top:-16px}.v-card__text{width:100%}.v-card__actions{align-items:center;display:flex;padding:8px}.v-card__actions>.v-btn.v-btn{padding:0 8px}.v-application--is-ltr .v-card__actions>.v-btn.v-btn+.v-btn{margin-left:8px}.v-application--is-ltr .v-card__actions>.v-btn.v-btn .v-icon--left{margin-left:4px}.v-application--is-ltr .v-card__actions>.v-btn.v-btn .v-icon--right{margin-right:4px}.v-application--is-rtl .v-card__actions>.v-btn.v-btn+.v-btn{margin-right:8px}.v-application--is-rtl .v-card__actions>.v-btn.v-btn .v-icon--left{margin-right:4px}.v-application--is-rtl .v-card__actions>.v-btn.v-btn .v-icon--right{margin-left:4px}.v-card--flat{box-shadow:0 0 0 0 rgba(0,0,0,.2),0 0 0 0 rgba(0,0,0,.14),0 0 0 0 rgba(0,0,0,.12)!important}.v-card--hover{cursor:pointer;transition:box-shadow .4s cubic-bezier(.25,.8,.25,1)}.v-card--hover:focus,.v-card--hover:hover{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.v-card--link,.v-card--link .v-chip{cursor:pointer}.v-card--link:focus:before{opacity:.08}.v-card--link:before{background:currentColor;bottom:0;content:"";left:0;opacity:0;pointer-events:none;position:absolute;right:0;top:0;transition:opacity .2s}.v-card--disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-card--disabled>:not(.v-card__progress){opacity:.6;transition:inherit}.v-card--loading{overflow:hidden}.v-card--raised{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.theme--light.v-progress-linear{color:rgba(0,0,0,.87)}.theme--dark.v-progress-linear{color:#fff}.v-progress-linear{background:transparent;overflow:hidden;position:relative;transition:.2s cubic-bezier(.4,0,.6,1);width:100%}.v-progress-linear__buffer{height:inherit;left:0;position:absolute;top:0;transition:inherit;width:100%;z-index:1}.v-progress-linear--reverse .v-progress-linear__buffer{left:auto;right:0}.v-progress-linear__background{bottom:0;left:0;position:absolute;top:0;transition:inherit}.v-progress-linear--reverse .v-progress-linear__background{left:auto;right:0}.v-progress-linear__content{align-items:center;display:flex;height:100%;left:0;justify-content:center;position:absolute;top:0;width:100%;z-index:2}.v-progress-linear--reverse .v-progress-linear__content{left:auto;right:0}.v-progress-linear__determinate{height:inherit;left:0;position:absolute;transition:inherit}.v-progress-linear--reverse .v-progress-linear__determinate{left:auto;right:0}.v-progress-linear .v-progress-linear__indeterminate .long,.v-progress-linear .v-progress-linear__indeterminate .short{background-color:inherit;bottom:0;height:inherit;left:0;position:absolute;right:auto;top:0;width:auto;will-change:left,right}.v-progress-linear .v-progress-linear__indeterminate--active .long{-webkit-animation-name:indeterminate-ltr;animation-name:indeterminate-ltr;-webkit-animation-duration:2.2s;animation-duration:2.2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.v-progress-linear .v-progress-linear__indeterminate--active .short{-webkit-animation-name:indeterminate-short-ltr;animation-name:indeterminate-short-ltr;-webkit-animation-duration:2.2s;animation-duration:2.2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.v-progress-linear--reverse .v-progress-linear__indeterminate .long,.v-progress-linear--reverse .v-progress-linear__indeterminate .short{left:auto;right:0}.v-progress-linear--reverse .v-progress-linear__indeterminate--active .long{-webkit-animation-name:indeterminate-rtl;animation-name:indeterminate-rtl}.v-progress-linear--reverse .v-progress-linear__indeterminate--active .short{-webkit-animation-name:indeterminate-short-rtl;animation-name:indeterminate-short-rtl}.v-progress-linear__stream{-webkit-animation:stream-ltr .25s linear infinite;animation:stream-ltr .25s linear infinite;border-color:currentColor;border-top:4px dotted;bottom:0;left:auto;right:-8px;opacity:.3;pointer-events:none;position:absolute;top:calc(50% - 2px);transition:inherit}.v-progress-linear--reverse .v-progress-linear__stream{-webkit-animation:stream-rtl .25s linear infinite;animation:stream-rtl .25s linear infinite;left:-8px;right:auto}.v-progress-linear__wrapper{overflow:hidden;position:relative;transition:inherit}.v-progress-linear--absolute,.v-progress-linear--fixed{left:0;z-index:1}.v-progress-linear--absolute{position:absolute}.v-progress-linear--fixed{position:fixed}.v-progress-linear--reactive .v-progress-linear__content{pointer-events:none}.v-progress-linear--rounded{border-radius:4px}.v-progress-linear--striped .v-progress-linear__determinate{background-image:linear-gradient(135deg,hsla(0,0%,100%,.25) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.25) 0,hsla(0,0%,100%,.25) 75%,transparent 0,transparent);background-size:40px 40px;background-repeat:repeat}.v-progress-linear--query .v-progress-linear__indeterminate--active .long{-webkit-animation-name:query-ltr;animation-name:query-ltr;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.v-progress-linear--query .v-progress-linear__indeterminate--active .short{-webkit-animation-name:query-short-ltr;animation-name:query-short-ltr;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.v-progress-linear--query.v-progress-linear--reverse .v-progress-linear__indeterminate--active .long{-webkit-animation-name:query-rtl;animation-name:query-rtl}.v-progress-linear--query.v-progress-linear--reverse .v-progress-linear__indeterminate--active .short{-webkit-animation-name:query-short-rtl;animation-name:query-short-rtl}@-webkit-keyframes indeterminate-ltr{0%{left:-90%;right:100%}60%{left:-90%;right:100%}to{left:100%;right:-35%}}@keyframes indeterminate-ltr{0%{left:-90%;right:100%}60%{left:-90%;right:100%}to{left:100%;right:-35%}}@-webkit-keyframes indeterminate-rtl{0%{left:100%;right:-90%}60%{left:100%;right:-90%}to{left:-35%;right:100%}}@keyframes indeterminate-rtl{0%{left:100%;right:-90%}60%{left:100%;right:-90%}to{left:-35%;right:100%}}@-webkit-keyframes indeterminate-short-ltr{0%{left:-200%;right:100%}60%{left:107%;right:-8%}to{left:107%;right:-8%}}@keyframes indeterminate-short-ltr{0%{left:-200%;right:100%}60%{left:107%;right:-8%}to{left:107%;right:-8%}}@-webkit-keyframes indeterminate-short-rtl{0%{left:100%;right:-200%}60%{left:-8%;right:107%}to{left:-8%;right:107%}}@keyframes indeterminate-short-rtl{0%{left:100%;right:-200%}60%{left:-8%;right:107%}to{left:-8%;right:107%}}@-webkit-keyframes query-ltr{0%{right:-90%;left:100%}60%{right:-90%;left:100%}to{right:100%;left:-35%}}@keyframes query-ltr{0%{right:-90%;left:100%}60%{right:-90%;left:100%}to{right:100%;left:-35%}}@-webkit-keyframes query-rtl{0%{right:100%;left:-90%}60%{right:100%;left:-90%}to{right:-35%;left:100%}}@keyframes query-rtl{0%{right:100%;left:-90%}60%{right:100%;left:-90%}to{right:-35%;left:100%}}@-webkit-keyframes query-short-ltr{0%{right:-200%;left:100%}60%{right:107%;left:-8%}to{right:107%;left:-8%}}@keyframes query-short-ltr{0%{right:-200%;left:100%}60%{right:107%;left:-8%}to{right:107%;left:-8%}}@-webkit-keyframes query-short-rtl{0%{right:100%;left:-200%}60%{right:-8%;left:107%}to{right:-8%;left:107%}}@keyframes query-short-rtl{0%{right:100%;left:-200%}60%{right:-8%;left:107%}to{right:-8%;left:107%}}@-webkit-keyframes stream-ltr{to{transform:translateX(-8px)}}@keyframes stream-ltr{to{transform:translateX(-8px)}}@-webkit-keyframes stream-rtl{to{transform:translateX(8px)}}@keyframes stream-rtl{to{transform:translateX(8px)}}.v-main{display:flex;flex:1 0 auto;max-width:100%;transition:.2s cubic-bezier(.4,0,.2,1)}.v-main:not([data-booted=true]){transition:none!important}.v-main__wrap{flex:1 1 auto;max-width:100%;position:relative}@-moz-document url-prefix(){@media print{.v-main{display:block}}}.v-dialog{border-radius:4px;margin:24px;overflow-y:auto;pointer-events:auto;transition:.3s cubic-bezier(.25,.8,.25,1);width:100%;z-index:inherit;box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)}.v-dialog:not(.v-dialog--fullscreen){max-height:90%}.v-dialog>*{width:100%}.v-dialog>.v-card>.v-card__title{font-size:1.25rem;font-weight:500;letter-spacing:.0125em;padding:16px 24px 10px}.v-dialog>.v-card>.v-card__subtitle,.v-dialog>.v-card>.v-card__text{padding:0 24px 20px}.v-dialog__content{align-items:center;display:flex;height:100%;justify-content:center;left:0;pointer-events:none;position:fixed;top:0;transition:.2s cubic-bezier(.25,.8,.25,1),z-index 1ms;width:100%;z-index:6;outline:none}.v-dialog__container{display:none}.v-dialog__container--attached{display:inline}.v-dialog--animated{-webkit-animation-duration:.15s;animation-duration:.15s;-webkit-animation-name:animate-dialog;animation-name:animate-dialog;-webkit-animation-timing-function:cubic-bezier(.25,.8,.25,1);animation-timing-function:cubic-bezier(.25,.8,.25,1)}.v-dialog--fullscreen{border-radius:0;margin:0;height:100%;position:fixed;overflow-y:auto;top:0;left:0}.v-dialog--fullscreen>.v-card{min-height:100%;min-width:100%;margin:0!important;padding:0!important}.v-dialog--scrollable,.v-dialog--scrollable>form{display:flex}.v-dialog--scrollable>.v-card,.v-dialog--scrollable>form>.v-card{display:flex;flex:1 1 100%;flex-direction:column;max-height:100%;max-width:100%}.v-dialog--scrollable>.v-card>.v-card__actions,.v-dialog--scrollable>.v-card>.v-card__title,.v-dialog--scrollable>form>.v-card>.v-card__actions,.v-dialog--scrollable>form>.v-card>.v-card__title{flex:0 0 auto}.v-dialog--scrollable>.v-card>.v-card__text,.v-dialog--scrollable>form>.v-card>.v-card__text{-webkit-backface-visibility:hidden;backface-visibility:hidden;flex:1 1 auto;overflow-y:auto}@-webkit-keyframes animate-dialog{0%{transform:scale(1)}50%{transform:scale(1.03)}to{transform:scale(1)}}@keyframes animate-dialog{0%{transform:scale(1)}50%{transform:scale(1.03)}to{transform:scale(1)}}.theme--light.v-overlay{color:rgba(0,0,0,.87)}.theme--dark.v-overlay{color:#fff}.v-overlay{align-items:center;border-radius:inherit;display:flex;justify-content:center;position:fixed;top:0;left:0;right:0;bottom:0;pointer-events:none;transition:.3s cubic-bezier(.25,.8,.5,1),z-index 1ms}.v-overlay__content{position:relative}.v-overlay__scrim{border-radius:inherit;bottom:0;height:100%;left:0;position:absolute;right:0;top:0;transition:inherit;width:100%;will-change:opacity}.v-overlay--absolute{position:absolute}.v-overlay--active{pointer-events:auto}.theme--light.v-navigation-drawer{background-color:#fff}.theme--light.v-navigation-drawer:not(.v-navigation-drawer--floating) .v-navigation-drawer__border{background-color:rgba(0,0,0,.12)}.theme--light.v-navigation-drawer .v-divider{border-color:rgba(0,0,0,.12)}.theme--dark.v-navigation-drawer{background-color:#363636}.theme--dark.v-navigation-drawer:not(.v-navigation-drawer--floating) .v-navigation-drawer__border{background-color:hsla(0,0%,100%,.12)}.theme--dark.v-navigation-drawer .v-divider{border-color:hsla(0,0%,100%,.12)}.v-navigation-drawer{-webkit-overflow-scrolling:touch;display:flex;flex-direction:column;left:0;max-width:100%;overflow:hidden;pointer-events:auto;top:0;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);will-change:transform;transition-property:transform,visibility,width}.v-navigation-drawer:not([data-booted=true]){transition:none!important}.v-navigation-drawer.v-navigation-drawer--right:after{left:0;right:auto}.v-navigation-drawer .v-list:not(.v-select-list){background:inherit}.v-navigation-drawer__border{position:absolute;right:0;top:0;height:100%;width:1px}.v-navigation-drawer__content{height:100%;overflow-y:auto;overflow-x:hidden}.v-navigation-drawer__image{border-radius:inherit;height:100%;position:absolute;top:0;bottom:0;z-index:-1;contain:strict;width:100%}.v-navigation-drawer__image .v-image{border-radius:inherit}.v-navigation-drawer--bottom.v-navigation-drawer--is-mobile{max-height:50%;top:auto;bottom:0;min-width:100%}.v-navigation-drawer--right{left:auto;right:0}.v-navigation-drawer--right>.v-navigation-drawer__border{right:auto;left:0}.v-navigation-drawer--absolute{z-index:1}.v-navigation-drawer--fixed{z-index:6}.v-navigation-drawer--absolute{position:absolute}.v-navigation-drawer--clipped:not(.v-navigation-drawer--temporary):not(.v-navigation-drawer--is-mobile){z-index:4}.v-navigation-drawer--fixed{position:fixed}.v-navigation-drawer--floating:after{display:none}.v-navigation-drawer--mini-variant{overflow:hidden}.v-navigation-drawer--mini-variant .v-list-item>:first-child{margin-left:0;margin-right:0}.v-navigation-drawer--mini-variant .v-list-item>:not(:first-child){position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap;display:initial}.v-navigation-drawer--mini-variant .v-list-group--no-action .v-list-group__items,.v-navigation-drawer--mini-variant .v-list-group--sub-group{display:none}.v-navigation-drawer--mini-variant.v-navigation-drawer--custom-mini-variant .v-list-item{justify-content:center}.v-navigation-drawer--temporary{z-index:7}.v-navigation-drawer--mobile{z-index:6}.v-navigation-drawer--close{visibility:hidden}.v-navigation-drawer--is-mobile:not(.v-navigation-drawer--close),.v-navigation-drawer--temporary:not(.v-navigation-drawer--close){box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.theme--light.v-text-field>.v-input__control>.v-input__slot:before{border-color:rgba(0,0,0,.42)}.theme--light.v-text-field:not(.v-input--has-state):hover>.v-input__control>.v-input__slot:before{border-color:rgba(0,0,0,.87)}.theme--light.v-text-field.v-input--is-disabled .v-input__slot:before{-o-border-image:repeating-linear-gradient(90deg,rgba(0,0,0,.38) 0,rgba(0,0,0,.38) 2px,transparent 0,transparent 4px) 1 repeat;border-image:repeating-linear-gradient(90deg,rgba(0,0,0,.38) 0,rgba(0,0,0,.38) 2px,transparent 0,transparent 4px) 1 repeat}.theme--light.v-text-field--filled>.v-input__control>.v-input__slot{background:rgba(0,0,0,.06)}.theme--light.v-text-field--filled:not(.v-input--is-focused):not(.v-input--has-state)>.v-input__control>.v-input__slot:hover{background:rgba(0,0,0,.12)}.theme--light.v-text-field--solo>.v-input__control>.v-input__slot{background:#fff}.theme--light.v-text-field--solo-inverted>.v-input__control>.v-input__slot{background:rgba(0,0,0,.06)}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot{background:#424242}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input{color:#fff}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::-webkit-input-placeholder{color:hsla(0,0%,100%,.5)}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::-moz-placeholder{color:hsla(0,0%,100%,.5)}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input:-ms-input-placeholder{color:hsla(0,0%,100%,.5)}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::-ms-input-placeholder{color:hsla(0,0%,100%,.5)}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::placeholder{color:hsla(0,0%,100%,.5)}.theme--light.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot .v-label{color:hsla(0,0%,100%,.7)}.theme--light.v-text-field--outlined:not(.v-input--is-focused):not(.v-input--has-state)>.v-input__control>.v-input__slot fieldset{color:rgba(0,0,0,.38)}.theme--light.v-text-field--outlined:not(.v-input--is-focused):not(.v-input--has-state):not(.v-input--is-disabled)>.v-input__control>.v-input__slot:hover fieldset{color:rgba(0,0,0,.86)}.theme--light.v-text-field--outlined:not(.v-input--is-focused).v-input--is-disabled>.v-input__control>.v-input__slot fieldset{color:rgba(0,0,0,.26)}.theme--dark.v-text-field>.v-input__control>.v-input__slot:before{border-color:hsla(0,0%,100%,.7)}.theme--dark.v-text-field:not(.v-input--has-state):hover>.v-input__control>.v-input__slot:before{border-color:#fff}.theme--dark.v-text-field.v-input--is-disabled .v-input__slot:before{-o-border-image:repeating-linear-gradient(90deg,hsla(0,0%,100%,.5) 0,hsla(0,0%,100%,.5) 2px,transparent 0,transparent 4px) 1 repeat;border-image:repeating-linear-gradient(90deg,hsla(0,0%,100%,.5) 0,hsla(0,0%,100%,.5) 2px,transparent 0,transparent 4px) 1 repeat}.theme--dark.v-text-field--filled>.v-input__control>.v-input__slot{background:hsla(0,0%,100%,.08)}.theme--dark.v-text-field--filled:not(.v-input--is-focused):not(.v-input--has-state)>.v-input__control>.v-input__slot:hover{background:hsla(0,0%,100%,.16)}.theme--dark.v-text-field--solo>.v-input__control>.v-input__slot{background:#1e1e1e}.theme--dark.v-text-field--solo-inverted>.v-input__control>.v-input__slot{background:hsla(0,0%,100%,.16)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot{background:#fff}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input{color:rgba(0,0,0,.87)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::-webkit-input-placeholder{color:rgba(0,0,0,.38)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::-moz-placeholder{color:rgba(0,0,0,.38)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input:-ms-input-placeholder{color:rgba(0,0,0,.38)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::-ms-input-placeholder{color:rgba(0,0,0,.38)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot input::placeholder{color:rgba(0,0,0,.38)}.theme--dark.v-text-field--solo-inverted.v-input--is-focused>.v-input__control>.v-input__slot .v-label{color:rgba(0,0,0,.6)}.theme--dark.v-text-field--outlined:not(.v-input--is-focused):not(.v-input--has-state)>.v-input__control>.v-input__slot fieldset{color:hsla(0,0%,100%,.24)}.theme--dark.v-text-field--outlined:not(.v-input--is-focused):not(.v-input--has-state):not(.v-input--is-disabled)>.v-input__control>.v-input__slot:hover fieldset{color:#fff}.theme--dark.v-text-field--outlined:not(.v-input--is-focused).v-input--is-disabled>.v-input__control>.v-input__slot fieldset{color:hsla(0,0%,100%,.16)}.v-text-field{padding-top:12px;margin-top:4px}.v-text-field input{flex:1 1 auto;line-height:20px;padding:8px 0 8px;max-width:100%;min-width:0;width:100%}.v-text-field .v-input__control,.v-text-field .v-input__slot,.v-text-field fieldset{border-radius:inherit}.v-text-field.v-input--has-state .v-input__control>.v-text-field__details>.v-counter,.v-text-field.v-input--is-disabled .v-input__control>.v-text-field__details>.v-counter,.v-text-field.v-input--is-disabled .v-input__control>.v-text-field__details>.v-messages,.v-text-field .v-input__control,.v-text-field fieldset{color:inherit}.v-text-field.v-input--dense{padding-top:0}.v-text-field.v-input--dense:not(.v-text-field--outlined) input{padding:4px 0 2px}.v-text-field.v-input--dense[type=text]::-ms-clear{display:none}.v-text-field.v-input--dense .v-input__append-inner,.v-text-field.v-input--dense .v-input__prepend-inner{margin-top:0}.v-text-field.v-input--dense:not(.v-text-field--enclosed):not(.v-text-field--full-width) .v-input__append-inner .v-input__icon>.v-icon,.v-text-field.v-input--dense:not(.v-text-field--enclosed):not(.v-text-field--full-width) .v-input__prepend-inner .v-input__icon>.v-icon{margin-top:8px}.v-text-field .v-input__append-inner,.v-text-field .v-input__prepend-inner{align-self:flex-start;display:inline-flex;margin-top:4px;line-height:1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-application--is-ltr .v-text-field .v-input__prepend-inner{margin-right:auto;padding-right:4px}.v-application--is-ltr .v-text-field .v-input__append-inner,.v-application--is-rtl .v-text-field .v-input__prepend-inner{margin-left:auto;padding-left:4px}.v-application--is-rtl .v-text-field .v-input__append-inner{margin-right:auto;padding-right:4px}.v-text-field .v-counter{white-space:nowrap}.v-application--is-ltr .v-text-field .v-counter{margin-left:8px}.v-application--is-rtl .v-text-field .v-counter{margin-right:8px}.v-text-field .v-label{max-width:90%;overflow:hidden;text-overflow:ellipsis;top:6px;white-space:nowrap;pointer-events:none}.v-application--is-ltr .v-text-field .v-label{transform-origin:top left}.v-application--is-rtl .v-text-field .v-label{transform-origin:top right}.v-text-field .v-label--active{max-width:133%;transform:translateY(-18px) scale(.75)}.v-text-field>.v-input__control>.v-input__slot{cursor:text;transition:background .3s cubic-bezier(.25,.8,.5,1)}.v-text-field>.v-input__control>.v-input__slot:after,.v-text-field>.v-input__control>.v-input__slot:before{bottom:-1px;content:"";left:0;position:absolute;transition:.3s cubic-bezier(.25,.8,.5,1);width:100%}.v-text-field>.v-input__control>.v-input__slot:before{border-color:inherit;border-style:solid;border-width:thin 0 0 0}.v-text-field>.v-input__control>.v-input__slot:after{border-color:currentColor;border-style:solid;border-width:thin 0 thin 0;transform:scaleX(0)}.v-text-field__details{display:flex;flex:1 0 auto;max-width:100%;min-height:14px;overflow:hidden}.v-text-field__prefix,.v-text-field__suffix{align-self:center;cursor:default;transition:color .3s cubic-bezier(.25,.8,.5,1);white-space:nowrap}.v-application--is-ltr .v-text-field__prefix{text-align:right;padding-right:4px}.v-application--is-rtl .v-text-field__prefix{text-align:left;padding-left:4px}.v-text-field__suffix{white-space:nowrap}.v-application--is-ltr .v-text-field__suffix{padding-left:4px}.v-application--is-rtl .v-text-field__suffix{padding-right:4px}.v-application--is-ltr .v-text-field--reverse .v-text-field__prefix{text-align:left;padding-right:0;padding-left:4px}.v-application--is-rtl .v-text-field--reverse .v-text-field__prefix{text-align:right;padding-right:4px;padding-left:0}.v-application--is-ltr .v-text-field--reverse .v-text-field__suffix{padding-left:0;padding-right:4px}.v-application--is-rtl .v-text-field--reverse .v-text-field__suffix{padding-left:4px;padding-right:0}.v-text-field>.v-input__control>.v-input__slot>.v-text-field__slot{display:flex;flex:1 1 auto;position:relative}.v-text-field:not(.v-text-field--is-booted) .v-label,.v-text-field:not(.v-text-field--is-booted) legend{transition:none}.v-text-field--filled,.v-text-field--full-width,.v-text-field--outlined{position:relative}.v-text-field--filled>.v-input__control>.v-input__slot,.v-text-field--full-width>.v-input__control>.v-input__slot,.v-text-field--outlined>.v-input__control>.v-input__slot{align-items:stretch;min-height:56px}.v-text-field--filled.v-input--dense>.v-input__control>.v-input__slot,.v-text-field--full-width.v-input--dense>.v-input__control>.v-input__slot,.v-text-field--outlined.v-input--dense>.v-input__control>.v-input__slot{min-height:52px}.v-text-field--filled.v-input--dense.v-text-field--outlined.v-text-field--filled>.v-input__control>.v-input__slot,.v-text-field--filled.v-input--dense.v-text-field--outlined>.v-input__control>.v-input__slot,.v-text-field--filled.v-input--dense.v-text-field--single-line>.v-input__control>.v-input__slot,.v-text-field--full-width.v-input--dense.v-text-field--outlined.v-text-field--filled>.v-input__control>.v-input__slot,.v-text-field--full-width.v-input--dense.v-text-field--outlined>.v-input__control>.v-input__slot,.v-text-field--full-width.v-input--dense.v-text-field--single-line>.v-input__control>.v-input__slot,.v-text-field--outlined.v-input--dense.v-text-field--outlined.v-text-field--filled>.v-input__control>.v-input__slot,.v-text-field--outlined.v-input--dense.v-text-field--outlined>.v-input__control>.v-input__slot,.v-text-field--outlined.v-input--dense.v-text-field--single-line>.v-input__control>.v-input__slot{min-height:40px}.v-text-field--outlined{border-radius:4px}.v-text-field--enclosed .v-input__append-inner,.v-text-field--enclosed .v-input__append-outer,.v-text-field--enclosed .v-input__prepend-inner,.v-text-field--enclosed .v-input__prepend-outer,.v-text-field--full-width .v-input__append-inner,.v-text-field--full-width .v-input__append-outer,.v-text-field--full-width .v-input__prepend-inner,.v-text-field--full-width .v-input__prepend-outer{margin-top:17px}.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo) .v-input__append-inner,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo) .v-input__append-outer,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo) .v-input__prepend-inner,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo) .v-input__prepend-outer,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo) .v-input__append-inner,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo) .v-input__append-outer,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo) .v-input__prepend-inner,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo) .v-input__prepend-outer{margin-top:14px}.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__append-inner,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__append-outer,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__prepend-inner,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__prepend-outer,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__append-inner,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__append-outer,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__prepend-inner,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--single-line .v-input__prepend-outer{margin-top:9px}.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__append-inner,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__append-outer,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__prepend-inner,.v-text-field--enclosed.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__prepend-outer,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__append-inner,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__append-outer,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__prepend-inner,.v-text-field--full-width.v-input--dense:not(.v-text-field--solo).v-text-field--outlined .v-input__prepend-outer{margin-top:8px}.v-text-field--filled .v-label,.v-text-field--full-width .v-label{top:18px}.v-text-field--filled .v-label--active,.v-text-field--full-width .v-label--active{transform:translateY(-6px) scale(.75)}.v-text-field--filled.v-input--dense .v-label,.v-text-field--full-width.v-input--dense .v-label{top:17px}.v-text-field--filled.v-input--dense .v-label--active,.v-text-field--full-width.v-input--dense .v-label--active{transform:translateY(-10px) scale(.75)}.v-text-field--filled.v-input--dense.v-text-field--single-line .v-label,.v-text-field--full-width.v-input--dense.v-text-field--single-line .v-label{top:11px}.v-text-field--filled{border-radius:4px 4px 0 0}.v-text-field--filled:not(.v-text-field--single-line) input{margin-top:22px}.v-text-field--filled.v-input--dense:not(.v-text-field--single-line).v-text-field--outlined input{margin-top:0}.v-text-field--filled .v-text-field__prefix,.v-text-field--filled .v-text-field__suffix{max-height:32px;margin-top:20px}.v-text-field--full-width{border-radius:0}.v-text-field--outlined .v-text-field__slot,.v-text-field--single-line .v-text-field__slot{align-items:center}.v-text-field.v-text-field--enclosed{margin:0;padding:0}.v-text-field.v-text-field--enclosed.v-text-field--single-line .v-text-field__prefix,.v-text-field.v-text-field--enclosed.v-text-field--single-line .v-text-field__suffix{margin-top:0}.v-text-field.v-text-field--enclosed:not(.v-text-field--filled) .v-progress-linear__background{display:none}.v-text-field.v-text-field--enclosed .v-text-field__details,.v-text-field.v-text-field--enclosed:not(.v-text-field--rounded)>.v-input__control>.v-input__slot{padding:0 12px}.v-text-field.v-text-field--enclosed .v-text-field__details{margin-bottom:8px}.v-application--is-ltr .v-text-field--reverse input{text-align:right}.v-application--is-rtl .v-text-field--reverse input{text-align:left}.v-application--is-ltr .v-text-field--reverse .v-label{transform-origin:top right}.v-application--is-rtl .v-text-field--reverse .v-label{transform-origin:top left}.v-text-field--reverse .v-text-field__slot,.v-text-field--reverse>.v-input__control>.v-input__slot{flex-direction:row-reverse}.v-text-field--outlined>.v-input__control>.v-input__slot:after,.v-text-field--outlined>.v-input__control>.v-input__slot:before,.v-text-field--rounded>.v-input__control>.v-input__slot:after,.v-text-field--rounded>.v-input__control>.v-input__slot:before,.v-text-field--solo>.v-input__control>.v-input__slot:after,.v-text-field--solo>.v-input__control>.v-input__slot:before{display:none}.v-text-field--outlined,.v-text-field--solo{border-radius:4px}.v-text-field--outlined{margin-bottom:16px;transition:border .3s cubic-bezier(.25,.8,.5,1)}.v-text-field--outlined .v-label{top:18px}.v-text-field--outlined .v-label--active{transform:translateY(-24px) scale(.75)}.v-text-field--outlined.v-input--dense .v-label{top:10px}.v-text-field--outlined.v-input--dense .v-label--active{transform:translateY(-16px) scale(.75)}.v-text-field--outlined fieldset{border-collapse:collapse;border-color:currentColor;border-style:solid;border-width:1px;bottom:0;left:0;pointer-events:none;position:absolute;right:0;top:-5px;transition-duration:.3s;transition-property:color,border-width;transition-timing-function:cubic-bezier(.25,.8,.25,1)}.v-application--is-ltr .v-text-field--outlined fieldset{padding-left:8px}.v-application--is-ltr .v-text-field--outlined.v-text-field--reverse fieldset,.v-application--is-rtl .v-text-field--outlined fieldset{padding-right:8px}.v-application--is-rtl .v-text-field--outlined.v-text-field--reverse fieldset{padding-left:8px}.v-text-field--outlined legend{line-height:11px;padding:0;transition:width .3s cubic-bezier(.25,.8,.5,1)}.v-application--is-ltr .v-text-field--outlined legend{text-align:left}.v-application--is-ltr .v-text-field--outlined.v-text-field--reverse legend,.v-application--is-rtl .v-text-field--outlined legend{text-align:right}.v-application--is-rtl .v-text-field--outlined.v-text-field--reverse legend{text-align:left}.v-application--is-ltr .v-text-field--outlined.v-text-field--rounded legend{margin-left:12px}.v-application--is-rtl .v-text-field--outlined.v-text-field--rounded legend{margin-right:12px}.v-text-field--outlined>.v-input__control>.v-input__slot{background:transparent}.v-text-field--outlined .v-text-field__prefix{max-height:32px}.v-text-field--outlined .v-input__append-outer,.v-text-field--outlined .v-input__prepend-outer{margin-top:18px}.v-text-field--outlined.v-input--has-state fieldset,.v-text-field--outlined.v-input--is-focused fieldset{border:2px solid currentColor}.v-text-field--rounded{border-radius:28px}.v-text-field--rounded>.v-input__control>.v-input__slot{padding:0 24px}.v-text-field--shaped{border-radius:16px 16px 0 0}.v-text-field.v-text-field--solo .v-label{top:calc(50% - 9px)}.v-text-field.v-text-field--solo .v-input__control{min-height:48px;padding:0}.v-text-field.v-text-field--solo .v-input__control input{caret-color:auto}.v-text-field.v-text-field--solo.v-input--dense>.v-input__control{min-height:38px}.v-text-field.v-text-field--solo:not(.v-text-field--solo-flat)>.v-input__control>.v-input__slot{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.v-text-field.v-text-field--solo .v-input__append-inner,.v-text-field.v-text-field--solo .v-input__prepend-inner{align-self:center;margin-top:0}.v-text-field.v-text-field--solo .v-input__append-outer,.v-text-field.v-text-field--solo .v-input__prepend-outer{margin-top:12px}.v-text-field.v-text-field--solo.v-input--dense .v-input__append-outer,.v-text-field.v-text-field--solo.v-input--dense .v-input__prepend-outer{margin-top:7px}.v-text-field.v-input--is-focused>.v-input__control>.v-input__slot:after{transform:scaleX(1)}.v-text-field.v-input--has-state>.v-input__control>.v-input__slot:before{border-color:currentColor}.theme--light.v-select .v-select__selection--comma{color:rgba(0,0,0,.87)}.theme--light.v-select .v-select__selection--disabled{color:rgba(0,0,0,.38)}.theme--dark.v-select .v-select__selection--comma,.theme--light.v-select.v-text-field--solo-inverted.v-input--is-focused .v-select__selection--comma{color:#fff}.theme--dark.v-select .v-select__selection--disabled{color:hsla(0,0%,100%,.5)}.theme--dark.v-select.v-text-field--solo-inverted.v-input--is-focused .v-select__selection--comma{color:rgba(0,0,0,.87)}.v-select{position:relative}.v-select:not(.v-select--is-multi).v-text-field--single-line .v-select__selections{flex-wrap:nowrap}.v-select>.v-input__control>.v-input__slot{cursor:pointer}.v-select .v-chip{flex:0 1 auto;margin:4px}.v-select .v-chip--selected:after{opacity:.22}.v-select .fade-transition-leave-active{position:absolute;left:0}.v-select.v-input--is-dirty ::-webkit-input-placeholder{color:transparent!important}.v-select.v-input--is-dirty ::-moz-placeholder{color:transparent!important}.v-select.v-input--is-dirty :-ms-input-placeholder{color:transparent!important}.v-select.v-input--is-dirty ::-ms-input-placeholder{color:transparent!important}.v-select.v-input--is-dirty ::placeholder{color:transparent!important}.v-select:not(.v-input--is-dirty):not(.v-input--is-focused) .v-text-field__prefix{line-height:20px;top:7px;transition:.3s cubic-bezier(.25,.8,.5,1)}.v-select.v-text-field--enclosed:not(.v-text-field--single-line):not(.v-text-field--outlined) .v-select__selections{padding-top:20px}.v-select.v-text-field--outlined:not(.v-text-field--single-line) .v-select__selections{padding:8px 0}.v-select.v-text-field--outlined:not(.v-text-field--single-line).v-input--dense .v-select__selections{padding:4px 0}.v-select.v-text-field input{flex:1 1;margin-top:0;min-width:0;pointer-events:none;position:relative}.v-select.v-select--is-menu-active .v-input__icon--append .v-icon{transform:rotate(180deg)}.v-select.v-select--chips input{margin:0}.v-select.v-select--chips .v-select__selections{min-height:42px}.v-select.v-select--chips.v-input--dense .v-select__selections{min-height:40px}.v-select.v-select--chips .v-chip--select.v-chip--active:before{opacity:.2}.v-select.v-select--chips.v-select--chips--small .v-select__selections{min-height:26px}.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--box .v-select__selections,.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--enclosed .v-select__selections{min-height:68px}.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--box.v-input--dense .v-select__selections,.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--enclosed.v-input--dense .v-select__selections{min-height:40px}.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--box.v-select--chips--small .v-select__selections,.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--enclosed.v-select--chips--small .v-select__selections{min-height:26px}.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--box.v-select--chips--small.v-input--dense .v-select__selections,.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--enclosed.v-select--chips--small.v-input--dense .v-select__selections{min-height:38px}.v-select.v-text-field--reverse .v-select__selections,.v-select.v-text-field--reverse .v-select__slot{flex-direction:row-reverse}.v-select__selections{align-items:center;display:flex;flex:1 1;flex-wrap:wrap;line-height:18px;max-width:100%;min-width:0}.v-select__selection{max-width:90%}.v-select__selection--comma{margin:7px 4px 7px 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.v-select.v-input--dense .v-select__selection--comma{margin:5px 4px 3px 0}.v-select.v-input--dense .v-chip{margin:0 4px 0 4px}.v-select__slot{position:relative;align-items:center;display:flex;max-width:100%;min-width:0;width:100%}.v-select:not(.v-text-field--single-line):not(.v-text-field--outlined) .v-select__slot>input{align-self:flex-end}.theme--light.v-input,.theme--light.v-input input,.theme--light.v-input textarea{color:rgba(0,0,0,.87)}.theme--light.v-input input::-webkit-input-placeholder,.theme--light.v-input textarea::-webkit-input-placeholder{color:rgba(0,0,0,.38)}.theme--light.v-input input::-moz-placeholder,.theme--light.v-input textarea::-moz-placeholder{color:rgba(0,0,0,.38)}.theme--light.v-input input:-ms-input-placeholder,.theme--light.v-input textarea:-ms-input-placeholder{color:rgba(0,0,0,.38)}.theme--light.v-input input::-ms-input-placeholder,.theme--light.v-input textarea::-ms-input-placeholder{color:rgba(0,0,0,.38)}.theme--light.v-input input::placeholder,.theme--light.v-input textarea::placeholder{color:rgba(0,0,0,.38)}.theme--light.v-input--is-disabled,.theme--light.v-input--is-disabled input,.theme--light.v-input--is-disabled textarea{color:rgba(0,0,0,.38)}.theme--dark.v-input,.theme--dark.v-input input,.theme--dark.v-input textarea{color:#fff}.theme--dark.v-input input::-webkit-input-placeholder,.theme--dark.v-input textarea::-webkit-input-placeholder{color:hsla(0,0%,100%,.5)}.theme--dark.v-input input::-moz-placeholder,.theme--dark.v-input textarea::-moz-placeholder{color:hsla(0,0%,100%,.5)}.theme--dark.v-input input:-ms-input-placeholder,.theme--dark.v-input textarea:-ms-input-placeholder{color:hsla(0,0%,100%,.5)}.theme--dark.v-input input::-ms-input-placeholder,.theme--dark.v-input textarea::-ms-input-placeholder{color:hsla(0,0%,100%,.5)}.theme--dark.v-input input::placeholder,.theme--dark.v-input textarea::placeholder{color:hsla(0,0%,100%,.5)}.theme--dark.v-input--is-disabled,.theme--dark.v-input--is-disabled input,.theme--dark.v-input--is-disabled textarea{color:hsla(0,0%,100%,.5)}.v-input{align-items:flex-start;display:flex;flex:1 1 auto;font-size:16px;letter-spacing:normal;max-width:100%;text-align:left}.v-input .v-progress-linear{top:calc(100% - 1px);left:0}.v-input input{max-height:32px}.v-input input:invalid,.v-input textarea:invalid{box-shadow:none}.v-input input:active,.v-input input:focus,.v-input textarea:active,.v-input textarea:focus{outline:none}.v-input .v-label{height:20px;line-height:20px}.v-input__append-outer,.v-input__prepend-outer{display:inline-flex;margin-bottom:4px;margin-top:4px;line-height:1}.v-input__append-outer .v-icon,.v-input__prepend-outer .v-icon{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-application--is-ltr .v-input__append-outer{margin-left:9px}.v-application--is-ltr .v-input__prepend-outer,.v-application--is-rtl .v-input__append-outer{margin-right:9px}.v-application--is-rtl .v-input__prepend-outer{margin-left:9px}.v-input__control{display:flex;flex-direction:column;height:auto;flex-grow:1;flex-wrap:wrap;min-width:0;width:100%}.v-input__icon{align-items:center;display:inline-flex;height:24px;flex:1 0 auto;justify-content:center;min-width:24px;width:24px}.v-input__icon--clear{border-radius:50%}.v-input__icon--clear .v-icon--disabled{visibility:hidden}.v-input__slot{align-items:center;color:inherit;display:flex;margin-bottom:8px;min-height:inherit;position:relative;transition:.3s cubic-bezier(.25,.8,.5,1);width:100%}.v-input--dense>.v-input__control>.v-input__slot{margin-bottom:4px}.v-input--is-disabled:not(.v-input--is-readonly){pointer-events:none}.v-input--is-loading>.v-input__control>.v-input__slot:after,.v-input--is-loading>.v-input__control>.v-input__slot:before{display:none}.v-input--hide-details>.v-input__control>.v-input__slot{margin-bottom:0}.v-input--has-state.error--text .v-label{-webkit-animation:v-shake .6s cubic-bezier(.25,.8,.5,1);animation:v-shake .6s cubic-bezier(.25,.8,.5,1)}.theme--light.v-label{color:rgba(0,0,0,.6)}.theme--light.v-label--is-disabled{color:rgba(0,0,0,.38)}.theme--dark.v-label{color:hsla(0,0%,100%,.7)}.theme--dark.v-label--is-disabled{color:hsla(0,0%,100%,.5)}.v-label{font-size:16px;line-height:1;min-height:8px;transition:.3s cubic-bezier(.25,.8,.5,1)}.theme--light.v-messages{color:rgba(0,0,0,.6)}.theme--dark.v-messages{color:hsla(0,0%,100%,.7)}.v-messages{flex:1 1 auto;font-size:12px;min-height:14px;min-width:1px;position:relative}.v-application--is-ltr .v-messages{text-align:left}.v-application--is-rtl .v-messages{text-align:right}.v-messages__message{line-height:12px;word-break:break-word;overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.theme--light.v-counter{color:rgba(0,0,0,.6)}.theme--dark.v-counter{color:hsla(0,0%,100%,.7)}.v-counter{flex:0 1 auto;font-size:12px;min-height:12px;line-height:12px}.v-simple-checkbox{align-self:center;line-height:normal;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer}.v-simple-checkbox--disabled{cursor:default}.theme--light.v-divider{border-color:rgba(0,0,0,.12)}.theme--dark.v-divider{border-color:hsla(0,0%,100%,.12)}.v-divider{display:block;flex:1 1 0px;max-width:100%;height:0;max-height:0;border:solid;border-width:thin 0 0 0;transition:inherit}.v-divider--inset:not(.v-divider--vertical){max-width:calc(100% - 72px)}.v-application--is-ltr .v-divider--inset:not(.v-divider--vertical){margin-left:72px}.v-application--is-rtl .v-divider--inset:not(.v-divider--vertical){margin-right:72px}.v-divider--vertical{align-self:stretch;border:solid;border-width:0 thin 0 0;display:inline-flex;height:inherit;min-height:100%;max-height:100%;max-width:0;width:0;vertical-align:text-bottom}.v-divider--vertical.v-divider--inset{margin-top:8px;min-height:0;max-height:calc(100% - 16px)}.theme--light.v-subheader{color:rgba(0,0,0,.6)}.theme--dark.v-subheader{color:hsla(0,0%,100%,.7)}.v-subheader{align-items:center;display:flex;height:48px;font-size:.875rem;font-weight:400;padding:0 16px 0 16px}.v-subheader--inset{margin-left:56px}.v-chip:not(.v-chip--outlined).accent,.v-chip:not(.v-chip--outlined).error,.v-chip:not(.v-chip--outlined).info,.v-chip:not(.v-chip--outlined).primary,.v-chip:not(.v-chip--outlined).secondary,.v-chip:not(.v-chip--outlined).success,.v-chip:not(.v-chip--outlined).warning{color:#fff}.theme--light.v-chip{border-color:rgba(0,0,0,.12);color:rgba(0,0,0,.87)}.theme--light.v-chip:not(.v-chip--active){background:#e0e0e0}.theme--light.v-chip:hover:before{opacity:.04}.theme--light.v-chip--active:before,.theme--light.v-chip--active:hover:before,.theme--light.v-chip:focus:before{opacity:.12}.theme--light.v-chip--active:focus:before{opacity:.16}.theme--dark.v-chip{border-color:hsla(0,0%,100%,.12);color:#fff}.theme--dark.v-chip:not(.v-chip--active){background:#555}.theme--dark.v-chip:hover:before{opacity:.08}.theme--dark.v-chip--active:before,.theme--dark.v-chip--active:hover:before,.theme--dark.v-chip:focus:before{opacity:.24}.theme--dark.v-chip--active:focus:before{opacity:.32}.v-chip{align-items:center;cursor:default;display:inline-flex;line-height:20px;max-width:100%;outline:none;overflow:hidden;padding:0 12px;position:relative;text-decoration:none;transition-duration:.28s;transition-property:box-shadow,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);vertical-align:middle;white-space:nowrap}.v-chip:before{background-color:currentColor;bottom:0;border-radius:inherit;content:"";left:0;opacity:0;position:absolute;pointer-events:none;right:0;top:0}.v-chip .v-avatar{height:24px!important;min-width:24px!important;width:24px!important}.v-chip .v-icon{font-size:24px}.v-application--is-ltr .v-chip .v-avatar--left,.v-application--is-ltr .v-chip .v-icon--left{margin-left:-6px;margin-right:6px}.v-application--is-ltr .v-chip .v-avatar--right,.v-application--is-ltr .v-chip .v-icon--right,.v-application--is-rtl .v-chip .v-avatar--left,.v-application--is-rtl .v-chip .v-icon--left{margin-left:6px;margin-right:-6px}.v-application--is-rtl .v-chip .v-avatar--right,.v-application--is-rtl .v-chip .v-icon--right{margin-left:-6px;margin-right:6px}.v-chip:not(.v-chip--no-color) .v-icon{color:inherit}.v-chip .v-chip__close.v-icon{font-size:18px;max-height:18px;max-width:18px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-application--is-ltr .v-chip .v-chip__close.v-icon.v-icon--right{margin-right:-4px}.v-application--is-rtl .v-chip .v-chip__close.v-icon.v-icon--right{margin-left:-4px}.v-chip .v-chip__close.v-icon:active,.v-chip .v-chip__close.v-icon:focus,.v-chip .v-chip__close.v-icon:hover{opacity:.72}.v-chip .v-chip__content{align-items:center;display:inline-flex;height:100%;max-width:100%}.v-chip--active .v-icon{color:inherit}.v-chip--link:before{transition:opacity .3s cubic-bezier(.25,.8,.5,1)}.v-chip--link:focus:before{opacity:.32}.v-chip--clickable{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-chip--clickable:active{box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.v-chip--disabled{opacity:.4;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-chip__filter{max-width:24px}.v-chip__filter.v-icon{color:inherit}.v-chip__filter.expand-x-transition-enter,.v-chip__filter.expand-x-transition-leave-active{margin:0}.v-chip--pill .v-chip__filter{margin-right:0 16px 0 0}.v-chip--pill .v-avatar{height:32px!important;width:32px!important}.v-application--is-ltr .v-chip--pill .v-avatar--left{margin-left:-12px}.v-application--is-ltr .v-chip--pill .v-avatar--right,.v-application--is-rtl .v-chip--pill .v-avatar--left{margin-right:-12px}.v-application--is-rtl .v-chip--pill .v-avatar--right{margin-left:-12px}.v-chip--label{border-radius:4px!important}.v-chip.v-chip--outlined{border-width:thin;border-style:solid}.v-chip.v-chip--outlined.v-chip--active:before{opacity:.08}.v-chip.v-chip--outlined .v-icon{color:inherit}.v-chip.v-chip--outlined.v-chip.v-chip{background-color:transparent!important}.v-chip.v-chip--selected{background:transparent}.v-chip.v-chip--selected:after{opacity:.28}.v-chip.v-size--x-small{border-radius:8px;font-size:10px;height:16px}.v-chip.v-size--small{border-radius:12px;font-size:12px;height:24px}.v-chip.v-size--default{border-radius:16px;font-size:14px;height:32px}.v-chip.v-size--large{border-radius:27px;font-size:16px;height:54px}.v-chip.v-size--x-large{border-radius:33px;font-size:18px;height:66px}.theme--light.v-slider .v-slider__thumb,.theme--light.v-slider .v-slider__track-background,.theme--light.v-slider .v-slider__track-fill{background:rgba(0,0,0,.26)}.theme--dark.v-slider .v-slider__thumb,.theme--dark.v-slider .v-slider__track-background,.theme--dark.v-slider .v-slider__track-fill{background:hsla(0,0%,100%,.2)}.v-slider{cursor:default;display:flex;align-items:center;position:relative;flex:1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-slider input{cursor:default;padding:0;width:100%;display:none}.v-slider__track-container{position:absolute;border-radius:0}.v-slider__thumb-container,.v-slider__track-background,.v-slider__track-fill{position:absolute;transition:.3s cubic-bezier(.25,.8,.5,1)}.v-slider__thumb-container{outline:none;top:50%}.v-slider__thumb-container:hover .v-slider__thumb:before{transform:scale(1)}.v-slider__thumb{width:12px;height:12px;left:-6px;top:50%;transform:translateY(-50%);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-slider__thumb,.v-slider__thumb:before{position:absolute;border-radius:50%;transition:.3s cubic-bezier(.25,.8,.5,1)}.v-slider__thumb:before{content:"";color:inherit;width:36px;height:36px;background:currentColor;opacity:.3;left:-12px;top:-12px;transform:scale(.1);pointer-events:none}.v-slider__tick,.v-slider__ticks-container{position:absolute}.v-slider__tick{opacity:0;background-color:rgba(0,0,0,.5);transition:.3s cubic-bezier(.25,.8,.5,1);border-radius:0}.v-slider__tick--filled{background-color:hsla(0,0%,100%,.5)}.v-application--is-ltr .v-slider__tick:first-child .v-slider__tick-label{transform:none}.v-application--is-rtl .v-slider__tick:first-child .v-slider__tick-label{transform:translateX(100%)}.v-application--is-ltr .v-slider__tick:last-child .v-slider__tick-label{transform:translateX(-100%)}.v-application--is-rtl .v-slider__tick:last-child .v-slider__tick-label{transform:none}.v-slider__tick-label{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}.v-slider__thumb-label-container{top:0}.v-slider__thumb-label,.v-slider__thumb-label-container{position:absolute;left:0;transition:.3s cubic-bezier(.25,.8,.25,1)}.v-slider__thumb-label{display:flex;align-items:center;justify-content:center;font-size:.75rem;color:#fff;width:32px;height:32px;border-radius:50% 50% 0;bottom:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.v-slider--horizontal{min-height:32px;margin-left:8px;margin-right:8px}.v-slider--horizontal .v-slider__track-container{width:100%;height:2px;left:0;top:50%;transform:translateY(-50%)}.v-slider--horizontal .v-slider__track-background,.v-slider--horizontal .v-slider__track-fill{height:100%}.v-slider--horizontal .v-slider__ticks-container{left:0;height:2px;width:100%}.v-application--is-ltr .v-slider--horizontal .v-slider__tick:first-child .v-slider__tick-label,.v-application--is-rtl .v-slider--horizontal .v-slider__tick:first-child .v-slider__tick-label{transform:translateX(0)}.v-application--is-ltr .v-slider--horizontal .v-slider__tick:last-child .v-slider__tick-label{transform:translateX(-100%)}.v-application--is-rtl .v-slider--horizontal .v-slider__tick:last-child .v-slider__tick-label{transform:translateX(100%)}.v-slider--horizontal .v-slider__tick .v-slider__tick-label{top:8px}.v-application--is-ltr .v-slider--horizontal .v-slider__tick .v-slider__tick-label{transform:translateX(-50%)}.v-application--is-rtl .v-slider--horizontal .v-slider__tick .v-slider__tick-label{transform:translateX(50%)}.v-slider--horizontal .v-slider__thumb-label{transform:translateY(-20%) translateY(-12px) translateX(-50%) rotate(45deg)}.v-slider--horizontal .v-slider__thumb-label>*{transform:rotate(-45deg)}.v-slider--vertical{min-height:150px;margin-top:12px;margin-bottom:12px}.v-slider--vertical .v-slider__track-container{height:100%;width:2px;left:50%;top:0;transform:translateX(-50%)}.v-slider--vertical .v-slider__track-background,.v-slider--vertical .v-slider__track-fill{width:100%}.v-slider--vertical .v-slider__thumb-container{left:50%}.v-slider--vertical .v-slider__ticks-container{top:0;width:2px;height:100%;left:50%;transform:translateX(-50%)}.v-application--is-ltr .v-slider--vertical .v-slider__tick .v-slider__tick-label,.v-application--is-ltr .v-slider--vertical .v-slider__tick:first-child .v-slider__tick-label,.v-application--is-ltr .v-slider--vertical .v-slider__tick:last-child .v-slider__tick-label{transform:translateY(-50%);left:12px}.v-application--is-rtl .v-slider--vertical .v-slider__tick .v-slider__tick-label,.v-application--is-rtl .v-slider--vertical .v-slider__tick:first-child .v-slider__tick-label,.v-application--is-rtl .v-slider--vertical .v-slider__tick:last-child .v-slider__tick-label{transform:translateY(-50%);right:12px}.v-slider--vertical .v-slider__thumb-label>*{transform:rotate(-135deg)}.v-slider__thumb-container--focused .v-slider__thumb:before{transform:scale(1)}.v-slider--active .v-slider__tick{opacity:1}.v-slider__thumb-container--active .v-slider__thumb:before{transform:scale(1.5)!important}.v-slider--disabled{pointer-events:none}.v-slider--disabled .v-slider__thumb{width:8px;height:8px;left:-4px}.v-slider--disabled .v-slider__thumb:before{display:none}.v-slider__ticks-container--always-show .v-slider__tick{opacity:1}.v-input__slider.v-input--is-readonly>.v-input__control{pointer-events:none}.v-application--is-ltr .v-input__slider .v-input__slot .v-label{margin-left:0;margin-right:12px}.v-application--is-ltr .v-input__slider--inverse-label .v-input__slot .v-label,.v-application--is-rtl .v-input__slider .v-input__slot .v-label{margin-right:0;margin-left:12px}.v-application--is-rtl .v-input__slider--inverse-label .v-input__slot .v-label{margin-left:0;margin-right:12px}.v-input__slider--vertical{align-items:center}.v-application--is-ltr .v-input__slider--vertical{flex-direction:column-reverse}.v-application--is-rtl .v-input__slider--vertical{flex-direction:column}.v-input__slider--vertical .v-input__append-outer,.v-input__slider--vertical .v-input__prepend-outer,.v-input__slider--vertical .v-input__slot{margin:0}.v-input__slider--vertical .v-messages{display:none}.v-input--has-state .v-slider__track-background{opacity:.4} \ No newline at end of file diff --git a/scripts/system/inventory/dist/favicon.ico b/scripts/system/inventory/dist/favicon.ico new file mode 100644 index 0000000000..df36fcfb72 Binary files /dev/null and b/scripts/system/inventory/dist/favicon.ico differ diff --git a/scripts/system/inventory/dist/index.html b/scripts/system/inventory/dist/index.html new file mode 100644 index 0000000000..75ddbbccb7 --- /dev/null +++ b/scripts/system/inventory/dist/index.html @@ -0,0 +1 @@ +Inventory
    \ No newline at end of file diff --git a/scripts/system/inventory/dist/inventory-a-msg.svg b/scripts/system/inventory/dist/inventory-a-msg.svg new file mode 100644 index 0000000000..480a3fdd5d --- /dev/null +++ b/scripts/system/inventory/dist/inventory-a-msg.svg @@ -0,0 +1 @@ + diff --git a/scripts/system/inventory/dist/inventory-a.svg b/scripts/system/inventory/dist/inventory-a.svg new file mode 100644 index 0000000000..c53d21722c --- /dev/null +++ b/scripts/system/inventory/dist/inventory-a.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/system/inventory/dist/inventory-i-msg.svg b/scripts/system/inventory/dist/inventory-i-msg.svg new file mode 100644 index 0000000000..95856910fa --- /dev/null +++ b/scripts/system/inventory/dist/inventory-i-msg.svg @@ -0,0 +1 @@ + diff --git a/scripts/system/inventory/dist/inventory-i.svg b/scripts/system/inventory/dist/inventory-i.svg new file mode 100644 index 0000000000..c5ec3d8cc3 --- /dev/null +++ b/scripts/system/inventory/dist/inventory-i.svg @@ -0,0 +1 @@ + diff --git a/scripts/system/inventory/dist/inventory.js b/scripts/system/inventory/dist/inventory.js new file mode 100644 index 0000000000..12ecba1546 --- /dev/null +++ b/scripts/system/inventory/dist/inventory.js @@ -0,0 +1,299 @@ +// +// inventory.js +// +// Created by kasenvr@gmail.com on 2 Apr 2020 +// Copyright 2020 Vircadia and contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* global AvatarList Clipboard console Controller Entities location Messages MyAvatar Script ScriptDiscoveryService Settings +Tablet Vec3 Window */ + +(function () { // BEGIN LOCAL_SCOPE + "use strict"; + var AppUi = Script.require('appUi'); + var ui; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + + // VARIABLES + var inventoryDataSettingString = "inventoryApp.data"; + var inventoryData; + + var inventorySettingsString = "inventoryApp.settings"; + var inventorySettings; + + var RECEIVING_ITEM_QUEUE_LIMIT = 5; + var receivingItemQueue = []; + + var NEARBY_USERS_SEARCH_RADIUS = 25; + + + // APP EVENT AND MESSAGING ROUTING + + function onWebAppEventReceived(event) { + var eventJSON = JSON.parse(event); + if (eventJSON.app === "inventory") { // This is our web app! + // print("inventory.js received a web event: " + event); + + if (eventJSON.command === "ready") { + initializeInventoryApp(); + } + + if (eventJSON.command === "web-to-script-inventory") { + receiveInventory(eventJSON.data); + } + + if (eventJSON.command === "web-to-script-settings") { + receiveSettings(eventJSON.data); + } + + if (eventJSON.command === "use-item") { + useItem(eventJSON.data); + } + + if (eventJSON.command === "share-item") { + shareItem(eventJSON.data); + } + + if (eventJSON.command === "web-to-script-request-nearby-users") { + sendNearbyUsers(); + } + + if (eventJSON.command === "web-to-script-request-receiving-item-queue") { + sendReceivingItemQueue(); + } + + if (eventJSON.command === "web-to-script-update-receiving-item-queue") { + updateReceivingItemQueue(eventJSON.data); + } + + } + } + + tablet.webEventReceived.connect(onWebAppEventReceived); + + function sendToWeb(command, data) { + var dataToSend = { + "app": "inventory", + "command": command, + "data": data + }; + + tablet.emitScriptEvent(JSON.stringify(dataToSend)); + } + + var inventoryMessagesChannel = "com.vircadia.inventory"; + + function onMessageReceived(channel, message, sender, localOnly) { + if (channel === inventoryMessagesChannel) { + var messageJSON = JSON.parse(message); + // Window.alert("Passed 0 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID); + if (messageJSON.command === "share-item" + && messageJSON.recipient === MyAvatar.sessionUUID) { // We are receiving an item. + // Window.alert("Passed 1 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID); + pushReceivedItemToQueue(sender, messageJSON.type, messageJSON.name, messageJSON.url); + } + } + // print("Message received:"); + // print("- channel: " + channel); + // print("- message: " + message); + // print("- sender: " + sender); + // print("- localOnly: " + localOnly); + } + + function sendMessage(dataToSend) { + Messages.sendMessage(inventoryMessagesChannel, JSON.stringify(dataToSend)); + } + + // END APP EVENT AND MESSAGING ROUTING + + // SEND AND RECEIVE INVENTORY STATE + + function receiveInventory(receivedInventoryData) { + inventoryData = receivedInventoryData; + saveInventory(); + } + + function sendInventory() { + sendToWeb("script-to-web-inventory", inventoryData); + } + + // END SEND AND RECEIVE INVENTORY STATE + + // SEND AND RECEIVE SETTINGS STATE + + function receiveSettings(receivedSettingsData) { + inventorySettings = receivedSettingsData; + saveSettings(); + } + + function sendSettings() { + sendToWeb("script-to-web-settings", inventorySettings); + } + + // END SEND AND RECEIVE SETTINGS STATE + + function saveInventory() { + Settings.setValue(inventoryDataSettingString, inventoryData); + } + + function loadInventory() { + inventoryData = Settings.getValue(inventoryDataSettingString); + } + + function saveSettings() { + Settings.setValue(inventorySettingsString, inventorySettings); + } + + function loadSettings() { + inventorySettings = Settings.getValue(inventorySettingsString); + } + + function pushReceivedItemToQueue(senderUUID, type, name, url) { + console.info("Receiving an item:", name, "from:", senderUUID); + var getAvatarData = AvatarList.getAvatar(senderUUID); + var senderName = getAvatarData.sessionDisplayName; + var senderDistance = Vec3.distance(MyAvatar.position, getAvatarData.position); + + var packageRequest = { + "senderUUID": senderUUID, + "senderName": senderName, + "senderDistance": senderDistance, + "data": { + "type": type, + "name": name, + "url": url + } + }; + + if (receivingItemQueue.length === RECEIVING_ITEM_QUEUE_LIMIT) { + receivingItemQueue = receivingItemQueue.slice(1, RECEIVING_ITEM_QUEUE_LIMIT); + } + + receivingItemQueue.push(packageRequest); + ui.messagesWaiting(receivingItemQueue.length > 0); + } + + function sendReceivingItemQueue() { + sendToWeb("script-to-web-receiving-item-queue", receivingItemQueue); + } + + function updateReceivingItemQueue(data) { + receivingItemQueue = data; + ui.messagesWaiting(receivingItemQueue.length > 0); + } + + function sendNearbyUsers() { + var nearbyUsers = AvatarList.getAvatarsInRange(MyAvatar.position, NEARBY_USERS_SEARCH_RADIUS); + var nearbyUsersToSend = []; + + nearbyUsers.forEach(function(user) { + var objectToWrite; + var aviDetails = AvatarList.getAvatar(user); + var aviName = aviDetails.displayName; + var aviDistance = Vec3.distance(MyAvatar.position, aviDetails.position); + // Window.alert("aviName" + aviName + "user" + user + "MyAvatar.sessionUUID" + MyAvatar.sessionUUID); + if (user !== MyAvatar.sessionUUID + || Controller.getValue(Controller.Hardware.Keyboard.Shift)) { // Don't add ourselves to the list! + objectToWrite = { "name": aviName, "distance": aviDistance, "uuid": user }; + nearbyUsersToSend.push(objectToWrite); + } + }); + + sendToWeb("script-to-web-nearby-users", nearbyUsersToSend); + } + + function useItem(item) { + + //TODO: Add animation support for avatars...? + + // Convert the item.type before checking it... + item.type = item.type.toUpperCase(); + + // Depending on the type, we decide how to load this item. + if (item.type === "SCRIPT") { + ScriptDiscoveryService.loadScript(item.url, true, false, false, true, false); + } + + if (item.type === "MODEL") { + Entities.addEntity({ + type: "Model", + position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })), + rotation: MyAvatar.orientation, + modelURL: item.url, + collisionless: true + }); + } + + if (item.type === "AVATAR") { + MyAvatar.useFullAvatarURL(item.url); + } + + if (item.type === "PLACE") { + location.handleLookupString(item.url, true); + } + + if (item.type === "JSON") { + var jsonToLoad = item.url; + if (jsonToLoad) { + if (Clipboard.importEntities(jsonToLoad)) { + Clipboard.pasteEntities( + Vec3.sum( + MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 }) + ) + ); + } + } + } + + if (item.type === "UNKNOWN") { + // We don't know how to handle this yet. + Window.alert("Unknown item type, unable to use."); + } + } + + function shareItem(data) { + data.command = "share-item"; + sendMessage(data); + } + + function initializeInventoryApp() { + sendSettings(); + sendInventory(); + sendReceivingItemQueue(); + } + + function onOpened() { + } + + function onClosed() { + } + + function startup() { + + loadInventory(); + loadSettings(); + + Messages.messageReceived.connect(onMessageReceived); + Messages.subscribe(inventoryMessagesChannel); + + ui = new AppUi({ + buttonName: "INVENTORY", + home: Script.resolvePath("index.html"), + graphicsDirectory: Script.resolvePath("./"), // Where your button icons are located + onOpened: onOpened, + onClosed: onClosed + }); + } + + startup(); + + Script.scriptEnding.connect(function () { + Messages.messageReceived.disconnect(onMessageReceived); + Messages.unsubscribe(inventoryMessagesChannel); + }); + +}()); // END LOCAL_SCOPE \ No newline at end of file diff --git a/scripts/system/inventory/dist/js/app.a3555a80.js b/scripts/system/inventory/dist/js/app.a3555a80.js new file mode 100644 index 0000000000..b902a5e4f5 --- /dev/null +++ b/scripts/system/inventory/dist/js/app.a3555a80.js @@ -0,0 +1,2 @@ +(function(e){function t(t){for(var r,s,l=t[0],n=t[1],d=t[2],u=0,m=[];u0,expression:"receivingItemQueueLength > 0"}]},[e._v(" A list of all items being received currently. ")]),i("v-card-text",{directives:[{name:"show",rawName:"v-show",value:0===e.receivingItemQueueLength,expression:"receivingItemQueueLength === 0"}]},[e._v(" There are currently no items in your inbox. ")]),i("v-card-actions",[i("v-list",{directives:[{name:"show",rawName:"v-show",value:e.receivingItemQueueLength>0,expression:"receivingItemQueueLength > 0"}],staticClass:"pt-5",attrs:{nav:"","max-width":"370"}},e._l(e.receivingItemsDialog.data.receivingItemQueue,(function(t){return i("v-list-item",{key:t.data.uuid,attrs:{"two-line":""}},[i("v-list-item-content",[i("v-list-item-title",[e._v(e._s(t.data.name))]),i("v-list-item-subtitle",[e._v("Sent by "+e._s(t.senderName))]),i("v-list-item-subtitle",[e._v("Distance: "+e._s(t.senderDistance.toFixed(1))+"m")])],1),i("v-btn",{attrs:{color:"success"},on:{click:function(i){return e.acceptReceivingItem(t)}}},[i("v-icon",[e._v("mdi-plus")])],1),i("v-btn",{attrs:{text:"",color:"red"},on:{click:function(i){return e.removeReceivingItem(t.data.uuid)}}},[i("v-icon",[e._v("mdi-minus")])],1)],1)})),1)],1)],1)],1),i("v-dialog",{attrs:{"max-width":"290"},model:{value:e.removeDialogStore.show,callback:function(t){e.$set(e.removeDialogStore,"show",t)},expression:"removeDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Remove Item")]),i("v-card-text",[e._v(" Are you sure you want to delete this item from your inventory? ")]),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"blue"},on:{click:function(t){e.removeDialogStore.show=!1}}},[e._v(" No ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.removeDialogStore.show=!1,e.removeItem(e.$store.state.removeDialog.uuid)}}},[e._v(" Yes ")])],1)],1)],1),i("v-dialog",{attrs:{"max-width":"290"},model:{value:e.removeFolderDialogStore.show,callback:function(t){e.$set(e.removeFolderDialogStore,"show",t)},expression:"removeFolderDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Remove Folder")]),i("v-card-text",[e._v(" Are you sure you want to delete this folder "),i("b",[e._v("and")]),e._v(" all items within from your inventory? ")]),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"blue"},on:{click:function(t){e.removeFolderDialogStore.show=!1}}},[e._v(" No ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.removeFolderDialogStore.show=!1,e.removeFolder(e.$store.state.removeFolderDialog.uuid)}}},[e._v(" Yes ")])],1)],1)],1),i("v-dialog",{attrs:{"max-width":"380"},model:{value:e.editDialogStore.show,callback:function(t){e.$set(e.editDialogStore,"show",t)},expression:"editDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Edit Item")]),i("v-form",{ref:"editForm",attrs:{"lazy-validation":!1},model:{value:e.editDialogStore.valid,callback:function(t){e.$set(e.editDialogStore,"valid",t)},expression:"editDialogStore.valid"}},[i("v-select",{staticClass:"my-2",attrs:{items:e.$store.state.supportedItemTypes,rules:[function(e){return!!e||"Type is required."}],label:"Item Type",outlined:""},model:{value:e.editDialogStore.data.type,callback:function(t){e.$set(e.editDialogStore.data,"type",t)},expression:"editDialogStore.data.type"}}),i("v-text-field",{staticClass:"px-2",attrs:{label:"Name",rules:[function(e){return!!e||"Name is required."}],required:""},model:{value:e.editDialogStore.data.name,callback:function(t){e.$set(e.editDialogStore.data,"name",t)},expression:"editDialogStore.data.name"}}),i("v-select",{staticClass:"my-2",attrs:{items:e.folderList,"item-text":"name","item-value":"uuid",label:"Folder",outlined:""},model:{value:e.editDialogStore.data.folder,callback:function(t){e.$set(e.editDialogStore.data,"folder",t)},expression:"editDialogStore.data.folder"}}),i("v-text-field",{staticClass:"px-2",attrs:{label:"URL",rules:[function(e){return!!e||"URL is required."}],required:""},model:{value:e.editDialogStore.data.url,callback:function(t){e.$set(e.editDialogStore.data,"url",t)},expression:"editDialogStore.data.url"}}),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.editDialogStore.show=!1}}},[e._v(" Cancel ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"blue",disabled:!e.$store.state.editDialog.valid},on:{click:function(t){e.editDialogStore.show=!1,e.editItem(e.$store.state.editDialog.uuid)}}},[e._v(" Done ")])],1)],1)],1)],1),i("v-dialog",{attrs:{"max-width":"380"},model:{value:e.editFolderDialogStore.show,callback:function(t){e.$set(e.editFolderDialogStore,"show",t)},expression:"editFolderDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Edit Folder")]),i("v-form",{ref:"editFolderForm",attrs:{"lazy-validation":!1},model:{value:e.editFolderDialogStore.valid,callback:function(t){e.$set(e.editFolderDialogStore,"valid",t)},expression:"editFolderDialogStore.valid"}},[i("v-text-field",{staticClass:"px-2",attrs:{label:"Name",rules:[function(e){return!!e||"Name is required."}],required:""},model:{value:e.editFolderDialogStore.data.name,callback:function(t){e.$set(e.editFolderDialogStore.data,"name",t)},expression:"editFolderDialogStore.data.name"}}),i("v-select",{staticClass:"my-2",attrs:{items:e.folderList,"item-text":"name","item-value":"uuid",label:"Parent Folder",outlined:""},model:{value:e.editFolderDialogStore.data.folder,callback:function(t){e.$set(e.editFolderDialogStore.data,"folder",t)},expression:"editFolderDialogStore.data.folder"}}),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.editFolderDialogStore.show=!1}}},[e._v(" Cancel ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"blue",disabled:!e.$store.state.editFolderDialog.valid},on:{click:function(t){e.editFolderDialogStore.show=!1,e.editFolder(e.$store.state.editFolderDialog.data.uuid)}}},[e._v(" Done ")])],1)],1)],1)],1),i("v-dialog",{attrs:{"max-width":"380"},model:{value:e.createFolderDialogStore.show,callback:function(t){e.$set(e.createFolderDialogStore,"show",t)},expression:"createFolderDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Create Folder")]),i("v-card-text",[e._v(" Enter the name of the folder. ")]),i("v-form",{ref:"createFolderForm",attrs:{"lazy-validation":!1},model:{value:e.createFolderDialogStore.valid,callback:function(t){e.$set(e.createFolderDialogStore,"valid",t)},expression:"createFolderDialogStore.valid"}},[i("v-text-field",{staticClass:"px-2",attrs:{label:"Name",rules:[function(e){return!!e||"Name is required."}],required:""},model:{value:e.createFolderDialogStore.data.name,callback:function(t){e.$set(e.createFolderDialogStore.data,"name",t)},expression:"createFolderDialogStore.data.name"}}),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.createFolderDialogStore.show=!1}}},[e._v(" Cancel ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"blue",disabled:!e.$store.state.createFolderDialog.valid},on:{click:function(t){e.createFolderDialogStore.show=!1,e.createFolder(e.$store.state.createFolderDialog.data.name)}}},[e._v(" Create ")])],1)],1)],1)],1),i("v-dialog",{attrs:{"max-width":"380"},model:{value:e.addDialogStore.show,callback:function(t){e.$set(e.addDialogStore,"show",t)},expression:"addDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Add Item")]),i("v-form",{ref:"addForm",attrs:{"lazy-validation":!1},model:{value:e.addDialogStore.valid,callback:function(t){e.$set(e.addDialogStore,"valid",t)},expression:"addDialogStore.valid"}},[i("v-card-text",[e._v(" Enter the name of the item. ")]),i("v-text-field",{staticClass:"px-2",attrs:{label:"Name",rules:[function(e){return!!e||"Name is required."}],required:""},model:{value:e.addDialogStore.data.name,callback:function(t){e.$set(e.addDialogStore.data,"name",t)},expression:"addDialogStore.data.name"}}),i("v-card-text",[e._v(" Select a folder (optional). ")]),i("v-select",{staticClass:"my-2",attrs:{items:e.folderList,label:"Folder",outlined:"","item-text":"name","item-value":"uuid"},model:{value:e.addDialogStore.data.folder,callback:function(t){e.$set(e.addDialogStore.data,"folder",t)},expression:"addDialogStore.data.folder"}}),i("v-card-text",[e._v(" Enter the URL of the item. ")]),i("v-text-field",{staticClass:"px-2",attrs:{label:"URL",rules:[function(e){return!!e||"URL is required."}],required:""},model:{value:e.addDialogStore.data.url,callback:function(t){e.$set(e.addDialogStore.data,"url",t)},expression:"addDialogStore.data.url"}}),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.addDialogStore.show=!1}}},[e._v(" Cancel ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"blue",disabled:!e.$store.state.addDialog.valid},on:{click:function(t){e.addDialogStore.show=!1,e.addItem(e.$store.state.addDialog.data.name,e.$store.state.addDialog.data.folder,e.$store.state.addDialog.data.url)}}},[e._v(" Add ")])],1)],1)],1)],1),i("v-dialog",{attrs:{"max-width":"380",persistent:""},model:{value:e.receiveDialogStore.show,callback:function(t){e.$set(e.receiveDialogStore,"show",t)},expression:"receiveDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Receiving Item")]),i("v-card-text",[i("b",[e._v(e._s(e.$store.state.receiveDialog.data.userDisplayName)+" sent you an item.")]),e._v(" "),i("br"),i("i",{staticClass:"caption"},[e._v("User UUID: "+e._s(e.$store.state.receiveDialog.data.userUUID))])]),i("v-form",{ref:"receiveForm",attrs:{"lazy-validation":!1},model:{value:e.receiveDialogStore.valid,callback:function(t){e.$set(e.receiveDialogStore,"valid",t)},expression:"receiveDialogStore.valid"}},[i("v-text-field",{staticClass:"px-2",attrs:{label:"Type",rules:[function(e){return!!e||"Type is required."}],required:""},model:{value:e.receiveDialogStore.data.type,callback:function(t){e.$set(e.receiveDialogStore.data,"type",t)},expression:"receiveDialogStore.data.type"}}),i("v-text-field",{staticClass:"px-2",attrs:{label:"Name",rules:[function(e){return!!e||"Name is required."}],required:""},model:{value:e.receiveDialogStore.data.name,callback:function(t){e.$set(e.receiveDialogStore.data,"name",t)},expression:"receiveDialogStore.data.name"}}),i("v-card-text",[e._v(" Select a folder (optional). ")]),i("v-select",{staticClass:"my-2",attrs:{items:e.folderList,label:"Folder",outlined:"","item-text":"name","item-value":"uuid"},model:{value:e.receiveDialogStore.data.folder,callback:function(t){e.$set(e.receiveDialogStore.data,"folder",t)},expression:"receiveDialogStore.data.folder"}}),i("v-text-field",{staticClass:"px-2",attrs:{label:"URL",rules:[function(e){return!!e||"URL is required."}],required:""},model:{value:e.receiveDialogStore.data.url,callback:function(t){e.$set(e.receiveDialogStore.data,"url",t)},expression:"receiveDialogStore.data.url"}}),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.receiveDialogStore.show=!1}}},[e._v(" Reject ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"blue",disabled:!e.$store.state.receiveDialog.valid},on:{click:function(t){e.receiveDialogStore.show=!1,e.confirmItemReceipt()}}},[e._v(" Accept ")])],1)],1)],1)],1),i("v-dialog",{attrs:{"max-width":"380",persistent:""},model:{value:e.shareDialogStore.show,callback:function(t){e.$set(e.shareDialogStore,"show",t)},expression:"shareDialogStore.show"}},[i("v-card",[i("v-card-title",{staticClass:"headline"},[e._v("Share Item")]),i("v-card-text",[e._v(" Select a user to send this item to. ")]),i("v-form",{ref:"shareForm",staticClass:"px-2",attrs:{"lazy-validation":!1},model:{value:e.shareDialogStore.valid,callback:function(t){e.$set(e.shareDialogStore,"valid",t)},expression:"shareDialogStore.valid"}},[i("v-select",{attrs:{items:e.nearbyUsers,"item-value":"uuid",rules:[function(e){return!!e||"A recipient is required"}],label:"Nearby Users",required:""},scopedSlots:e._u([{key:"item",fn:function(t){return[i("i",{staticStyle:{color:"grey","margin-right":"5px"}},[e._v(e._s(t.item.distance.toFixed(1))+"m")]),e._v(" "+e._s(t.item.name)+" ")]}},{key:"selection",fn:function(t){return[i("i",{staticStyle:{color:"grey","margin-right":"5px"}},[e._v(e._s(t.item.distance.toFixed(1))+"m")]),e._v(" "+e._s(t.item.name)+" ")]}}]),model:{value:e.shareDialogStore.data.recipient,callback:function(t){e.$set(e.shareDialogStore.data,"recipient",t)},expression:"shareDialogStore.data.recipient"}}),i("v-text-field",{staticClass:"px-2",attrs:{label:"URL",rules:[function(e){return!!e||"URL is required."}],required:""},model:{value:e.shareDialogStore.data.url,callback:function(t){e.$set(e.shareDialogStore.data,"url",t)},expression:"shareDialogStore.data.url"}}),i("v-card-actions",[i("v-btn",{staticClass:"px-3",attrs:{color:"red"},on:{click:function(t){e.shareDialogStore.show=!1}}},[e._v(" Cancel ")]),i("v-spacer"),i("v-btn",{staticClass:"px-3",attrs:{color:"blue",disabled:!e.$store.state.shareDialog.valid},on:{click:function(t){e.shareDialogStore.show=!1,e.shareItem(e.$store.state.shareDialog.data.uuid)}}},[e._v(" Send ")])],1)],1)],1)],1)],1)},s=[],l=(i("99af"),i("4160"),i("a15b"),i("a434"),i("b0c0"),i("d3b7"),i("ac1f"),i("3ca3"),i("466d"),i("159b"),i("ddb0"),i("2b3d"),new a["a"]),n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("draggable",{attrs:{group:e.options,list:e.itemsForIterator,handle:".handle"}},e._l(e.itemsForIterator,(function(t){return i("v-item-group",{key:t.uuid},[t.items?i("v-list-group",{staticClass:"top-level-folder",scopedSlots:e._u([{key:"activator",fn:function(){return[i("v-list-item",{staticClass:"mx-auto",attrs:{"one-line":"","max-width":"344",outlined:""}},[i("v-icon",{staticClass:"folder-icon",attrs:{color:"teal"}},[e._v("mdi-folder-settings")]),e._v(" "+e._s(t.name)+" ")],1)]},proxy:!0}],null,!0)},[i("div",{staticClass:"text-center my-2"},[i("v-btn",{staticClass:"mx-1 folder-button",attrs:{medium:"",tile:"",color:"purple"},on:{click:function(i){e.editFolderDialogStore.data.uuid=t.uuid,e.editFolderDialogStore.data.name=t.name,e.editFolderDialogStore.show=!0}}},[i("v-icon",[e._v("mdi-pencil")])],1),i("v-btn",{staticClass:"mx-1 folder-button",attrs:{medium:"",tile:"",color:"red"},on:{click:function(i){e.removeFolderDialogStore.show=!0,e.removeFolderDialogStore.uuid=t.uuid}}},[i("v-icon",[e._v("mdi-minus")])],1),i("v-menu",{attrs:{bottom:"",left:""},scopedSlots:e._u([{key:"activator",fn:function(t){var r=t.on;return[i("v-btn",e._g({staticClass:"mx-1 folder-button",attrs:{medium:"",tile:"",color:"blue"}},r),[i("v-icon",[e._v("mdi-sort")])],1)]}}],null,!0)},[i("v-list",{attrs:{color:"grey darken-3"}},[i("v-list-item",{on:{click:function(i){return e.sortFolder(t.uuid,"az")}}},[i("v-list-item-title",[e._v("A-Z")]),i("v-list-item-action",[i("v-icon",{attrs:{large:""}},[e._v("mdi-sort-alphabetical-ascending")])],1)],1),i("v-list-item",{on:{click:function(i){return e.sortFolder(t.uuid,"za")}}},[i("v-list-item-title",[e._v("Z-A")]),i("v-list-item-action",[i("v-icon",{attrs:{large:""}},[e._v("mdi-sort-alphabetical-descending")])],1)],1)],1)],1)],1),i("v-container",{attrs:{fluid:""}},[i("itemiterator",{key:t.uuid,attrs:{itemsForIterator:t.items}})],1)],1):i("v-list-item",{staticClass:"mx-auto draggable-card",attrs:{"one-line":"","max-width":"344",outlined:""}},[i("div",{staticClass:"handle pa-2"},[i("v-icon",{attrs:{color:"orange darken-2"}},[e._v("mdi-square-medium-outline")])],1),i("v-list-item-content",{staticClass:"pb-1 pt-2 pl-4"},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.settings.displayDensity.size>0,expression:"settings.displayDensity.size > 0"}],staticClass:"overline",staticStyle:{"font-size":"0.825rem !important"}},[e._v(e._s(t.type))]),i("v-list-item-title",{staticClass:"subtitle-1 mb-1"},[e._v(e._s(t.name))]),i("v-list-item-subtitle",{directives:[{name:"show",rawName:"v-show",value:2==e.settings.displayDensity.size,expression:"settings.displayDensity.size == 2"}]},[e._v(e._s(t.url))])],1),i("v-menu",{attrs:{bottom:"",left:""},scopedSlots:e._u([{key:"activator",fn:function(r){var a=r.on;return[i("v-btn",e._g({directives:[{name:"show",rawName:"v-show",value:e.settings.displayDensity.size>=1,expression:"settings.displayDensity.size >= 1"}],style:{backgroundColor:e.getIconColor(t.type)},attrs:{medium:"",fab:"",dark:""}},a),[i("v-icon",[e._v(e._s(e.getIcon(t.type)))])],1),i("v-btn",e._g({directives:[{name:"show",rawName:"v-show",value:e.settings.displayDensity.size<1,expression:"settings.displayDensity.size < 1"}],style:{backgroundColor:e.getIconColor(t.type)},attrs:{small:"",fab:"",dark:""}},a),[i("v-icon",[e._v(e._s(e.getIcon(t.type)))])],1)]}}],null,!0)},[i("v-list",{attrs:{color:"grey darken-3"}},[i("v-list-item",{on:{click:function(i){return e.sendEvent("use-item",{type:t.type,url:t.url})}}},[i("v-list-item-title",[e._v("Use")]),i("v-list-item-action",[i("v-icon",[e._v("mdi-play")])],1)],1),i("v-list-item",{on:{click:function(i){e.editDialogStore.show=!0,e.editDialogStore.uuid=t.uuid,e.editDialogStore.data.type=t.type.toUpperCase(),e.editDialogStore.data.folder=null,e.editDialogStore.data.name=t.name,e.editDialogStore.data.url=t.url}}},[i("v-list-item-title",[e._v("Edit")]),i("v-list-item-action",[i("v-icon",[e._v("mdi-pencil")])],1)],1),i("v-list-item",{on:{click:function(i){e.shareDialogStore.show=!0,e.shareDialogStore.data.url=t.url,e.shareDialogStore.data.uuid=t.uuid}}},[i("v-list-item-title",[e._v("Share")]),i("v-list-item-action",[i("v-icon",[e._v("mdi-share")])],1)],1),i("v-list-item",{attrs:{color:"red darken-1"},on:{click:function(i){e.removeDialogStore.show=!0,e.removeDialogStore.uuid=t.uuid}}},[i("v-list-item-title",[e._v("Remove")]),i("v-list-item-action",[i("v-icon",[e._v("mdi-minus")])],1)],1)],1)],1)],1)],1)})),1)},d=[],c=(i("4e827"),i("310e")),u=i.n(c),m={name:"itemiterator",components:{draggable:u.a},props:["itemsForIterator"],data:function(){return{settings:{}}},created:function(){this.settings=this.$store.state.settings},computed:{options:function(){return{name:"column-item",pull:!0,put:!0}},settingsChanged:function(){return this.$store.state.settings},addDialogStore:{get:function(){return this.$store.state.addDialog},set:function(e){this.$store.commit("mutate",{property:"addDialog",with:e})}},editDialogStore:{get:function(){return this.$store.state.editDialog},set:function(e){this.$store.commit("mutate",{property:"editDialog",with:e})}},editFolderDialogStore:{get:function(){return this.$store.state.editFolderDialog},set:function(e){this.$store.commit("mutate",{property:"editFolderDialog",with:e})}},shareDialogStore:{get:function(){return this.$store.state.shareDialog},set:function(e){this.$store.commit("mutate",{property:"shareDialog",with:e})}},removeFolderDialogStore:{get:function(){return this.$store.state.removeFolderDialog},set:function(e){this.$store.commit("mutate",{property:"removeFolderDialog",with:e})}},removeDialogStore:{get:function(){return this.$store.state.removeDialog},set:function(e){this.$store.commit("mutate",{property:"removeDialog",with:e})}}},watch:{settingsChanged:function(e,t){console.info("Settings previous value:",t),e&&(this.settings=e)}},methods:{sendEvent:function(e,t){l.$emit(e,t)},getIcon:function(e){var t;return e=e.toUpperCase(),t=this.$store.state.iconType[e]?this.$store.state.iconType[e].icon:this.$store.state.iconType.UNKNOWN.icon,t},getIconColor:function(e){var t;return e=e.toUpperCase(),t=this.$store.state.iconType[e]?this.$store.state.iconType[e].color:this.$store.state.iconType.UNKNOWN.color,t},sortFolder:function(e,t){var i=this.searchForItem(e);i&&("az"===t&&i.returnedItem.items.sort((function(e,t){var i=e.name.toUpperCase(),r=t.name.toUpperCase();return ir?1:0})),"za"===t&&i.returnedItem.items.sort((function(e,t){var i=e.name.toUpperCase(),r=t.name.toUpperCase();return i>r?-1:i0)return this.recursiveSingularSearch(e,t[i].items)}}}},v=m,g=i("2877"),p=i("6544"),h=i.n(p),f=i("8336"),D=i("a523"),S=i("132d"),y=i("604c"),b=i("8860"),G=i("56b0"),I=i("da13"),F=i("1800"),w=i("5d23"),x=i("e449"),$=Object(g["a"])(v,n,d,!1,null,null,null),_=$.exports;function k(){return"undefined"===typeof EventBridge}h()($,{VBtn:f["a"],VContainer:D["a"],VIcon:S["a"],VItemGroup:y["b"],VList:b["a"],VListGroup:G["a"],VListItem:I["a"],VListItemAction:F["a"],VListItemContent:w["a"],VListItemSubtitle:w["b"],VListItemTitle:w["c"],VMenu:x["a"]}),k()||EventBridge.scriptEventReceived.connect((function(e){if(e=JSON.parse(e),"inventory"===e.app)switch(e.command){case"script-to-web-inventory":r.receiveInventory(e.data);break;case"script-to-web-receiving-item-queue":r.receiveReceivingItemQueue(e.data);break;case"script-to-web-nearby-users":r.receiveNearbyUsers(e.data);break;case"script-to-web-settings":r.receiveSettings(e.data);break}})),l.$on("use-item",(function(e){r.useItem(e.type,e.url)}));var C={name:"App",components:{itemiterator:_},data:function(){return{receivingItemsDialog:{show:!1,data:{receivingItemQueue:[{senderUUID:"SENDERUUIDLOL",senderName:"WHOISTHIS1",senderDistance:2.5,data:{type:"script",name:"This Is A Real Script",url:"https://butwhythough.com/lol.js",uuid:"This Is A Real Script"}},{senderUUID:"TEST2SENDERUUID",senderName:"WHOTHISBE2",senderDistance:1.22,data:{type:"script",name:"REALLYLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",url:"https://butwhythough.com/looool.js",uuid:"REALLYLONNGGGGGGGG"}}]}},folderList:[],recursiveFolderHoldingList:[],nearbyUsers:[{name:"Who",uuid:"{4131531653652562}",distance:5},{name:"Is",uuid:"{4131531653756756576543652562}",distance:3.23},{name:"This?",uuid:"{4131531676575653652562}",distance:1}],sortBy:"alphabetical",settings:{displayDensity:{size:1,labels:["List","Compact","Large"]}},appVersion:"2.0.2",darkTheme:!0,drawer:!1,disabledProp:!0}},created:function(){r=this,this.$vuetify.theme.dark=this.darkTheme,this.sendAppMessage("ready","")},methods:{createUUID:function(){for(var e=[],t="0123456789abcdef",i=0;i<36;i++)e[i]=t.substr(Math.floor(16*Math.random()),1);e[14]="4",e[19]=t.substr(3&e[19]|8,1),e[8]=e[13]=e[18]=e[23]="-";var r=e.join("");return r},pushToItems:function(e,t,i,r,a){var o;o=null!=a?a:this.createUUID(),this.$store.commit("pushToItems",{type:e,name:t,folder:i,url:r,uuid:o}),null!==i&&"No Folder"!==i&&this.moveItem(o,i)},checkFileType:function(e){var t=null;switch(e){case".fbx":t="MODEL";break;case".gltf":t="MODEL";break;case".glb":t="MODEL";break;case".js":t="SCRIPT";break;case".fst":t="AVATAR";break;case".json":t="JSON";break;default:t="UNKNOWN"}return t},checkItemType:function(e){var t=null;return e=e.toUpperCase(),this.$store.state.supportedItemTypes.forEach((function(i){i===e&&(t=i)})),null===t&&(t="UNKNOWN"),t},createFolder:function(e){this.$store.commit("pushToItems",{name:e,folder:"No Folder",items:[],uuid:this.createUUID()}),this.createFolderDialogStore.data.name=null},editFolder:function(e){var t=this.searchForItem(e);t&&(t.returnedItem.name=this.$store.state.editFolderDialog.data.name,null!==this.$store.state.editFolderDialog.data.folder&&"No Change"!==this.$store.state.editFolderDialog.data.folder&&(t.returnedItem.folder!==this.$store.state.editFolderDialog.data.folder&&"No Folder"!==this.$store.state.editFolderDialog.data.folder?this.moveFolder(e,this.$store.state.editFolderDialog.data.folder):"No Folder"===this.$store.state.editFolderDialog.data.folder&&this.moveFolder(e,"top")))},addItem:function(e,t,i){var r,a=this.detectFileType(i);r=null==a||null==a[0]?"unknown":this.checkFileType(a[0]),this.pushToItems(r,e,t,i,null),this.addDialogStore.data.name=null,this.addDialogStore.data.folder=null,this.addDialogStore.data.url=null},detectFileType:function(e){var t=/\.[0-9a-z]+$/i,i=e.match(t);if(null==i||null==i[0]){var r=/\.[0-9a-z]+$/i,a=new URL(e);i=a.pathname.match(r)}else null!=i&&null!=i[0]||(i=null);return i},removeItem:function(e){var t=this.searchForItem(e);t.parentArray.splice(t.iteration,1)},removeFolder:function(e){var t=this.searchForItem(e);t.parentArray.splice(t.iteration,1)},editItem:function(e){var t,i=this.searchForItem(e);i.returnedItem.type=this.checkItemType(this.$store.state.editDialog.data.type),i.returnedItem.name=this.$store.state.editDialog.data.name,i.returnedItem.folder=this.$store.state.editDialog.data.folder,i.returnedItem.url=this.$store.state.editDialog.data.url;for(var r=0;r0){var a=this.recursiveSingularSearch(e,t[i].items);if(null!==a)return a}}return null},recursiveFolderPopulate:function(e,t){for(var i=0;ir?1:0})),"za"===t.sort&&e.items.sort((function(e,t){var i=e.name.toUpperCase(),r=t.name.toUpperCase();return i>r?-1:i\r\n\r\n\r\n\r\n\r\n\r\n\r\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('v-app',[_c('v-app-bar',{attrs:{\"app\":\"\"}},[_c('v-app-bar-nav-icon',{on:{\"click\":function($event){_vm.drawer = true}}}),_c('v-toolbar-title',[_vm._v(\"Inventory\")]),_c('v-spacer'),_c('v-badge',{staticClass:\"mx-5\",attrs:{\"bordered\":\"\",\"color\":\"primary\",\"value\":_vm.receivingItemQueueLength,\"content\":_vm.receivingItemQueueLength,\"overlap\":\"\"}},[_c('v-btn',{attrs:{\"small\":\"\",\"color\":\"red\",\"fab\":\"\"},on:{\"click\":function($event){_vm.receivingItemsDialog.show = true; _vm.sendAppMessage('web-to-script-request-receiving-item-queue', '')}}},[_c('v-icon',[_vm._v(\" mdi-tray-full \")])],1)],1),_c('v-menu',{attrs:{\"bottom\":\"\",\"left\":\"\"},scopedSlots:_vm._u([{key:\"activator\",fn:function(ref){\nvar on = ref.on;\nreturn [_c('v-btn',_vm._g({attrs:{\"large\":\"\",\"color\":\"primary\"}},on),[_c('h4',[_vm._v(\"Sort\")])])]}}])},[_c('v-list',{attrs:{\"color\":\"grey darken-3\"}},[_c('v-list-item',{on:{\"click\":function($event){return _vm.sortTopInventory('az')}}},[_c('v-list-item-title',[_vm._v(\"A-Z\")]),_c('v-list-item-action',[_c('v-icon',{attrs:{\"large\":\"\"}},[_vm._v(\"mdi-sort-alphabetical-ascending\")])],1)],1),_c('v-list-item',{on:{\"click\":function($event){return _vm.sortTopInventory('za')}}},[_c('v-list-item-title',[_vm._v(\"Z-A\")]),_c('v-list-item-action',[_c('v-icon',{attrs:{\"large\":\"\"}},[_vm._v(\"mdi-sort-alphabetical-descending\")])],1)],1)],1)],1)],1),_c('v-navigation-drawer',{attrs:{\"fixed\":\"\",\"temporary\":\"\"},model:{value:(_vm.drawer),callback:function ($$v) {_vm.drawer=$$v},expression:\"drawer\"}},[_c('v-list',{staticClass:\"pt-5\",attrs:{\"nav\":\"\"}},[_c('v-list-item-group',[_c('v-slider',{attrs:{\"tick-labels\":_vm.settings.displayDensity.labels,\"max\":2,\"step\":\"1\",\"ticks\":\"always\",\"tick-size\":\"3\"},model:{value:(_vm.settings.displayDensity.size),callback:function ($$v) {_vm.$set(_vm.settings.displayDensity, \"size\", $$v)},expression:\"settings.displayDensity.size\"}}),_c('v-list-item',{on:{\"click\":function($event){_vm.addDialogStore.show = true; _vm.getFolderList('add');}}},[_c('v-list-item-icon',[_c('v-icon',[_vm._v(\"mdi-plus\")])],1),_c('v-list-item-title',[_vm._v(\"Add Item\")])],1),_c('v-list-item',{on:{\"click\":function($event){_vm.createFolderDialogStore.show = true}}},[_c('v-list-item-icon',[_c('v-icon',[_vm._v(\"mdi-folder-plus\")])],1),_c('v-list-item-title',[_vm._v(\"Create Folder\")])],1),_c('p',{staticClass:\"app-version\"},[_vm._v(\"Version \"+_vm._s(_vm.appVersion))])],1)],1)],1),_c('v-content',[_c('v-container',{attrs:{\"fluid\":\"\"}},[_c('v-col',{staticClass:\"py-1 column-item\",attrs:{\"cols\":\"12\",\"sm\":\"6\",\"md\":\"4\",\"lg\":\"3\"}},[_c('itemiterator',{attrs:{\"itemsForIterator\":this.$store.state.items}})],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\"},model:{value:(_vm.receivingItemsDialog.show),callback:function ($$v) {_vm.$set(_vm.receivingItemsDialog, \"show\", $$v)},expression:\"receivingItemsDialog.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Item Inbox\")]),_c('v-card-text',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.receivingItemQueueLength > 0),expression:\"receivingItemQueueLength > 0\"}]},[_vm._v(\" A list of all items being received currently. \")]),_c('v-card-text',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.receivingItemQueueLength === 0),expression:\"receivingItemQueueLength === 0\"}]},[_vm._v(\" There are currently no items in your inbox. \")]),_c('v-card-actions',[_c('v-list',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.receivingItemQueueLength > 0),expression:\"receivingItemQueueLength > 0\"}],staticClass:\"pt-5\",attrs:{\"nav\":\"\",\"max-width\":\"370\"}},_vm._l((_vm.receivingItemsDialog.data.receivingItemQueue),function(item){return _c('v-list-item',{key:item.data.uuid,attrs:{\"two-line\":\"\"}},[_c('v-list-item-content',[_c('v-list-item-title',[_vm._v(_vm._s(item.data.name))]),_c('v-list-item-subtitle',[_vm._v(\"Sent by \"+_vm._s(item.senderName))]),_c('v-list-item-subtitle',[_vm._v(\"Distance: \"+_vm._s(item.senderDistance.toFixed(1))+\"m\")])],1),_c('v-btn',{attrs:{\"color\":\"success\"},on:{\"click\":function($event){return _vm.acceptReceivingItem(item)}}},[_c('v-icon',[_vm._v(\"mdi-plus\")])],1),_c('v-btn',{attrs:{\"text\":\"\",\"color\":\"red\"},on:{\"click\":function($event){return _vm.removeReceivingItem(item.data.uuid)}}},[_c('v-icon',[_vm._v(\"mdi-minus\")])],1)],1)}),1)],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"290\"},model:{value:(_vm.removeDialogStore.show),callback:function ($$v) {_vm.$set(_vm.removeDialogStore, \"show\", $$v)},expression:\"removeDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Remove Item\")]),_c('v-card-text',[_vm._v(\" Are you sure you want to delete this item from your inventory? \")]),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\"},on:{\"click\":function($event){_vm.removeDialogStore.show = false}}},[_vm._v(\" No \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.removeDialogStore.show = false; _vm.removeItem(_vm.$store.state.removeDialog.uuid);}}},[_vm._v(\" Yes \")])],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"290\"},model:{value:(_vm.removeFolderDialogStore.show),callback:function ($$v) {_vm.$set(_vm.removeFolderDialogStore, \"show\", $$v)},expression:\"removeFolderDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Remove Folder\")]),_c('v-card-text',[_vm._v(\" Are you sure you want to delete this folder \"),_c('b',[_vm._v(\"and\")]),_vm._v(\" all items within from your inventory? \")]),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\"},on:{\"click\":function($event){_vm.removeFolderDialogStore.show = false}}},[_vm._v(\" No \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.removeFolderDialogStore.show = false; _vm.removeFolder(_vm.$store.state.removeFolderDialog.uuid);}}},[_vm._v(\" Yes \")])],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\"},model:{value:(_vm.editDialogStore.show),callback:function ($$v) {_vm.$set(_vm.editDialogStore, \"show\", $$v)},expression:\"editDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Edit Item\")]),_c('v-form',{ref:\"editForm\",attrs:{\"lazy-validation\":false},model:{value:(_vm.editDialogStore.valid),callback:function ($$v) {_vm.$set(_vm.editDialogStore, \"valid\", $$v)},expression:\"editDialogStore.valid\"}},[_c('v-select',{staticClass:\"my-2\",attrs:{\"items\":_vm.$store.state.supportedItemTypes,\"rules\":[function (v) { return !!v || 'Type is required.'; }],\"label\":\"Item Type\",\"outlined\":\"\"},model:{value:(_vm.editDialogStore.data.type),callback:function ($$v) {_vm.$set(_vm.editDialogStore.data, \"type\", $$v)},expression:\"editDialogStore.data.type\"}}),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"Name\",\"rules\":[function (v) { return !!v || 'Name is required.'; }],\"required\":\"\"},model:{value:(_vm.editDialogStore.data.name),callback:function ($$v) {_vm.$set(_vm.editDialogStore.data, \"name\", $$v)},expression:\"editDialogStore.data.name\"}}),_c('v-select',{staticClass:\"my-2\",attrs:{\"items\":_vm.folderList,\"item-text\":\"name\",\"item-value\":\"uuid\",\"label\":\"Folder\",\"outlined\":\"\"},model:{value:(_vm.editDialogStore.data.folder),callback:function ($$v) {_vm.$set(_vm.editDialogStore.data, \"folder\", $$v)},expression:\"editDialogStore.data.folder\"}}),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"URL\",\"rules\":[function (v) { return !!v || 'URL is required.'; }],\"required\":\"\"},model:{value:(_vm.editDialogStore.data.url),callback:function ($$v) {_vm.$set(_vm.editDialogStore.data, \"url\", $$v)},expression:\"editDialogStore.data.url\"}}),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.editDialogStore.show = false}}},[_vm._v(\" Cancel \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\",\"disabled\":!_vm.$store.state.editDialog.valid},on:{\"click\":function($event){_vm.editDialogStore.show = false; _vm.editItem(_vm.$store.state.editDialog.uuid);}}},[_vm._v(\" Done \")])],1)],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\"},model:{value:(_vm.editFolderDialogStore.show),callback:function ($$v) {_vm.$set(_vm.editFolderDialogStore, \"show\", $$v)},expression:\"editFolderDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Edit Folder\")]),_c('v-form',{ref:\"editFolderForm\",attrs:{\"lazy-validation\":false},model:{value:(_vm.editFolderDialogStore.valid),callback:function ($$v) {_vm.$set(_vm.editFolderDialogStore, \"valid\", $$v)},expression:\"editFolderDialogStore.valid\"}},[_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"Name\",\"rules\":[function (v) { return !!v || 'Name is required.'; }],\"required\":\"\"},model:{value:(_vm.editFolderDialogStore.data.name),callback:function ($$v) {_vm.$set(_vm.editFolderDialogStore.data, \"name\", $$v)},expression:\"editFolderDialogStore.data.name\"}}),_c('v-select',{staticClass:\"my-2\",attrs:{\"items\":_vm.folderList,\"item-text\":\"name\",\"item-value\":\"uuid\",\"label\":\"Parent Folder\",\"outlined\":\"\"},model:{value:(_vm.editFolderDialogStore.data.folder),callback:function ($$v) {_vm.$set(_vm.editFolderDialogStore.data, \"folder\", $$v)},expression:\"editFolderDialogStore.data.folder\"}}),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.editFolderDialogStore.show = false}}},[_vm._v(\" Cancel \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\",\"disabled\":!_vm.$store.state.editFolderDialog.valid},on:{\"click\":function($event){_vm.editFolderDialogStore.show = false; _vm.editFolder(_vm.$store.state.editFolderDialog.data.uuid);}}},[_vm._v(\" Done \")])],1)],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\"},model:{value:(_vm.createFolderDialogStore.show),callback:function ($$v) {_vm.$set(_vm.createFolderDialogStore, \"show\", $$v)},expression:\"createFolderDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Create Folder\")]),_c('v-card-text',[_vm._v(\" Enter the name of the folder. \")]),_c('v-form',{ref:\"createFolderForm\",attrs:{\"lazy-validation\":false},model:{value:(_vm.createFolderDialogStore.valid),callback:function ($$v) {_vm.$set(_vm.createFolderDialogStore, \"valid\", $$v)},expression:\"createFolderDialogStore.valid\"}},[_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"Name\",\"rules\":[function (v) { return !!v || 'Name is required.'; }],\"required\":\"\"},model:{value:(_vm.createFolderDialogStore.data.name),callback:function ($$v) {_vm.$set(_vm.createFolderDialogStore.data, \"name\", $$v)},expression:\"createFolderDialogStore.data.name\"}}),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.createFolderDialogStore.show = false}}},[_vm._v(\" Cancel \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\",\"disabled\":!_vm.$store.state.createFolderDialog.valid},on:{\"click\":function($event){_vm.createFolderDialogStore.show = false; _vm.createFolder(_vm.$store.state.createFolderDialog.data.name)}}},[_vm._v(\" Create \")])],1)],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\"},model:{value:(_vm.addDialogStore.show),callback:function ($$v) {_vm.$set(_vm.addDialogStore, \"show\", $$v)},expression:\"addDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Add Item\")]),_c('v-form',{ref:\"addForm\",attrs:{\"lazy-validation\":false},model:{value:(_vm.addDialogStore.valid),callback:function ($$v) {_vm.$set(_vm.addDialogStore, \"valid\", $$v)},expression:\"addDialogStore.valid\"}},[_c('v-card-text',[_vm._v(\" Enter the name of the item. \")]),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"Name\",\"rules\":[function (v) { return !!v || 'Name is required.'; }],\"required\":\"\"},model:{value:(_vm.addDialogStore.data.name),callback:function ($$v) {_vm.$set(_vm.addDialogStore.data, \"name\", $$v)},expression:\"addDialogStore.data.name\"}}),_c('v-card-text',[_vm._v(\" Select a folder (optional). \")]),_c('v-select',{staticClass:\"my-2\",attrs:{\"items\":_vm.folderList,\"label\":\"Folder\",\"outlined\":\"\",\"item-text\":\"name\",\"item-value\":\"uuid\"},model:{value:(_vm.addDialogStore.data.folder),callback:function ($$v) {_vm.$set(_vm.addDialogStore.data, \"folder\", $$v)},expression:\"addDialogStore.data.folder\"}}),_c('v-card-text',[_vm._v(\" Enter the URL of the item. \")]),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"URL\",\"rules\":[function (v) { return !!v || 'URL is required.'; }],\"required\":\"\"},model:{value:(_vm.addDialogStore.data.url),callback:function ($$v) {_vm.$set(_vm.addDialogStore.data, \"url\", $$v)},expression:\"addDialogStore.data.url\"}}),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.addDialogStore.show = false}}},[_vm._v(\" Cancel \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\",\"disabled\":!_vm.$store.state.addDialog.valid},on:{\"click\":function($event){_vm.addDialogStore.show = false; _vm.addItem(_vm.$store.state.addDialog.data.name, _vm.$store.state.addDialog.data.folder, _vm.$store.state.addDialog.data.url)}}},[_vm._v(\" Add \")])],1)],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\",\"persistent\":\"\"},model:{value:(_vm.receiveDialogStore.show),callback:function ($$v) {_vm.$set(_vm.receiveDialogStore, \"show\", $$v)},expression:\"receiveDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Receiving Item\")]),_c('v-card-text',[_c('b',[_vm._v(_vm._s(_vm.$store.state.receiveDialog.data.userDisplayName)+\" sent you an item.\")]),_vm._v(\" \"),_c('br'),_c('i',{staticClass:\"caption\"},[_vm._v(\"User UUID: \"+_vm._s(_vm.$store.state.receiveDialog.data.userUUID))])]),_c('v-form',{ref:\"receiveForm\",attrs:{\"lazy-validation\":false},model:{value:(_vm.receiveDialogStore.valid),callback:function ($$v) {_vm.$set(_vm.receiveDialogStore, \"valid\", $$v)},expression:\"receiveDialogStore.valid\"}},[_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"Type\",\"rules\":[function (v) { return !!v || 'Type is required.'; }],\"required\":\"\"},model:{value:(_vm.receiveDialogStore.data.type),callback:function ($$v) {_vm.$set(_vm.receiveDialogStore.data, \"type\", $$v)},expression:\"receiveDialogStore.data.type\"}}),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"Name\",\"rules\":[function (v) { return !!v || 'Name is required.'; }],\"required\":\"\"},model:{value:(_vm.receiveDialogStore.data.name),callback:function ($$v) {_vm.$set(_vm.receiveDialogStore.data, \"name\", $$v)},expression:\"receiveDialogStore.data.name\"}}),_c('v-card-text',[_vm._v(\" Select a folder (optional). \")]),_c('v-select',{staticClass:\"my-2\",attrs:{\"items\":_vm.folderList,\"label\":\"Folder\",\"outlined\":\"\",\"item-text\":\"name\",\"item-value\":\"uuid\"},model:{value:(_vm.receiveDialogStore.data.folder),callback:function ($$v) {_vm.$set(_vm.receiveDialogStore.data, \"folder\", $$v)},expression:\"receiveDialogStore.data.folder\"}}),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"URL\",\"rules\":[function (v) { return !!v || 'URL is required.'; }],\"required\":\"\"},model:{value:(_vm.receiveDialogStore.data.url),callback:function ($$v) {_vm.$set(_vm.receiveDialogStore.data, \"url\", $$v)},expression:\"receiveDialogStore.data.url\"}}),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.receiveDialogStore.show = false}}},[_vm._v(\" Reject \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\",\"disabled\":!_vm.$store.state.receiveDialog.valid},on:{\"click\":function($event){_vm.receiveDialogStore.show = false; _vm.confirmItemReceipt();}}},[_vm._v(\" Accept \")])],1)],1)],1)],1),_c('v-dialog',{attrs:{\"max-width\":\"380\",\"persistent\":\"\"},model:{value:(_vm.shareDialogStore.show),callback:function ($$v) {_vm.$set(_vm.shareDialogStore, \"show\", $$v)},expression:\"shareDialogStore.show\"}},[_c('v-card',[_c('v-card-title',{staticClass:\"headline\"},[_vm._v(\"Share Item\")]),_c('v-card-text',[_vm._v(\" Select a user to send this item to. \")]),_c('v-form',{ref:\"shareForm\",staticClass:\"px-2\",attrs:{\"lazy-validation\":false},model:{value:(_vm.shareDialogStore.valid),callback:function ($$v) {_vm.$set(_vm.shareDialogStore, \"valid\", $$v)},expression:\"shareDialogStore.valid\"}},[_c('v-select',{attrs:{\"items\":_vm.nearbyUsers,\"item-value\":\"uuid\",\"rules\":[function (v) { return !!v || 'A recipient is required'; }],\"label\":\"Nearby Users\",\"required\":\"\"},scopedSlots:_vm._u([{key:\"item\",fn:function(data){return [_c('i',{staticStyle:{\"color\":\"grey\",\"margin-right\":\"5px\"}},[_vm._v(_vm._s(data.item.distance.toFixed(1))+\"m\")]),_vm._v(\" \"+_vm._s(data.item.name)+\" \")]}},{key:\"selection\",fn:function(data){return [_c('i',{staticStyle:{\"color\":\"grey\",\"margin-right\":\"5px\"}},[_vm._v(_vm._s(data.item.distance.toFixed(1))+\"m\")]),_vm._v(\" \"+_vm._s(data.item.name)+\" \")]}}]),model:{value:(_vm.shareDialogStore.data.recipient),callback:function ($$v) {_vm.$set(_vm.shareDialogStore.data, \"recipient\", $$v)},expression:\"shareDialogStore.data.recipient\"}}),_c('v-text-field',{staticClass:\"px-2\",attrs:{\"label\":\"URL\",\"rules\":[function (v) { return !!v || 'URL is required.'; }],\"required\":\"\"},model:{value:(_vm.shareDialogStore.data.url),callback:function ($$v) {_vm.$set(_vm.shareDialogStore.data, \"url\", $$v)},expression:\"shareDialogStore.data.url\"}}),_c('v-card-actions',[_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"red\"},on:{\"click\":function($event){_vm.shareDialogStore.show = false}}},[_vm._v(\" Cancel \")]),_c('v-spacer'),_c('v-btn',{staticClass:\"px-3\",attrs:{\"color\":\"blue\",\"disabled\":!_vm.$store.state.shareDialog.valid},on:{\"click\":function($event){_vm.shareDialogStore.show = false; _vm.shareItem(_vm.$store.state.shareDialog.data.uuid);}}},[_vm._v(\" Send \")])],1)],1)],1)],1)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","/*\r\n event-bus.js\r\n\r\n Created by Kalila L. on 21 May 2020.\r\n Copyright 2020 Vircadia and contributors.\r\n \r\n Distributed under the Apache License, Version 2.0.\r\n See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html\r\n*/\r\n\r\nimport Vue from 'vue';\r\nexport const EventBus = new Vue();","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('draggable',{attrs:{\"group\":_vm.options,\"list\":_vm.itemsForIterator,\"handle\":\".handle\"}},_vm._l((_vm.itemsForIterator),function(item){return _c('v-item-group',{key:item.uuid},[(!item.items)?_c('v-list-item',{staticClass:\"mx-auto draggable-card\",attrs:{\"one-line\":\"\",\"max-width\":\"344\",\"outlined\":\"\"}},[_c('div',{staticClass:\"handle pa-2\"},[_c('v-icon',{attrs:{\"color\":\"orange darken-2\"}},[_vm._v(\"mdi-square-medium-outline\")])],1),_c('v-list-item-content',{staticClass:\"pb-1 pt-2 pl-4\"},[_c('div',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.settings.displayDensity.size > 0),expression:\"settings.displayDensity.size > 0\"}],staticClass:\"overline\",staticStyle:{\"font-size\":\"0.825rem !important\"}},[_vm._v(_vm._s(item.type))]),_c('v-list-item-title',{staticClass:\"subtitle-1 mb-1\"},[_vm._v(_vm._s(item.name))]),_c('v-list-item-subtitle',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.settings.displayDensity.size == 2),expression:\"settings.displayDensity.size == 2\"}]},[_vm._v(_vm._s(item.url))])],1),_c('v-menu',{attrs:{\"bottom\":\"\",\"left\":\"\"},scopedSlots:_vm._u([{key:\"activator\",fn:function(ref){\nvar on = ref.on;\nreturn [_c('v-btn',_vm._g({directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.settings.displayDensity.size >= 1),expression:\"settings.displayDensity.size >= 1\"}],style:({backgroundColor: (_vm.getIconColor(item.type)) }),attrs:{\"medium\":\"\",\"fab\":\"\",\"dark\":\"\"}},on),[_c('v-icon',[_vm._v(_vm._s(_vm.getIcon(item.type)))])],1),_c('v-btn',_vm._g({directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.settings.displayDensity.size < 1),expression:\"settings.displayDensity.size < 1\"}],style:({backgroundColor: (_vm.getIconColor(item.type)) }),attrs:{\"small\":\"\",\"fab\":\"\",\"dark\":\"\"}},on),[_c('v-icon',[_vm._v(_vm._s(_vm.getIcon(item.type)))])],1)]}}],null,true)},[_c('v-list',{attrs:{\"color\":\"grey darken-3\"}},[_c('v-list-item',{on:{\"click\":function($event){return _vm.sendEvent('use-item', { 'type': item.type, 'url': item.url })}}},[_c('v-list-item-title',[_vm._v(\"Use\")]),_c('v-list-item-action',[_c('v-icon',[_vm._v(\"mdi-play\")])],1)],1),_c('v-list-item',{on:{\"click\":function($event){_vm.editDialogStore.show = true; \n _vm.editDialogStore.uuid = item.uuid;\n _vm.editDialogStore.data.type = item.type.toUpperCase();\n _vm.editDialogStore.data.folder = null;\n _vm.editDialogStore.data.name = item.name;\n _vm.editDialogStore.data.url = item.url;}}},[_c('v-list-item-title',[_vm._v(\"Edit\")]),_c('v-list-item-action',[_c('v-icon',[_vm._v(\"mdi-pencil\")])],1)],1),_c('v-list-item',{on:{\"click\":function($event){_vm.shareDialogStore.show = true; \n _vm.shareDialogStore.data.url = item.url; \n _vm.shareDialogStore.data.uuid = item.uuid;}}},[_c('v-list-item-title',[_vm._v(\"Share\")]),_c('v-list-item-action',[_c('v-icon',[_vm._v(\"mdi-share\")])],1)],1),_c('v-list-item',{attrs:{\"color\":\"red darken-1\"},on:{\"click\":function($event){_vm.removeDialogStore.show = true; \n _vm.removeDialogStore.uuid = item.uuid;}}},[_c('v-list-item-title',[_vm._v(\"Remove\")]),_c('v-list-item-action',[_c('v-icon',[_vm._v(\"mdi-minus\")])],1)],1)],1)],1)],1):_c('v-list-group',{staticClass:\"top-level-folder\",scopedSlots:_vm._u([{key:\"activator\",fn:function(){return [_c('v-list-item',{staticClass:\"mx-auto\",attrs:{\"one-line\":\"\",\"max-width\":\"344\",\"outlined\":\"\"}},[_c('v-icon',{staticClass:\"folder-icon\",attrs:{\"color\":\"teal\"}},[_vm._v(\"mdi-folder-settings\")]),_vm._v(\" \"+_vm._s(item.name)+\" \")],1)]},proxy:true}],null,true)},[_c('div',{staticClass:\"text-center my-2\"},[_c('v-btn',{staticClass:\"mx-1 folder-button\",attrs:{\"medium\":\"\",\"tile\":\"\",\"color\":\"purple\"},on:{\"click\":function($event){_vm.editFolderDialogStore.data.uuid = item.uuid;\n _vm.editFolderDialogStore.data.name = item.name;\n _vm.editFolderDialogStore.show = true;}}},[_c('v-icon',[_vm._v(\"mdi-pencil\")])],1),_c('v-btn',{staticClass:\"mx-1 folder-button\",attrs:{\"medium\":\"\",\"tile\":\"\",\"color\":\"red\"},on:{\"click\":function($event){_vm.removeFolderDialogStore.show = true; \n _vm.removeFolderDialogStore.uuid = item.uuid;}}},[_c('v-icon',[_vm._v(\"mdi-minus\")])],1),_c('v-menu',{attrs:{\"bottom\":\"\",\"left\":\"\"},scopedSlots:_vm._u([{key:\"activator\",fn:function(ref){\n var on = ref.on;\nreturn [_c('v-btn',_vm._g({staticClass:\"mx-1 folder-button\",attrs:{\"medium\":\"\",\"tile\":\"\",\"color\":\"blue\"}},on),[_c('v-icon',[_vm._v(\"mdi-sort\")])],1)]}}],null,true)},[_c('v-list',{attrs:{\"color\":\"grey darken-3\"}},[_c('v-list-item',{on:{\"click\":function($event){return _vm.sortFolder(item.uuid, 'az');}}},[_c('v-list-item-title',[_vm._v(\"A-Z\")]),_c('v-list-item-action',[_c('v-icon',{attrs:{\"large\":\"\"}},[_vm._v(\"mdi-sort-alphabetical-ascending\")])],1)],1),_c('v-list-item',{on:{\"click\":function($event){return _vm.sortFolder(item.uuid, 'za');}}},[_c('v-list-item-title',[_vm._v(\"Z-A\")]),_c('v-list-item-action',[_c('v-icon',{attrs:{\"large\":\"\"}},[_vm._v(\"mdi-sort-alphabetical-descending\")])],1)],1)],1)],1)],1),_c('v-container',{attrs:{\"fluid\":\"\"}},[_c('itemiterator',{key:item.uuid,attrs:{\"itemsForIterator\":item.items}})],1)],1)],1)}),1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\r\n\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ItemIterator.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ItemIterator.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./ItemIterator.vue?vue&type=template&id=366e0b2f&\"\nimport script from \"./ItemIterator.vue?vue&type=script&lang=js&\"\nexport * from \"./ItemIterator.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports\n\n/* vuetify-loader */\nimport installComponents from \"!../../node_modules/vuetify-loader/lib/runtime/installComponents.js\"\nimport { VBtn } from 'vuetify/lib/components/VBtn';\nimport { VContainer } from 'vuetify/lib/components/VGrid';\nimport { VIcon } from 'vuetify/lib/components/VIcon';\nimport { VItemGroup } from 'vuetify/lib/components/VItemGroup';\nimport { VList } from 'vuetify/lib/components/VList';\nimport { VListGroup } from 'vuetify/lib/components/VList';\nimport { VListItem } from 'vuetify/lib/components/VList';\nimport { VListItemAction } from 'vuetify/lib/components/VList';\nimport { VListItemContent } from 'vuetify/lib/components/VList';\nimport { VListItemSubtitle } from 'vuetify/lib/components/VList';\nimport { VListItemTitle } from 'vuetify/lib/components/VList';\nimport { VMenu } from 'vuetify/lib/components/VMenu';\ninstallComponents(component, {VBtn,VContainer,VIcon,VItemGroup,VList,VListGroup,VListItem,VListItemAction,VListItemContent,VListItemSubtitle,VListItemTitle,VMenu})\n","import mod from \"-!../node_modules/cache-loader/dist/cjs.js??ref--12-0!../node_modules/thread-loader/dist/cjs.js!../node_modules/babel-loader/lib/index.js!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../node_modules/cache-loader/dist/cjs.js??ref--12-0!../node_modules/thread-loader/dist/cjs.js!../node_modules/babel-loader/lib/index.js!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./App.vue?vue&type=template&id=07483036&\"\nimport script from \"./App.vue?vue&type=script&lang=js&\"\nexport * from \"./App.vue?vue&type=script&lang=js&\"\nimport style0 from \"./App.vue?vue&type=style&index=0&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports\n\n/* vuetify-loader */\nimport installComponents from \"!../node_modules/vuetify-loader/lib/runtime/installComponents.js\"\nimport { VApp } from 'vuetify/lib/components/VApp';\nimport { VAppBar } from 'vuetify/lib/components/VAppBar';\nimport { VAppBarNavIcon } from 'vuetify/lib/components/VAppBar';\nimport { VBadge } from 'vuetify/lib/components/VBadge';\nimport { VBtn } from 'vuetify/lib/components/VBtn';\nimport { VCard } from 'vuetify/lib/components/VCard';\nimport { VCardActions } from 'vuetify/lib/components/VCard';\nimport { VCardText } from 'vuetify/lib/components/VCard';\nimport { VCardTitle } from 'vuetify/lib/components/VCard';\nimport { VCol } from 'vuetify/lib/components/VGrid';\nimport { VContainer } from 'vuetify/lib/components/VGrid';\nimport { VContent } from 'vuetify/lib/components/VContent';\nimport { VDialog } from 'vuetify/lib/components/VDialog';\nimport { VForm } from 'vuetify/lib/components/VForm';\nimport { VIcon } from 'vuetify/lib/components/VIcon';\nimport { VList } from 'vuetify/lib/components/VList';\nimport { VListItem } from 'vuetify/lib/components/VList';\nimport { VListItemAction } from 'vuetify/lib/components/VList';\nimport { VListItemContent } from 'vuetify/lib/components/VList';\nimport { VListItemGroup } from 'vuetify/lib/components/VList';\nimport { VListItemIcon } from 'vuetify/lib/components/VList';\nimport { VListItemSubtitle } from 'vuetify/lib/components/VList';\nimport { VListItemTitle } from 'vuetify/lib/components/VList';\nimport { VMenu } from 'vuetify/lib/components/VMenu';\nimport { VNavigationDrawer } from 'vuetify/lib/components/VNavigationDrawer';\nimport { VSelect } from 'vuetify/lib/components/VSelect';\nimport { VSlider } from 'vuetify/lib/components/VSlider';\nimport { VSpacer } from 'vuetify/lib/components/VGrid';\nimport { VTextField } from 'vuetify/lib/components/VTextField';\nimport { VToolbarTitle } from 'vuetify/lib/components/VToolbar';\ninstallComponents(component, {VApp,VAppBar,VAppBarNavIcon,VBadge,VBtn,VCard,VCardActions,VCardText,VCardTitle,VCol,VContainer,VContent,VDialog,VForm,VIcon,VList,VListItem,VListItemAction,VListItemContent,VListItemGroup,VListItemIcon,VListItemSubtitle,VListItemTitle,VMenu,VNavigationDrawer,VSelect,VSlider,VSpacer,VTextField,VToolbarTitle})\n","/*\r\n vuetify.js\r\n\r\n Created by Kalila L. on 7 Apr 2020\r\n Copyright 2020 Vircadia and contributors.\r\n \r\n Distributed under the Apache License, Version 2.0.\r\n See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html\r\n*/\r\n\r\nimport Vue from 'vue';\r\nimport Vuetify from 'vuetify/lib';\r\n\r\nVue.use(Vuetify);\r\n\r\nexport default new Vuetify({\r\n});\r\n","/*\r\n store.js\r\n\r\n Created by Kalila L. on 16 Apr 2020.\r\n Copyright 2020 Vircadia and contributors.\r\n \r\n Distributed under the Apache License, Version 2.0.\r\n See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html\r\n*/\r\n\r\nimport Vue from 'vue';\r\nimport Vuex from 'vuex';\r\n\r\nVue.use(Vuex);\r\n\r\nexport const store = new Vuex.Store({\r\n devtools: true,\r\n state: {\r\n items: [\r\n // This is test data and is primarily used for in browser development.\r\n {\r\n \"type\": \"script\",\r\n \"name\": \"VRGrabScale\",\r\n \"url\": \"https://gooawefaweawfgle.com/vr.js\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"54254354353\"\r\n },\r\n {\r\n \"name\": \"Test Folder\",\r\n \"folder\": \"No Folder\",\r\n \"items\": [\r\n {\r\n \"name\": \"inception1\",\r\n \"folder\": \"Test Folder\",\r\n \"items\": [\r\n {\r\n \"name\": \"inception2\",\r\n \"folder\": \"Test Folder\",\r\n \"items\": [\r\n {\r\n \"type\": \"script\",\r\n \"name\": \"itemincepted\",\r\n \"url\": \"https://googfdafsgaergale.com/vr.js\",\r\n \"folder\": \"FolderWithinAFolder\",\r\n \"uuid\": \"hkjkjhkjk\",\r\n },\r\n ],\r\n \"uuid\": \"adsfa32\"\r\n },\r\n ],\r\n \"uuid\": \"s4g4sg\"\r\n },\r\n ],\r\n \"uuid\": \"sdfsdf\",\r\n },\r\n {\r\n \"type\": \"script\",\r\n \"name\": \"VRGrabScale\",\r\n \"url\": \"https://googfdafsgaergale.com/vr.js\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"54hgfhgf254354353\",\r\n },\r\n {\r\n \"type\": \"script\",\r\n \"name\": \"TEST\",\r\n \"url\": \"https://gooadfdagle.com/vr.js\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"542rfwat4t5fsddf4354353\",\r\n },\r\n {\r\n \"type\": \"json\",\r\n \"name\": \"TESTJSON\",\r\n \"url\": \"https://gooadfdagle.com/vr.json\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"542rfwat4t54354353\",\r\n },\r\n {\r\n \"type\": \"script\",\r\n \"name\": \"TESTLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\",\r\n \"url\": \"https://googfdaffle.com/vrLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.js\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"5425ggsrg45354353\",\r\n },\r\n {\r\n \"type\": \"whatttype\",\r\n \"name\": \"BrokenIcon\",\r\n \"url\": \"https://googfdaffle.com/vrLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.js\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"5425ggsrg4fdaffdff535asdasd4353\",\r\n },\r\n {\r\n \"type\": \"avatar\",\r\n \"name\": \"AVI\",\r\n \"url\": \"https://googlfadfe.com/vr.fst\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"542gregg45s3g4354353\",\r\n },\r\n {\r\n \"type\": \"avatar\",\r\n \"name\": \"AVI\",\r\n \"url\": \"https://googlefdaf.com/vr.fst\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"5420798-087-54354353\",\r\n },\r\n {\r\n \"type\": \"model\",\r\n \"name\": \"3D MODEL\",\r\n \"url\": \"https://googlee.com/vr.fbx\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"54254354980-7667jt353\",\r\n },\r\n {\r\n \"type\": \"place\",\r\n \"name\": \"PLACE DOMAIN\",\r\n \"url\": \"https://googleee.com/vr.fbx\",\r\n \"folder\": \"No Folder\",\r\n \"uuid\": \"542543sg45s4gg54353\",\r\n },\r\n ],\r\n settings: {\r\n \"displayDensity\": {\r\n \"size\": 1,\r\n \"labels\": [\r\n \"List\",\r\n \"Compact\",\r\n \"Large\",\r\n ],\r\n },\r\n },\r\n iconType: {\r\n \"SCRIPT\": {\r\n \"icon\": \"mdi-code-tags\",\r\n \"color\": \"red\",\r\n },\r\n \"MODEL\": {\r\n \"icon\": \"mdi-video-3d\",\r\n \"color\": \"green\",\r\n },\r\n \"AVATAR\": {\r\n \"icon\": \"mdi-account-convert\",\r\n \"color\": \"purple\",\r\n },\r\n \"PLACE\": {\r\n \"icon\": \"mdi-earth\",\r\n \"color\": \"#0097A7\", // cyan darken-2\r\n },\r\n \"JSON\": {\r\n \"icon\": \"mdi-inbox-multiple\",\r\n \"color\": \"#37474F\", // blue-grey darken-3\r\n },\r\n \"UNKNOWN\": {\r\n \"icon\": \"mdi-help\",\r\n \"color\": \"grey\",\r\n }\r\n },\r\n supportedItemTypes: [\r\n \"SCRIPT\",\r\n \"MODEL\",\r\n \"AVATAR\",\r\n \"PLACE\",\r\n \"JSON\",\r\n \"UNKNOWN\",\r\n ],\r\n removeDialog: {\r\n show: false,\r\n uuid: null,\r\n },\r\n removeFolderDialog: {\r\n show: false,\r\n uuid: null,\r\n },\r\n createFolderDialog: {\r\n show: false,\r\n valid: false,\r\n data: {\r\n \"name\": null,\r\n },\r\n },\r\n addDialog: {\r\n show: false,\r\n valid: false,\r\n data: {\r\n \"name\": null,\r\n \"folder\": null,\r\n \"url\": null,\r\n },\r\n },\r\n editDialog: {\r\n show: false,\r\n valid: false,\r\n uuid: null, //\r\n data: {\r\n \"type\": null,\r\n \"name\": null,\r\n \"url\": null,\r\n \"folder\": null,\r\n },\r\n },\r\n editFolderDialog: {\r\n show: false,\r\n valid: false,\r\n uuid: null, //\r\n data: {\r\n \"name\": null,\r\n \"folder\": null,\r\n },\r\n },\r\n receiveDialog: {\r\n show: false,\r\n valid: false,\r\n data: {\r\n \"userUUID\": null,\r\n \"userDisplayName\": null,\r\n \"name\": null,\r\n \"folder\": null,\r\n \"type\": null,\r\n \"url\": null,\r\n },\r\n },\r\n shareDialog: {\r\n show: false,\r\n valid: false,\r\n data: {\r\n \"uuid\": null, // UUID of the item you want to share. THIS IS THE KEY.\r\n \"url\": null, // The item you want to share.\r\n \"recipient\": null,\r\n }\r\n },\r\n },\r\n mutations: {\r\n mutate (state, payload) {\r\n state[payload.property] = payload.with;\r\n // console.info(\"Payload:\", payload.property, \"with:\", payload.with, \"state is now:\", this.state);\r\n },\r\n sortTopInventory (state, payload) {\r\n let { items } = state;\r\n \r\n if (payload.sort === \"az\") {\r\n state.items.sort(function(a, b) {\r\n var nameA = a.name.toUpperCase(); // ignore upper and lowercase\r\n var nameB = b.name.toUpperCase(); // ignore upper and lowercase\r\n if (nameA < nameB) {\r\n return -1;\r\n }\r\n if (nameA > nameB) {\r\n return 1;\r\n }\r\n \r\n // names must be equal\r\n return 0;\r\n });\r\n } \r\n \r\n if (payload.sort === \"za\") {\r\n state.items.sort(function(a, b) {\r\n var nameA = a.name.toUpperCase(); // ignore upper and lowercase\r\n var nameB = b.name.toUpperCase(); // ignore upper and lowercase\r\n if (nameA > nameB) {\r\n return -1;\r\n }\r\n if (nameA < nameB) {\r\n return 1;\r\n }\r\n \r\n // names must be equal\r\n return 0;\r\n });\r\n }\r\n \r\n Vue.set(state,'items', items);\r\n },\r\n pushToItems (state, payload) {\r\n let { items } = state;\r\n items.push(payload);\r\n \r\n Vue.set(state,'items', items);\r\n },\r\n moveFolder (state, payload) {\r\n let { items } = state;\r\n \r\n if (payload.parentFolderUUID === \"top\") {\r\n payload.findFolder.returnedItem.folder = \"No Folder\";\r\n // console.info(\"Going to push...\", payload.findFolder.returnedItem);\r\n // console.info(\"Containing these items...\", payload.findFolder.returnedItem.items);\r\n\r\n items.push(payload.findFolder.returnedItem);\r\n Vue.set(state, 'items', items);\r\n \r\n } else if (payload.findParentFolder) { \r\n // console.info(\"Going to push...\", payload.findFolder.returnedItem);\r\n // console.info(\"Containing these items...\", payload.findFolder.returnedItem.items);\r\n // console.info(\"Into...\", payload.findParentFolder.returnedItem);\r\n\r\n payload.findFolder.returnedItem.folder = payload.findParentFolder.name;\r\n payload.findParentFolder.returnedItem.items.push(payload.findFolder.returnedItem);\r\n Vue.set(state,'items', items);\r\n }\r\n },\r\n moveItem (state, payload) {\r\n let { items } = state;\r\n \r\n if (payload.parentFolderUUID === \"top\") {\r\n payload.findItem.returnedItem.folder = \"No Folder\";\r\n // console.info(\"Going to push...\", payload.findFolder.returnedItem);\r\n // console.info(\"Containing these items...\", payload.findFolder.returnedItem.items);\r\n\r\n items.push(payload.findItem.returnedItem);\r\n Vue.set(state,'items', items);\r\n \r\n } else if (payload.findParentFolder) { \r\n // console.info(\"Going to push...\", payload.findFolder.returnedItem);\r\n // console.info(\"Containing these items...\", payload.findFolder.returnedItem.items);\r\n // console.info(\"Into...\", payload.findParentFolder.returnedItem);\r\n\r\n payload.findItem.returnedItem.folder = payload.findParentFolder.name;\r\n payload.findParentFolder.returnedItem.items.push(payload.findItem.returnedItem);\r\n Vue.set(state,'items', items);\r\n }\r\n }\r\n }\r\n})\r\n","/*\r\n main.js\r\n\r\n Created by Kalila L. on 7 Apr 2020\r\n Copyright 2020 Vircadia and contributors.\r\n \r\n Distributed under the Apache License, Version 2.0.\r\n See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html\r\n*/\r\n\r\nimport Vue from 'vue'\r\nimport App from './App.vue'\r\nimport vuetify from './plugins/vuetify';\r\nimport { store } from './plugins/store';\r\n\r\nVue.config.productionTip = false;\r\n\r\nwindow.vm = new Vue({\r\n vuetify,\r\n store,\r\n render: h => h(App)\r\n}).$mount('#app');\r\n"],"sourceRoot":""} \ No newline at end of file diff --git a/scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js b/scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js new file mode 100644 index 0000000000..82d8c35668 --- /dev/null +++ b/scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js @@ -0,0 +1,21 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-vendors"],{"00ee":function(t,e,n){var i=n("b622"),r=i("toStringTag"),o={};o[r]="z",t.exports="[object z]"===String(o)},"0366":function(t,e,n){var i=n("1c0b");t.exports=function(t,e,n){if(i(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,i){return t.call(e,n,i)};case 3:return function(n,i,r){return t.call(e,n,i,r)}}return function(){return t.apply(e,arguments)}}},"0481":function(t,e,n){"use strict";var i=n("23e7"),r=n("a2bf"),o=n("7b0b"),a=n("50c4"),s=n("a691"),c=n("65f0");i({target:"Array",proto:!0},{flat:function(){var t=arguments.length?arguments[0]:void 0,e=o(this),n=a(e.length),i=c(e,0);return i.length=r(i,e,e,n,0,void 0===t?1:s(t)),i}})},"0538":function(t,e,n){"use strict";var i=n("1c0b"),r=n("861d"),o=[].slice,a={},s=function(t,e,n){if(!(e in a)){for(var i=[],r=0;r0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length,i=new Array(n>1?n-1:0),r=1;r1&&void 0!==arguments[1]?arguments[1]:"top center 0",n=arguments.length>2?arguments[2]:void 0;return{name:t,functional:!0,props:{group:{type:Boolean,default:!1},hideOnLeave:{type:Boolean,default:!1},leaveAbsolute:{type:Boolean,default:!1},mode:{type:String,default:n},origin:{type:String,default:e}},render:function(e,n){var o="transition".concat(n.props.group?"-group":""),a={props:{name:t,mode:n.props.mode},on:{beforeEnter:function(t){t.style.transformOrigin=n.props.origin,t.style.webkitTransformOrigin=n.props.origin}}};return n.props.leaveAbsolute&&(a.on.leave=r(a.on.leave,(function(t){return t.style.position="absolute"}))),n.props.hideOnLeave&&(a.on.leave=r(a.on.leave,(function(t){return t.style.display="none"}))),e(o,Object(i["a"])(n.data,a),n.children)}}}function a(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"in-out";return{name:t,functional:!0,props:{mode:{type:String,default:n}},render:function(n,r){return n("transition",Object(i["a"])(r.data,{props:{name:t},on:e}),r.children)}}}var s=n("ade3"),c=n("80d2"),l=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=e?"width":"height",i="offset".concat(Object(c["y"])(n));return{beforeEnter:function(t){t._parent=t.parentNode,t._initialStyle=Object(s["a"])({transition:t.style.transition,visibility:t.style.visibility,overflow:t.style.overflow},n,t.style[n])},enter:function(e){var r=e._initialStyle,o="".concat(e[i],"px");e.style.setProperty("transition","none","important"),e.style.visibility="hidden",e.style.visibility=r.visibility,e.style.overflow="hidden",e.style[n]="0",e.offsetHeight,e.style.transition=r.transition,t&&e._parent&&e._parent.classList.add(t),requestAnimationFrame((function(){e.style[n]=o}))},afterEnter:o,enterCancelled:o,leave:function(t){t._initialStyle=Object(s["a"])({transition:"",visibility:"",overflow:t.style.overflow},n,t.style[n]),t.style.overflow="hidden",t.style[n]="".concat(t[i],"px"),t.offsetHeight,requestAnimationFrame((function(){return t.style[n]="0"}))},afterLeave:r,leaveCancelled:r};function r(e){t&&e._parent&&e._parent.classList.remove(t),o(e)}function o(t){var e=t._initialStyle[n];t.style.overflow=t._initialStyle.overflow,null!=e&&(t.style[n]=e),delete t._initialStyle}},u=(o("carousel-transition"),o("carousel-reverse-transition"),o("tab-transition"),o("tab-reverse-transition"),o("menu-transition"),o("fab-transition","center center","out-in"),o("dialog-transition"),o("dialog-bottom-transition"),o("fade-transition")),h=o("scale-transition"),f=(o("scroll-x-transition"),o("scroll-x-reverse-transition"),o("scroll-y-transition"),o("scroll-y-reverse-transition"),o("slide-x-transition")),d=(o("slide-x-reverse-transition"),o("slide-y-transition"),o("slide-y-reverse-transition"),a("expand-transition",l())),p=a("expand-x-transition",l("",!0))},"07ac":function(t,e,n){var i=n("23e7"),r=n("6f53").values;i({target:"Object",stat:!0},{values:function(t){return r(t)}})},"0bc6":function(t,e,n){},"0cfb":function(t,e,n){var i=n("83ab"),r=n("d039"),o=n("cc12");t.exports=!i&&!r((function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}))},"0d3b":function(t,e,n){var i=n("d039"),r=n("b622"),o=n("c430"),a=r("iterator");t.exports=!i((function(){var t=new URL("b?a=1&b=2&c=3","http://a"),e=t.searchParams,n="";return t.pathname="c%20d",e.forEach((function(t,i){e["delete"]("b"),n+=i+t})),o&&!t.toJSON||!e.sort||"http://a/c%20d?a=1&c=3"!==t.href||"3"!==e.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!e[a]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==n||"x"!==new URL("http://x",void 0).host}))},"10d2":function(t,e,n){"use strict";var i=n("8dd9");e["a"]=i["a"]},1148:function(t,e,n){"use strict";var i=n("a691"),r=n("1d80");t.exports="".repeat||function(t){var e=String(r(this)),n="",o=i(t);if(o<0||o==1/0)throw RangeError("Wrong number of repetitions");for(;o>0;(o>>>=1)&&(e+=e))1&o&&(n+=e);return n}},1276:function(t,e,n){"use strict";var i=n("d784"),r=n("44e7"),o=n("825a"),a=n("1d80"),s=n("4840"),c=n("8aa5"),l=n("50c4"),u=n("14c3"),h=n("9263"),f=n("d039"),d=[].push,p=Math.min,v=4294967295,m=!f((function(){return!RegExp(v,"y")}));i("split",2,(function(t,e,n){var i;return i="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var i=String(a(this)),o=void 0===n?v:n>>>0;if(0===o)return[];if(void 0===t)return[i];if(!r(t))return e.call(i,t,o);var s,c,l,u=[],f=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),p=0,m=new RegExp(t.source,f+"g");while(s=h.call(m,i)){if(c=m.lastIndex,c>p&&(u.push(i.slice(p,s.index)),s.length>1&&s.index=o))break;m.lastIndex===s.index&&m.lastIndex++}return p===i.length?!l&&m.test("")||u.push(""):u.push(i.slice(p)),u.length>o?u.slice(0,o):u}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:e.call(this,t,n)}:e,[function(e,n){var r=a(this),o=void 0==e?void 0:e[t];return void 0!==o?o.call(e,r,n):i.call(String(r),e,n)},function(t,r){var a=n(i,t,this,r,i!==e);if(a.done)return a.value;var h=o(t),f=String(this),d=s(h,RegExp),g=h.unicode,b=(h.ignoreCase?"i":"")+(h.multiline?"m":"")+(h.unicode?"u":"")+(m?"y":"g"),y=new d(m?h:"^(?:"+h.source+")",b),w=void 0===r?v:r>>>0;if(0===w)return[];if(0===f.length)return null===u(y,f)?[f]:[];var x=0,O=0,_=[];while(O4}(function(t){t["xSmall"]="12px",t["small"]="16px",t["default"]="24px",t["medium"]="28px",t["large"]="36px",t["xLarge"]="40px"})(i||(i={}));var p=Object(h["a"])(o["a"],a["a"],s["a"],c["a"]).extend({name:"v-icon",props:{dense:Boolean,disabled:Boolean,left:Boolean,right:Boolean,size:[Number,String],tag:{type:String,required:!1,default:"i"}},computed:{medium:function(){return!1},hasClickListener:function(){return Boolean(this.listeners$.click||this.listeners$["!click"])}},methods:{getIcon:function(){var t="";return this.$slots.default&&(t=this.$slots.default[0].text.trim()),Object(l["x"])(this,t)},getSize:function(){var t={xSmall:this.xSmall,small:this.small,medium:this.medium,large:this.large,xLarge:this.xLarge},e=Object(l["t"])(t).find((function(e){return t[e]}));return e&&i[e]||Object(l["f"])(this.size)},getDefaultData:function(){var t={staticClass:"v-icon notranslate",class:{"v-icon--disabled":this.disabled,"v-icon--left":this.left,"v-icon--link":this.hasClickListener,"v-icon--right":this.right,"v-icon--dense":this.dense},attrs:Object(r["a"])({"aria-hidden":!this.hasClickListener,disabled:this.hasClickListener&&this.disabled,type:this.hasClickListener?"button":void 0},this.attrs$),on:this.listeners$};return t},applyColors:function(t){t.class=Object(r["a"])(Object(r["a"])({},t.class),this.themeClasses),this.setTextColor(this.color,t)},renderFontIcon:function(t,e){var n=[],i=this.getDefaultData(),r="material-icons",o=t.indexOf("-"),a=o<=-1;a?n.push(t):(r=t.slice(0,o),f(r)&&(r="")),i.class[r]=!0,i.class[t]=!a;var s=this.getSize();return s&&(i.style={fontSize:s}),this.applyColors(i),e(this.hasClickListener?"button":this.tag,i,n)},renderSvgIcon:function(t,e){var n=this.getSize(),i=Object(r["a"])(Object(r["a"])({},this.getDefaultData()),{},{style:n?{fontSize:n,height:n,width:n}:void 0});i.class["v-icon--svg"]=!0,this.applyColors(i);var o={attrs:{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",height:n||"24",width:n||"24",role:"img","aria-hidden":!0}};return e(this.hasClickListener?"button":"span",i,[e("svg",o,[e("path",{attrs:{d:t}})])])},renderSvgIconComponent:function(t,e){var n=this.getDefaultData();n.class["v-icon--is-component"]=!0;var i=this.getSize();i&&(n.style={fontSize:i,height:i,width:i}),this.applyColors(n);var r=t.component;return n.props=t.props,n.nativeOn=n.on,e(r,n)}},render:function(t){var e=this.getIcon();return"string"===typeof e?d(e)?this.renderSvgIcon(e,t):this.renderFontIcon(e,t):this.renderSvgIconComponent(e,t)}});e["a"]=u["a"].extend({name:"v-icon",$_wrapperFor:p,functional:!0,render:function(t,e){var n=e.data,i=e.children,r="";return n.domProps&&(r=n.domProps.textContent||n.domProps.innerHTML||r,delete n.domProps.textContent,delete n.domProps.innerHTML),t(p,n,r?[r]:i)}})},"13d5":function(t,e,n){"use strict";var i=n("23e7"),r=n("d58f").left,o=n("a640"),a=n("ae40"),s=o("reduce"),c=a("reduce",{1:0});i({target:"Array",proto:!0,forced:!s||!c},{reduce:function(t){return r(this,t,arguments.length,arguments.length>1?arguments[1]:void 0)}})},"14c3":function(t,e,n){var i=n("c6b6"),r=n("9263");t.exports=function(t,e){var n=t.exec;if("function"===typeof n){var o=n.call(t,e);if("object"!==typeof o)throw TypeError("RegExp exec method returned something other than an Object or null");return o}if("RegExp"!==i(t))throw TypeError("RegExp#exec called on incompatible receiver");return r.call(t,e)}},"159b":function(t,e,n){var i=n("da84"),r=n("fdbc"),o=n("17c2"),a=n("9112");for(var s in r){var c=i[s],l=c&&c.prototype;if(l&&l.forEach!==o)try{a(l,"forEach",o)}catch(u){l.forEach=o}}},"15fd":function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));n("a4d3"),n("c975"),n("b64b");function i(t,e){if(null==t)return{};var n,i,r={},o=Object.keys(t);for(i=0;i=0||(r[n]=t[n]);return r}function r(t,e){if(null==t)return{};var n,r,o=i(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}},"166a":function(t,e,n){},"169a":function(t,e,n){"use strict";n("7db0"),n("caad"),n("45fc"),n("a9e3"),n("2532"),n("498a");var i=n("5530"),r=n("2909"),o=n("ade3"),a=(n("368e"),n("480e")),s=n("4ad4"),c=n("b848"),l=n("75eb"),u=n("e707"),h=n("e4d3"),f=n("21be"),d=n("f2e7"),p=n("a293"),v=n("58df"),m=n("d9bd"),g=n("80d2"),b=Object(v["a"])(s["a"],c["a"],l["a"],u["a"],h["a"],f["a"],d["a"]);e["a"]=b.extend({name:"v-dialog",directives:{ClickOutside:p["a"]},props:{dark:Boolean,disabled:Boolean,fullscreen:Boolean,light:Boolean,maxWidth:{type:[String,Number],default:"none"},noClickAnimation:Boolean,origin:{type:String,default:"center center"},persistent:Boolean,retainFocus:{type:Boolean,default:!0},scrollable:Boolean,transition:{type:[String,Boolean],default:"dialog-transition"},width:{type:[String,Number],default:"auto"}},data:function(){return{activatedBy:null,animate:!1,animateTimeout:-1,isActive:!!this.value,stackMinZIndex:200}},computed:{classes:function(){var t;return t={},Object(o["a"])(t,"v-dialog ".concat(this.contentClass).trim(),!0),Object(o["a"])(t,"v-dialog--active",this.isActive),Object(o["a"])(t,"v-dialog--persistent",this.persistent),Object(o["a"])(t,"v-dialog--fullscreen",this.fullscreen),Object(o["a"])(t,"v-dialog--scrollable",this.scrollable),Object(o["a"])(t,"v-dialog--animated",this.animate),t},contentClasses:function(){return{"v-dialog__content":!0,"v-dialog__content--active":this.isActive}},hasActivator:function(){return Boolean(!!this.$slots.activator||!!this.$scopedSlots.activator)}},watch:{isActive:function(t){t?(this.show(),this.hideScroll()):(this.removeOverlay(),this.unbind())},fullscreen:function(t){this.isActive&&(t?(this.hideScroll(),this.removeOverlay(!1)):(this.showScroll(),this.genOverlay()))}},created:function(){this.$attrs.hasOwnProperty("full-width")&&Object(m["e"])("full-width",this)},beforeMount:function(){var t=this;this.$nextTick((function(){t.isBooted=t.isActive,t.isActive&&t.show()}))},beforeDestroy:function(){"undefined"!==typeof window&&this.unbind()},methods:{animateClick:function(){var t=this;this.animate=!1,this.$nextTick((function(){t.animate=!0,window.clearTimeout(t.animateTimeout),t.animateTimeout=window.setTimeout((function(){return t.animate=!1}),150)}))},closeConditional:function(t){var e=t.target;return!(this._isDestroyed||!this.isActive||this.$refs.content.contains(e)||this.overlay&&e&&!this.overlay.$el.contains(e))&&this.activeZIndex>=this.getMaxZIndex()},hideScroll:function(){this.fullscreen?document.documentElement.classList.add("overflow-y-hidden"):u["a"].options.methods.hideScroll.call(this)},show:function(){var t=this;!this.fullscreen&&!this.hideOverlay&&this.genOverlay(),this.$nextTick((function(){t.$refs.content.focus(),t.bind()}))},bind:function(){window.addEventListener("focusin",this.onFocusin)},unbind:function(){window.removeEventListener("focusin",this.onFocusin)},onClickOutside:function(t){this.$emit("click:outside",t),this.persistent?this.noClickAnimation||this.animateClick():this.isActive=!1},onKeydown:function(t){if(t.keyCode===g["s"].esc&&!this.getOpenDependents().length)if(this.persistent)this.noClickAnimation||this.animateClick();else{this.isActive=!1;var e=this.getActivator();this.$nextTick((function(){return e&&e.focus()}))}this.$emit("keydown",t)},onFocusin:function(t){if(t&&this.retainFocus){var e=t.target;if(e&&![document,this.$refs.content].includes(e)&&!this.$refs.content.contains(e)&&this.activeZIndex>=this.getMaxZIndex()&&!this.getOpenDependentElements().some((function(t){return t.contains(e)}))){var n=this.$refs.content.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),i=Object(r["a"])(n).find((function(t){return!t.hasAttribute("disabled")}));i&&i.focus()}}},genContent:function(){var t=this;return this.showLazyContent((function(){return[t.$createElement(a["a"],{props:{root:!0,light:t.light,dark:t.dark}},[t.$createElement("div",{class:t.contentClasses,attrs:Object(i["a"])({role:"document",tabindex:t.isActive?0:void 0},t.getScopeIdAttrs()),on:{keydown:t.onKeydown},style:{zIndex:t.activeZIndex},ref:"content"},[t.genTransition()])])]}))},genTransition:function(){var t=this.genInnerContent();return this.transition?this.$createElement("transition",{props:{name:this.transition,origin:this.origin,appear:!0}},[t]):t},genInnerContent:function(){var t={class:this.classes,ref:"dialog",directives:[{name:"click-outside",value:{handler:this.onClickOutside,closeConditional:this.closeConditional,include:this.getOpenDependentElements}},{name:"show",value:this.isActive}],style:{transformOrigin:this.origin}};return this.fullscreen||(t.style=Object(i["a"])(Object(i["a"])({},t.style),{},{maxWidth:"none"===this.maxWidth?void 0:Object(g["f"])(this.maxWidth),width:"auto"===this.width?void 0:Object(g["f"])(this.width)})),this.$createElement("div",t,this.getContentSlot())}},render:function(t){return t("div",{staticClass:"v-dialog__container",class:{"v-dialog__container--attached":""===this.attach||!0===this.attach||"attach"===this.attach},attrs:{role:"dialog"}},[this.genActivator(),this.genContent()])}})},"16b7":function(t,e,n){"use strict";n("a9e3");var i=n("2b0e");e["a"]=i["a"].extend().extend({name:"delayable",props:{openDelay:{type:[Number,String],default:0},closeDelay:{type:[Number,String],default:0}},data:function(){return{openTimeout:void 0,closeTimeout:void 0}},methods:{clearDelay:function(){clearTimeout(this.openTimeout),clearTimeout(this.closeTimeout)},runDelay:function(t,e){var n=this;this.clearDelay();var i=parseInt(this["".concat(t,"Delay")],10);this["".concat(t,"Timeout")]=setTimeout(e||function(){n.isActive={open:!0,close:!1}[t]},i)}}})},"17c2":function(t,e,n){"use strict";var i=n("b727").forEach,r=n("a640"),o=n("ae40"),a=r("forEach"),s=o("forEach");t.exports=a&&s?[].forEach:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}},1800:function(t,e,n){"use strict";n("4de4");var i=n("2b0e");e["a"]=i["a"].extend({name:"v-list-item-action",functional:!0,render:function(t,e){var n=e.data,i=e.children,r=void 0===i?[]:i;n.staticClass=n.staticClass?"v-list-item__action ".concat(n.staticClass):"v-list-item__action";var o=r.filter((function(t){return!1===t.isComment&&" "!==t.text}));return o.length>1&&(n.staticClass+=" v-list-item__action--stack"),t("div",n,r)}})},"18a5":function(t,e,n){"use strict";var i=n("23e7"),r=n("857a"),o=n("af03");i({target:"String",proto:!0,forced:o("anchor")},{anchor:function(t){return r(this,"a","name",t)}})},"19aa":function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation");return t}},"1b2c":function(t,e,n){},"1baa":function(t,e,n){"use strict";var i=n("5530"),r=(n("899c"),n("604c")),o=n("a9ad"),a=n("58df");e["a"]=Object(a["a"])(r["a"],o["a"]).extend({name:"v-list-item-group",provide:function(){return{isInGroup:!0,listItemGroup:this}},computed:{classes:function(){return Object(i["a"])(Object(i["a"])({},r["a"].options.computed.classes.call(this)),{},{"v-list-item-group":!0})}},methods:{genData:function(){return this.setTextColor(this.color,Object(i["a"])(Object(i["a"])({},r["a"].options.methods.genData.call(this)),{},{attrs:{role:"listbox"}}))}}})},"1be4":function(t,e,n){var i=n("d066");t.exports=i("document","documentElement")},"1c0b":function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},"1c7e":function(t,e,n){var i=n("b622"),r=i("iterator"),o=!1;try{var a=0,s={next:function(){return{done:!!a++}},return:function(){o=!0}};s[r]=function(){return this},Array.from(s,(function(){throw 2}))}catch(c){}t.exports=function(t,e){if(!e&&!o)return!1;var n=!1;try{var i={};i[r]=function(){return{next:function(){return{done:n=!0}}}},t(i)}catch(c){}return n}},"1c87":function(t,e,n){"use strict";n("99af"),n("ac1f"),n("5319"),n("498a"),n("9911");var i=n("ade3"),r=n("5530"),o=n("2b0e"),a=n("5607"),s=n("80d2");e["a"]=o["a"].extend({name:"routable",directives:{Ripple:a["a"]},props:{activeClass:String,append:Boolean,disabled:Boolean,exact:{type:Boolean,default:void 0},exactActiveClass:String,link:Boolean,href:[String,Object],to:[String,Object],nuxt:Boolean,replace:Boolean,ripple:{type:[Boolean,Object],default:null},tag:String,target:String},data:function(){return{isActive:!1,proxyClass:""}},computed:{classes:function(){var t={};return this.to||(this.activeClass&&(t[this.activeClass]=this.isActive),this.proxyClass&&(t[this.proxyClass]=this.isActive)),t},computedRipple:function(){return null!=this.ripple?this.ripple:!this.disabled&&this.isClickable},isClickable:function(){return!this.disabled&&Boolean(this.isLink||this.$listeners.click||this.$listeners["!click"]||this.$attrs.tabindex)},isLink:function(){return this.to||this.href||this.link},styles:function(){return{}}},watch:{$route:"onRouteChange"},methods:{click:function(t){this.$emit("click",t)},generateRouteLink:function(){var t,e,n=this.exact,o=(t={attrs:{tabindex:"tabindex"in this.$attrs?this.$attrs.tabindex:void 0},class:this.classes,style:this.styles,props:{},directives:[{name:"ripple",value:this.computedRipple}]},Object(i["a"])(t,this.to?"nativeOn":"on",Object(r["a"])(Object(r["a"])({},this.$listeners),{},{click:this.click})),Object(i["a"])(t,"ref","link"),t);if("undefined"===typeof this.exact&&(n="/"===this.to||this.to===Object(this.to)&&"/"===this.to.path),this.to){var a=this.activeClass,s=this.exactActiveClass||a;this.proxyClass&&(a="".concat(a," ").concat(this.proxyClass).trim(),s="".concat(s," ").concat(this.proxyClass).trim()),e=this.nuxt?"nuxt-link":"router-link",Object.assign(o.props,{to:this.to,exact:n,activeClass:a,exactActiveClass:s,append:this.append,replace:this.replace})}else e=(this.href?"a":this.tag)||"div","a"===e&&this.href&&(o.attrs.href=this.href);return this.target&&(o.attrs.target=this.target),{tag:e,data:o}},onRouteChange:function(){var t=this;if(this.to&&this.$refs.link&&this.$route){var e="".concat(this.activeClass," ").concat(this.proxyClass||"").trim(),n="_vnode.data.class.".concat(e);this.$nextTick((function(){Object(s["m"])(t.$refs.link,n)&&t.toggle()}))}},toggle:function(){}}})},"1cdc":function(t,e,n){var i=n("342f");t.exports=/(iphone|ipod|ipad).*applewebkit/i.test(i)},"1d80":function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},"1dde":function(t,e,n){var i=n("d039"),r=n("b622"),o=n("2d00"),a=r("species");t.exports=function(t){return o>=51||!i((function(){var e=[],n=e.constructor={};return n[a]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},"20f6":function(t,e,n){},"21be":function(t,e,n){"use strict";n("99af"),n("caad"),n("2532");var i=n("2909"),r=n("2b0e"),o=n("80d2");e["a"]=r["a"].extend().extend({name:"stackable",data:function(){return{stackElement:null,stackExclude:null,stackMinZIndex:0,isActive:!1}},computed:{activeZIndex:function(){if("undefined"===typeof window)return 0;var t=this.stackElement||this.$refs.content,e=this.isActive?this.getMaxZIndex(this.stackExclude||[t])+2:Object(o["q"])(t);return null==e?e:parseInt(e)}},methods:{getMaxZIndex:function(){for(var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=this.$el,n=[this.stackMinZIndex,Object(o["q"])(e)],r=[].concat(Object(i["a"])(document.getElementsByClassName("v-menu__content--active")),Object(i["a"])(document.getElementsByClassName("v-dialog__content--active"))),a=0;ap;p++)if(m=u?y(i(b=t[p])[0],b[1]):y(t[p]),m&&m instanceof l)return m;return new l(!1)}f=d.call(t)}g=f.next;while(!(b=g.call(f)).done)if(m=c(f,y,b.value,u),"object"==typeof m&&m&&m instanceof l)return m;return new l(!1)};u.stop=function(t){return new l(!0,t)}},"23cb":function(t,e,n){var i=n("a691"),r=Math.max,o=Math.min;t.exports=function(t,e){var n=i(t);return n<0?r(n+e,0):o(n,e)}},"23e7":function(t,e,n){var i=n("da84"),r=n("06cf").f,o=n("9112"),a=n("6eeb"),s=n("ce4e"),c=n("e893"),l=n("94ca");t.exports=function(t,e){var n,u,h,f,d,p,v=t.target,m=t.global,g=t.stat;if(u=m?i:g?i[v]||s(v,{}):(i[v]||{}).prototype,u)for(h in e){if(d=e[h],t.noTargetGet?(p=r(u,h),f=p&&p.value):f=u[h],n=l(m?h:v+(g?".":"#")+h,t.forced),!n&&void 0!==f){if(typeof d===typeof f)continue;c(d,f)}(t.sham||f&&f.sham)&&o(d,"sham",!0),a(u,h,d,t)}}},"241c":function(t,e,n){var i=n("ca84"),r=n("7839"),o=r.concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return i(t,o)}},"24b2":function(t,e,n){"use strict";n("a9e3");var i=n("80d2"),r=n("2b0e");e["a"]=r["a"].extend({name:"measurable",props:{height:[Number,String],maxHeight:[Number,String],maxWidth:[Number,String],minHeight:[Number,String],minWidth:[Number,String],width:[Number,String]},computed:{measurableStyles:function(){var t={},e=Object(i["f"])(this.height),n=Object(i["f"])(this.minHeight),r=Object(i["f"])(this.minWidth),o=Object(i["f"])(this.maxHeight),a=Object(i["f"])(this.maxWidth),s=Object(i["f"])(this.width);return e&&(t.height=e),n&&(t.minHeight=n),r&&(t.minWidth=r),o&&(t.maxHeight=o),a&&(t.maxWidth=a),s&&(t.width=s),t}}})},2532:function(t,e,n){"use strict";var i=n("23e7"),r=n("5a34"),o=n("1d80"),a=n("ab13");i({target:"String",proto:!0,forced:!a("includes")},{includes:function(t){return!!~String(o(this)).indexOf(r(t),arguments.length>1?arguments[1]:void 0)}})},"25a8":function(t,e,n){},"25f0":function(t,e,n){"use strict";var i=n("6eeb"),r=n("825a"),o=n("d039"),a=n("ad6d"),s="toString",c=RegExp.prototype,l=c[s],u=o((function(){return"/a/b"!=l.call({source:"a",flags:"b"})})),h=l.name!=s;(u||h)&&i(RegExp.prototype,s,(function(){var t=r(this),e=String(t.source),n=t.flags,i=String(void 0===n&&t instanceof RegExp&&!("flags"in c)?a.call(t):n);return"/"+e+"/"+i}),{unsafe:!0})},2626:function(t,e,n){"use strict";var i=n("d066"),r=n("9bf2"),o=n("b622"),a=n("83ab"),s=o("species");t.exports=function(t){var e=i(t),n=r.f;a&&e&&!e[s]&&n(e,s,{configurable:!0,get:function(){return this}})}},2877:function(t,e,n){"use strict";function i(t,e,n,i,r,o,a,s){var c,l="function"===typeof t?t.options:t;if(e&&(l.render=e,l.staticRenderFns=n,l._compiled=!0),i&&(l.functional=!0),o&&(l._scopeId="data-v-"+o),a?(c=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),r&&r.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},l._ssrRegister=c):r&&(c=s?function(){r.call(this,(l.functional?this.parent:this).$root.$options.shadowRoot)}:r),c)if(l.functional){l._injectStyles=c;var u=l.render;l.render=function(t,e){return c.call(e),u(t,e)}}else{var h=l.beforeCreate;l.beforeCreate=h?[].concat(h,c):[c]}return{exports:t,options:l}}n.d(e,"a",(function(){return i}))},2909:function(t,e,n){"use strict";n.d(e,"a",(function(){return c}));var i=n("6b75");function r(t){if(Array.isArray(t))return Object(i["a"])(t)}n("a4d3"),n("e01a"),n("d28b"),n("a630"),n("d3b7"),n("3ca3"),n("ddb0");function o(t){if("undefined"!==typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}var a=n("06c5");function s(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function c(t){return r(t)||o(t)||Object(a["a"])(t)||s()}},"297c":function(t,e,n){"use strict";n("a9e3");var i=n("2b0e"),r=(n("c7cd"),n("5530")),o=n("ade3"),a=(n("6ece"),n("0789")),s=n("a9ad"),c=n("fe6c"),l=n("a452"),u=n("7560"),h=n("80d2"),f=n("58df"),d=Object(f["a"])(s["a"],Object(c["b"])(["absolute","fixed","top","bottom"]),l["a"],u["a"]),p=d.extend({name:"v-progress-linear",props:{active:{type:Boolean,default:!0},backgroundColor:{type:String,default:null},backgroundOpacity:{type:[Number,String],default:null},bufferValue:{type:[Number,String],default:100},color:{type:String,default:"primary"},height:{type:[Number,String],default:4},indeterminate:Boolean,query:Boolean,reverse:Boolean,rounded:Boolean,stream:Boolean,striped:Boolean,value:{type:[Number,String],default:0}},data:function(){return{internalLazyValue:this.value||0}},computed:{__cachedBackground:function(){return this.$createElement("div",this.setBackgroundColor(this.backgroundColor||this.color,{staticClass:"v-progress-linear__background",style:this.backgroundStyle}))},__cachedBar:function(){return this.$createElement(this.computedTransition,[this.__cachedBarType])},__cachedBarType:function(){return this.indeterminate?this.__cachedIndeterminate:this.__cachedDeterminate},__cachedBuffer:function(){return this.$createElement("div",{staticClass:"v-progress-linear__buffer",style:this.styles})},__cachedDeterminate:function(){return this.$createElement("div",this.setBackgroundColor(this.color,{staticClass:"v-progress-linear__determinate",style:{width:Object(h["f"])(this.normalizedValue,"%")}}))},__cachedIndeterminate:function(){return this.$createElement("div",{staticClass:"v-progress-linear__indeterminate",class:{"v-progress-linear__indeterminate--active":this.active}},[this.genProgressBar("long"),this.genProgressBar("short")])},__cachedStream:function(){return this.stream?this.$createElement("div",this.setTextColor(this.color,{staticClass:"v-progress-linear__stream",style:{width:Object(h["f"])(100-this.normalizedBuffer,"%")}})):null},backgroundStyle:function(){var t,e=null==this.backgroundOpacity?this.backgroundColor?1:.3:parseFloat(this.backgroundOpacity);return t={opacity:e},Object(o["a"])(t,this.isReversed?"right":"left",Object(h["f"])(this.normalizedValue,"%")),Object(o["a"])(t,"width",Object(h["f"])(this.normalizedBuffer-this.normalizedValue,"%")),t},classes:function(){return Object(r["a"])({"v-progress-linear--absolute":this.absolute,"v-progress-linear--fixed":this.fixed,"v-progress-linear--query":this.query,"v-progress-linear--reactive":this.reactive,"v-progress-linear--reverse":this.isReversed,"v-progress-linear--rounded":this.rounded,"v-progress-linear--striped":this.striped},this.themeClasses)},computedTransition:function(){return this.indeterminate?a["c"]:a["e"]},isReversed:function(){return this.$vuetify.rtl!==this.reverse},normalizedBuffer:function(){return this.normalize(this.bufferValue)},normalizedValue:function(){return this.normalize(this.internalLazyValue)},reactive:function(){return Boolean(this.$listeners.change)},styles:function(){var t={};return this.active||(t.height=0),this.indeterminate||100===parseFloat(this.normalizedBuffer)||(t.width=Object(h["f"])(this.normalizedBuffer,"%")),t}},methods:{genContent:function(){var t=Object(h["o"])(this,"default",{value:this.internalLazyValue});return t?this.$createElement("div",{staticClass:"v-progress-linear__content"},t):null},genListeners:function(){var t=this.$listeners;return this.reactive&&(t.click=this.onClick),t},genProgressBar:function(t){return this.$createElement("div",this.setBackgroundColor(this.color,{staticClass:"v-progress-linear__indeterminate",class:Object(o["a"])({},t,!0)}))},onClick:function(t){if(this.reactive){var e=this.$el.getBoundingClientRect(),n=e.width;this.internalValue=t.offsetX/n*100}},normalize:function(t){return t<0?0:t>100?100:parseFloat(t)}},render:function(t){var e={staticClass:"v-progress-linear",attrs:{role:"progressbar","aria-valuemin":0,"aria-valuemax":this.normalizedBuffer,"aria-valuenow":this.indeterminate?void 0:this.normalizedValue},class:this.classes,style:{bottom:this.bottom?0:void 0,height:this.active?Object(h["f"])(this.height):0,top:this.top?0:void 0},on:this.genListeners()};return t("div",e,[this.__cachedStream,this.__cachedBackground,this.__cachedBuffer,this.__cachedBar,this.genContent()])}}),v=p;e["a"]=i["a"].extend().extend({name:"loadable",props:{loading:{type:[Boolean,String],default:!1},loaderHeight:{type:[Number,String],default:2}},methods:{genProgress:function(){return!1===this.loading?null:this.$slots.progress||this.$createElement(v,{props:{absolute:!0,color:!0===this.loading||""===this.loading?this.color||"primary":this.loading,height:this.loaderHeight,indeterminate:!0}})}}})},"2a7f":function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var i=n("71d9"),r=n("80d2"),o=Object(r["h"])("v-toolbar__title"),a=Object(r["h"])("v-toolbar__items");i["a"]},"2b0e":function(t,e,n){"use strict";(function(t){ +/*! + * Vue.js v2.6.11 + * (c) 2014-2019 Evan You + * Released under the MIT License. + */ +var n=Object.freeze({});function i(t){return void 0===t||null===t}function r(t){return void 0!==t&&null!==t}function o(t){return!0===t}function a(t){return!1===t}function s(t){return"string"===typeof t||"number"===typeof t||"symbol"===typeof t||"boolean"===typeof t}function c(t){return null!==t&&"object"===typeof t}var l=Object.prototype.toString;function u(t){return"[object Object]"===l.call(t)}function h(t){return"[object RegExp]"===l.call(t)}function f(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return r(t)&&"function"===typeof t.then&&"function"===typeof t.catch}function p(t){return null==t?"":Array.isArray(t)||u(t)&&t.toString===l?JSON.stringify(t,null,2):String(t)}function v(t){var e=parseFloat(t);return isNaN(e)?t:e}function m(t,e){for(var n=Object.create(null),i=t.split(","),r=0;r-1)return t.splice(n,1)}}var y=Object.prototype.hasOwnProperty;function w(t,e){return y.call(t,e)}function x(t){var e=Object.create(null);return function(n){var i=e[n];return i||(e[n]=t(n))}}var O=/-(\w)/g,_=x((function(t){return t.replace(O,(function(t,e){return e?e.toUpperCase():""}))})),S=x((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),C=/\B([A-Z])/g,k=x((function(t){return t.replace(C,"-$1").toLowerCase()}));function j(t,e){function n(n){var i=arguments.length;return i?i>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function A(t,e){return t.bind(e)}var $=Function.prototype.bind?A:j;function E(t,e){e=e||0;var n=t.length-e,i=new Array(n);while(n--)i[n]=t[n+e];return i}function T(t,e){for(var n in e)t[n]=e[n];return t}function I(t){for(var e={},n=0;n0,nt=Q&&Q.indexOf("edge/")>0,it=(Q&&Q.indexOf("android"),Q&&/iphone|ipad|ipod|ios/.test(Q)||"ios"===J),rt=(Q&&/chrome\/\d+/.test(Q),Q&&/phantomjs/.test(Q),Q&&Q.match(/firefox\/(\d+)/)),ot={}.watch,at=!1;if(K)try{var st={};Object.defineProperty(st,"passive",{get:function(){at=!0}}),window.addEventListener("test-passive",null,st)}catch(_a){}var ct=function(){return void 0===X&&(X=!K&&!Z&&"undefined"!==typeof t&&(t["process"]&&"server"===t["process"].env.VUE_ENV)),X},lt=K&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ut(t){return"function"===typeof t&&/native code/.test(t.toString())}var ht,ft="undefined"!==typeof Symbol&&ut(Symbol)&&"undefined"!==typeof Reflect&&ut(Reflect.ownKeys);ht="undefined"!==typeof Set&&ut(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var dt=L,pt=0,vt=function(){this.id=pt++,this.subs=[]};vt.prototype.addSub=function(t){this.subs.push(t)},vt.prototype.removeSub=function(t){b(this.subs,t)},vt.prototype.depend=function(){vt.target&&vt.target.addDep(this)},vt.prototype.notify=function(){var t=this.subs.slice();for(var e=0,n=t.length;e-1)if(o&&!w(r,"default"))a=!1;else if(""===a||a===k(t)){var c=te(String,r.type);(c<0||s0&&(a=je(a,(e||"")+"_"+n),ke(a[0])&&ke(l)&&(u[c]=Ot(l.text+a[0].text),a.shift()),u.push.apply(u,a)):s(a)?ke(l)?u[c]=Ot(l.text+a):""!==a&&u.push(Ot(a)):ke(a)&&ke(l)?u[c]=Ot(l.text+a.text):(o(t._isVList)&&r(a.tag)&&i(a.key)&&r(e)&&(a.key="__vlist"+e+"_"+n+"__"),u.push(a)));return u}function Ae(t){var e=t.$options.provide;e&&(t._provided="function"===typeof e?e.call(t):e)}function $e(t){var e=Ee(t.$options.inject,t);e&&($t(!1),Object.keys(e).forEach((function(n){Mt(t,n,e[n])})),$t(!0))}function Ee(t,e){if(t){for(var n=Object.create(null),i=ft?Reflect.ownKeys(t):Object.keys(t),r=0;r0,a=t?!!t.$stable:!o,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&i&&i!==n&&s===i.$key&&!o&&!i.$hasNormal)return i;for(var c in r={},t)t[c]&&"$"!==c[0]&&(r[c]=Me(e,c,t[c]))}else r={};for(var l in e)l in r||(r[l]=De(e,l));return t&&Object.isExtensible(t)&&(t._normalized=r),U(r,"$stable",a),U(r,"$key",s),U(r,"$hasNormal",o),r}function Me(t,e,n){var i=function(){var t=arguments.length?n.apply(null,arguments):n({});return t=t&&"object"===typeof t&&!Array.isArray(t)?[t]:Ce(t),t&&(0===t.length||1===t.length&&t[0].isComment)?void 0:t};return n.proxy&&Object.defineProperty(t,e,{get:i,enumerable:!0,configurable:!0}),i}function De(t,e){return function(){return t[e]}}function Be(t,e){var n,i,o,a,s;if(Array.isArray(t)||"string"===typeof t)for(n=new Array(t.length),i=0,o=t.length;i1?E(n):n;for(var i=E(arguments,1),r='event handler for "'+t+'"',o=0,a=n.length;odocument.createEvent("Event").timeStamp&&(Xn=function(){return Gn.now()})}function Kn(){var t,e;for(qn=Xn(),Wn=!0,Rn.sort((function(t,e){return t.id-e.id})),Un=0;UnUn&&Rn[n].id>t.id)n--;Rn.splice(n+1,0,t)}else Rn.push(t);Hn||(Hn=!0,pe(Kn))}}var ei=0,ni=function(t,e,n,i,r){this.vm=t,r&&(t._watcher=this),t._watchers.push(this),i?(this.deep=!!i.deep,this.user=!!i.user,this.lazy=!!i.lazy,this.sync=!!i.sync,this.before=i.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++ei,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new ht,this.newDepIds=new ht,this.expression="","function"===typeof e?this.getter=e:(this.getter=q(e),this.getter||(this.getter=L)),this.value=this.lazy?void 0:this.get()};ni.prototype.get=function(){var t;gt(this);var e=this.vm;try{t=this.getter.call(e,e)}catch(_a){if(!this.user)throw _a;ee(_a,e,'getter for watcher "'+this.expression+'"')}finally{this.deep&&me(t),bt(),this.cleanupDeps()}return t},ni.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},ni.prototype.cleanupDeps=function(){var t=this.deps.length;while(t--){var e=this.deps[t];this.newDepIds.has(e.id)||e.removeSub(this)}var n=this.depIds;this.depIds=this.newDepIds,this.newDepIds=n,this.newDepIds.clear(),n=this.deps,this.deps=this.newDeps,this.newDeps=n,this.newDeps.length=0},ni.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():ti(this)},ni.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||c(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(_a){ee(_a,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},ni.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},ni.prototype.depend=function(){var t=this.deps.length;while(t--)this.deps[t].depend()},ni.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||b(this.vm._watchers,this);var t=this.deps.length;while(t--)this.deps[t].removeSub(this);this.active=!1}};var ii={enumerable:!0,configurable:!0,get:L,set:L};function ri(t,e,n){ii.get=function(){return this[e][n]},ii.set=function(t){this[e][n]=t},Object.defineProperty(t,n,ii)}function oi(t){t._watchers=[];var e=t.$options;e.props&&ai(t,e.props),e.methods&&pi(t,e.methods),e.data?si(t):Lt(t._data={},!0),e.computed&&ui(t,e.computed),e.watch&&e.watch!==ot&&vi(t,e.watch)}function ai(t,e){var n=t.$options.propsData||{},i=t._props={},r=t.$options._propKeys=[],o=!t.$parent;o||$t(!1);var a=function(o){r.push(o);var a=Kt(o,e,n,t);Mt(i,o,a),o in t||ri(t,"_props",o)};for(var s in e)a(s);$t(!0)}function si(t){var e=t.$options.data;e=t._data="function"===typeof e?ci(e,t):e||{},u(e)||(e={});var n=Object.keys(e),i=t.$options.props,r=(t.$options.methods,n.length);while(r--){var o=n[r];0,i&&w(i,o)||W(o)||ri(t,"_data",o)}Lt(e,!0)}function ci(t,e){gt();try{return t.call(e,e)}catch(_a){return ee(_a,e,"data()"),{}}finally{bt()}}var li={lazy:!0};function ui(t,e){var n=t._computedWatchers=Object.create(null),i=ct();for(var r in e){var o=e[r],a="function"===typeof o?o:o.get;0,i||(n[r]=new ni(t,a||L,L,li)),r in t||hi(t,r,o)}}function hi(t,e,n){var i=!ct();"function"===typeof n?(ii.get=i?fi(e):di(n),ii.set=L):(ii.get=n.get?i&&!1!==n.cache?fi(e):di(n.get):L,ii.set=n.set||L),Object.defineProperty(t,e,ii)}function fi(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),vt.target&&e.depend(),e.value}}function di(t){return function(){return t.call(this,this)}}function pi(t,e){t.$options.props;for(var n in e)t[n]="function"!==typeof e[n]?L:$(e[n],t)}function vi(t,e){for(var n in e){var i=e[n];if(Array.isArray(i))for(var r=0;r-1)return this;var n=E(arguments,1);return n.unshift(this),"function"===typeof t.install?t.install.apply(t,n):"function"===typeof t&&t.apply(null,n),e.push(t),this}}function Ci(t){t.mixin=function(t){return this.options=Xt(this.options,t),this}}function ki(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,i=n.cid,r=t._Ctor||(t._Ctor={});if(r[i])return r[i];var o=t.name||n.options.name;var a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=Xt(n.options,t),a["super"]=n,a.options.props&&ji(a),a.options.computed&&Ai(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,R.forEach((function(t){a[t]=n[t]})),o&&(a.options.components[o]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=T({},a.options),r[i]=a,a}}function ji(t){var e=t.options.props;for(var n in e)ri(t.prototype,"_props",n)}function Ai(t){var e=t.options.computed;for(var n in e)hi(t.prototype,n,e[n])}function $i(t){R.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&u(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&"function"===typeof n&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}function Ei(t){return t&&(t.Ctor.options.name||t.tag)}function Ti(t,e){return Array.isArray(t)?t.indexOf(e)>-1:"string"===typeof t?t.split(",").indexOf(e)>-1:!!h(t)&&t.test(e)}function Ii(t,e){var n=t.cache,i=t.keys,r=t._vnode;for(var o in n){var a=n[o];if(a){var s=Ei(a.componentOptions);s&&!e(s)&&Li(n,o,i,r)}}}function Li(t,e,n,i){var r=t[e];!r||i&&r.tag===i.tag||r.componentInstance.$destroy(),t[e]=null,b(n,e)}yi(_i),gi(_i),$n(_i),Ln(_i),bn(_i);var Mi=[String,RegExp,Array],Di={name:"keep-alive",abstract:!0,props:{include:Mi,exclude:Mi,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Li(this.cache,t,this.keys)},mounted:function(){var t=this;this.$watch("include",(function(e){Ii(t,(function(t){return Ti(e,t)}))})),this.$watch("exclude",(function(e){Ii(t,(function(t){return!Ti(e,t)}))}))},render:function(){var t=this.$slots.default,e=_n(t),n=e&&e.componentOptions;if(n){var i=Ei(n),r=this,o=r.include,a=r.exclude;if(o&&(!i||!Ti(o,i))||a&&i&&Ti(a,i))return e;var s=this,c=s.cache,l=s.keys,u=null==e.key?n.Ctor.cid+(n.tag?"::"+n.tag:""):e.key;c[u]?(e.componentInstance=c[u].componentInstance,b(l,u),l.push(u)):(c[u]=e,l.push(u),this.max&&l.length>parseInt(this.max)&&Li(c,l[0],l,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}},Bi={KeepAlive:Di};function Pi(t){var e={get:function(){return z}};Object.defineProperty(t,"config",e),t.util={warn:dt,extend:T,mergeOptions:Xt,defineReactive:Mt},t.set=Dt,t.delete=Bt,t.nextTick=pe,t.observable=function(t){return Lt(t),t},t.options=Object.create(null),R.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,T(t.options.components,Bi),Si(t),Ci(t),ki(t),$i(t)}Pi(_i),Object.defineProperty(_i.prototype,"$isServer",{get:ct}),Object.defineProperty(_i.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(_i,"FunctionalRenderContext",{value:Ze}),_i.version="2.6.11";var Ni=m("style,class"),Vi=m("input,textarea,option,select,progress"),Ri=function(t,e,n){return"value"===n&&Vi(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},Fi=m("contenteditable,draggable,spellcheck"),zi=m("events,caret,typing,plaintext-only"),Hi=function(t,e){return Xi(e)||"false"===e?"false":"contenteditable"===t&&zi(e)?e:"true"},Wi=m("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Ui="http://www.w3.org/1999/xlink",Yi=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},qi=function(t){return Yi(t)?t.slice(6,t.length):""},Xi=function(t){return null==t||!1===t};function Gi(t){var e=t.data,n=t,i=t;while(r(i.componentInstance))i=i.componentInstance._vnode,i&&i.data&&(e=Ki(i.data,e));while(r(n=n.parent))n&&n.data&&(e=Ki(e,n.data));return Zi(e.staticClass,e.class)}function Ki(t,e){return{staticClass:Ji(t.staticClass,e.staticClass),class:r(t.class)?[t.class,e.class]:e.class}}function Zi(t,e){return r(t)||r(e)?Ji(t,Qi(e)):""}function Ji(t,e){return t?e?t+" "+e:t:e||""}function Qi(t){return Array.isArray(t)?tr(t):c(t)?er(t):"string"===typeof t?t:""}function tr(t){for(var e,n="",i=0,o=t.length;i-1?sr[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:sr[t]=/HTMLUnknownElement/.test(e.toString())}var lr=m("text,number,password,search,email,tel,url");function ur(t){if("string"===typeof t){var e=document.querySelector(t);return e||document.createElement("div")}return t}function hr(t,e){var n=document.createElement(t);return"select"!==t||e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n}function fr(t,e){return document.createElementNS(nr[t],e)}function dr(t){return document.createTextNode(t)}function pr(t){return document.createComment(t)}function vr(t,e,n){t.insertBefore(e,n)}function mr(t,e){t.removeChild(e)}function gr(t,e){t.appendChild(e)}function br(t){return t.parentNode}function yr(t){return t.nextSibling}function wr(t){return t.tagName}function xr(t,e){t.textContent=e}function Or(t,e){t.setAttribute(e,"")}var _r=Object.freeze({createElement:hr,createElementNS:fr,createTextNode:dr,createComment:pr,insertBefore:vr,removeChild:mr,appendChild:gr,parentNode:br,nextSibling:yr,tagName:wr,setTextContent:xr,setStyleScope:Or}),Sr={create:function(t,e){Cr(e)},update:function(t,e){t.data.ref!==e.data.ref&&(Cr(t,!0),Cr(e))},destroy:function(t){Cr(t,!0)}};function Cr(t,e){var n=t.data.ref;if(r(n)){var i=t.context,o=t.componentInstance||t.elm,a=i.$refs;e?Array.isArray(a[n])?b(a[n],o):a[n]===o&&(a[n]=void 0):t.data.refInFor?Array.isArray(a[n])?a[n].indexOf(o)<0&&a[n].push(o):a[n]=[o]:a[n]=o}}var kr=new yt("",{},[]),jr=["create","activate","update","remove","destroy"];function Ar(t,e){return t.key===e.key&&(t.tag===e.tag&&t.isComment===e.isComment&&r(t.data)===r(e.data)&&$r(t,e)||o(t.isAsyncPlaceholder)&&t.asyncFactory===e.asyncFactory&&i(e.asyncFactory.error))}function $r(t,e){if("input"!==t.tag)return!0;var n,i=r(n=t.data)&&r(n=n.attrs)&&n.type,o=r(n=e.data)&&r(n=n.attrs)&&n.type;return i===o||lr(i)&&lr(o)}function Er(t,e,n){var i,o,a={};for(i=e;i<=n;++i)o=t[i].key,r(o)&&(a[o]=i);return a}function Tr(t){var e,n,a={},c=t.modules,l=t.nodeOps;for(e=0;ev?(h=i(n[b+1])?null:n[b+1].elm,_(t,h,n,p,b,o)):p>b&&C(e,f,v)}function A(t,e,n,i){for(var o=n;o-1?zr(t,e,n):Wi(e)?Xi(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Fi(e)?t.setAttribute(e,Hi(e,n)):Yi(e)?Xi(n)?t.removeAttributeNS(Ui,qi(e)):t.setAttributeNS(Ui,e,n):zr(t,e,n)}function zr(t,e,n){if(Xi(n))t.removeAttribute(e);else{if(tt&&!et&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var i=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",i)};t.addEventListener("input",i),t.__ieph=!0}t.setAttribute(e,n)}}var Hr={create:Rr,update:Rr};function Wr(t,e){var n=e.elm,o=e.data,a=t.data;if(!(i(o.staticClass)&&i(o.class)&&(i(a)||i(a.staticClass)&&i(a.class)))){var s=Gi(e),c=n._transitionClasses;r(c)&&(s=Ji(s,Qi(c))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var Ur,Yr={create:Wr,update:Wr},qr="__r",Xr="__c";function Gr(t){if(r(t[qr])){var e=tt?"change":"input";t[e]=[].concat(t[qr],t[e]||[]),delete t[qr]}r(t[Xr])&&(t.change=[].concat(t[Xr],t.change||[]),delete t[Xr])}function Kr(t,e,n){var i=Ur;return function r(){var o=e.apply(null,arguments);null!==o&&Qr(t,r,n,i)}}var Zr=ae&&!(rt&&Number(rt[1])<=53);function Jr(t,e,n,i){if(Zr){var r=qn,o=e;e=o._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=r||t.timeStamp<=0||t.target.ownerDocument!==document)return o.apply(this,arguments)}}Ur.addEventListener(t,e,at?{capture:n,passive:i}:n)}function Qr(t,e,n,i){(i||Ur).removeEventListener(t,e._wrapper||e,n)}function to(t,e){if(!i(t.data.on)||!i(e.data.on)){var n=e.data.on||{},r=t.data.on||{};Ur=e.elm,Gr(n),we(n,r,Jr,Qr,Kr,e.context),Ur=void 0}}var eo,no={create:to,update:to};function io(t,e){if(!i(t.data.domProps)||!i(e.data.domProps)){var n,o,a=e.elm,s=t.data.domProps||{},c=e.data.domProps||{};for(n in r(c.__ob__)&&(c=e.data.domProps=T({},c)),s)n in c||(a[n]="");for(n in c){if(o=c[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),o===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===n&&"PROGRESS"!==a.tagName){a._value=o;var l=i(o)?"":String(o);ro(a,l)&&(a.value=l)}else if("innerHTML"===n&&rr(a.tagName)&&i(a.innerHTML)){eo=eo||document.createElement("div"),eo.innerHTML=""+o+"";var u=eo.firstChild;while(a.firstChild)a.removeChild(a.firstChild);while(u.firstChild)a.appendChild(u.firstChild)}else if(o!==s[n])try{a[n]=o}catch(_a){}}}}function ro(t,e){return!t.composing&&("OPTION"===t.tagName||oo(t,e)||ao(t,e))}function oo(t,e){var n=!0;try{n=document.activeElement!==t}catch(_a){}return n&&t.value!==e}function ao(t,e){var n=t.value,i=t._vModifiers;if(r(i)){if(i.number)return v(n)!==v(e);if(i.trim)return n.trim()!==e.trim()}return n!==e}var so={create:io,update:io},co=x((function(t){var e={},n=/;(?![^(]*\))/g,i=/:(.+)/;return t.split(n).forEach((function(t){if(t){var n=t.split(i);n.length>1&&(e[n[0].trim()]=n[1].trim())}})),e}));function lo(t){var e=uo(t.style);return t.staticStyle?T(t.staticStyle,e):e}function uo(t){return Array.isArray(t)?I(t):"string"===typeof t?co(t):t}function ho(t,e){var n,i={};if(e){var r=t;while(r.componentInstance)r=r.componentInstance._vnode,r&&r.data&&(n=lo(r.data))&&T(i,n)}(n=lo(t.data))&&T(i,n);var o=t;while(o=o.parent)o.data&&(n=lo(o.data))&&T(i,n);return i}var fo,po=/^--/,vo=/\s*!important$/,mo=function(t,e,n){if(po.test(e))t.style.setProperty(e,n);else if(vo.test(n))t.style.setProperty(k(e),n.replace(vo,""),"important");else{var i=bo(e);if(Array.isArray(n))for(var r=0,o=n.length;r-1?e.split(xo).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" "+(t.getAttribute("class")||"")+" ";n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function _o(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(xo).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{var n=" "+(t.getAttribute("class")||"")+" ",i=" "+e+" ";while(n.indexOf(i)>=0)n=n.replace(i," ");n=n.trim(),n?t.setAttribute("class",n):t.removeAttribute("class")}}function So(t){if(t){if("object"===typeof t){var e={};return!1!==t.css&&T(e,Co(t.name||"v")),T(e,t),e}return"string"===typeof t?Co(t):void 0}}var Co=x((function(t){return{enterClass:t+"-enter",enterToClass:t+"-enter-to",enterActiveClass:t+"-enter-active",leaveClass:t+"-leave",leaveToClass:t+"-leave-to",leaveActiveClass:t+"-leave-active"}})),ko=K&&!et,jo="transition",Ao="animation",$o="transition",Eo="transitionend",To="animation",Io="animationend";ko&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&($o="WebkitTransition",Eo="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(To="WebkitAnimation",Io="webkitAnimationEnd"));var Lo=K?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Mo(t){Lo((function(){Lo(t)}))}function Do(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),Oo(t,e))}function Bo(t,e){t._transitionClasses&&b(t._transitionClasses,e),_o(t,e)}function Po(t,e,n){var i=Vo(t,e),r=i.type,o=i.timeout,a=i.propCount;if(!r)return n();var s=r===jo?Eo:Io,c=0,l=function(){t.removeEventListener(s,u),n()},u=function(e){e.target===t&&++c>=a&&l()};setTimeout((function(){c0&&(n=jo,u=a,h=o.length):e===Ao?l>0&&(n=Ao,u=l,h=c.length):(u=Math.max(a,l),n=u>0?a>l?jo:Ao:null,h=n?n===jo?o.length:c.length:0);var f=n===jo&&No.test(i[$o+"Property"]);return{type:n,timeout:u,propCount:h,hasTransform:f}}function Ro(t,e){while(t.length1}function Yo(t,e){!0!==e.data.show&&zo(e)}var qo=K?{create:Yo,activate:Yo,remove:function(t,e){!0!==t.data.show?Ho(t,e):e()}}:{},Xo=[Hr,Yr,no,so,wo,qo],Go=Xo.concat(Vr),Ko=Tr({nodeOps:_r,modules:Go});et&&document.addEventListener("selectionchange",(function(){var t=document.activeElement;t&&t.vmodel&&ra(t,"input")}));var Zo={inserted:function(t,e,n,i){"select"===n.tag?(i.elm&&!i.elm._vOptions?xe(n,"postpatch",(function(){Zo.componentUpdated(t,e,n)})):Jo(t,e,n.context),t._vOptions=[].map.call(t.options,ea)):("textarea"===n.tag||lr(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener("compositionstart",na),t.addEventListener("compositionend",ia),t.addEventListener("change",ia),et&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if("select"===n.tag){Jo(t,e,n.context);var i=t._vOptions,r=t._vOptions=[].map.call(t.options,ea);if(r.some((function(t,e){return!B(t,i[e])}))){var o=t.multiple?e.value.some((function(t){return ta(t,r)})):e.value!==e.oldValue&&ta(e.value,r);o&&ra(t,"change")}}}};function Jo(t,e,n){Qo(t,e,n),(tt||nt)&&setTimeout((function(){Qo(t,e,n)}),0)}function Qo(t,e,n){var i=e.value,r=t.multiple;if(!r||Array.isArray(i)){for(var o,a,s=0,c=t.options.length;s-1,a.selected!==o&&(a.selected=o);else if(B(ea(a),i))return void(t.selectedIndex!==s&&(t.selectedIndex=s));r||(t.selectedIndex=-1)}}function ta(t,e){return e.every((function(e){return!B(e,t)}))}function ea(t){return"_value"in t?t._value:t.value}function na(t){t.target.composing=!0}function ia(t){t.target.composing&&(t.target.composing=!1,ra(t.target,"input"))}function ra(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function oa(t){return!t.componentInstance||t.data&&t.data.transition?t:oa(t.componentInstance._vnode)}var aa={bind:function(t,e,n){var i=e.value;n=oa(n);var r=n.data&&n.data.transition,o=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;i&&r?(n.data.show=!0,zo(n,(function(){t.style.display=o}))):t.style.display=i?o:"none"},update:function(t,e,n){var i=e.value,r=e.oldValue;if(!i!==!r){n=oa(n);var o=n.data&&n.data.transition;o?(n.data.show=!0,i?zo(n,(function(){t.style.display=t.__vOriginalDisplay})):Ho(n,(function(){t.style.display="none"}))):t.style.display=i?t.__vOriginalDisplay:"none"}},unbind:function(t,e,n,i,r){r||(t.style.display=t.__vOriginalDisplay)}},sa={model:Zo,show:aa},ca={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function la(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?la(_n(e.children)):t}function ua(t){var e={},n=t.$options;for(var i in n.propsData)e[i]=t[i];var r=n._parentListeners;for(var o in r)e[_(o)]=r[o];return e}function ha(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}function fa(t){while(t=t.parent)if(t.data.transition)return!0}function da(t,e){return e.key===t.key&&e.tag===t.tag}var pa=function(t){return t.tag||On(t)},va=function(t){return"show"===t.name},ma={name:"transition",props:ca,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(pa),n.length)){0;var i=this.mode;0;var r=n[0];if(fa(this.$vnode))return r;var o=la(r);if(!o)return r;if(this._leaving)return ha(t,r);var a="__transition-"+this._uid+"-";o.key=null==o.key?o.isComment?a+"comment":a+o.tag:s(o.key)?0===String(o.key).indexOf(a)?o.key:a+o.key:o.key;var c=(o.data||(o.data={})).transition=ua(this),l=this._vnode,u=la(l);if(o.data.directives&&o.data.directives.some(va)&&(o.data.show=!0),u&&u.data&&!da(o,u)&&!On(u)&&(!u.componentInstance||!u.componentInstance._vnode.isComment)){var h=u.data.transition=T({},c);if("out-in"===i)return this._leaving=!0,xe(h,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),ha(t,r);if("in-out"===i){if(On(o))return l;var f,d=function(){f()};xe(c,"afterEnter",d),xe(c,"enterCancelled",d),xe(h,"delayLeave",(function(t){f=t}))}}return r}}},ga=T({tag:String,moveClass:String},ca);delete ga.mode;var ba={props:ga,beforeMount:function(){var t=this,e=this._update;this._update=function(n,i){var r=Tn(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,r(),e.call(t,n,i)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),i=this.prevChildren=this.children,r=this.$slots.default||[],o=this.children=[],a=ua(this),s=0;s4)return t;for(n=[],i=0;i1&&"0"==r.charAt(0)&&(o=L.test(r)?16:8,r=r.slice(8==o?1:2)),""===r)a=0;else{if(!(10==o?D:8==o?M:B).test(r))return t;a=parseInt(r,o)}n.push(a)}for(i=0;i=C(256,5-e))return null}else if(a>255)return null;for(s=n.pop(),i=0;i6)return;i=0;while(f()){if(r=null,i>0){if(!("."==f()&&i<4))return;h++}if(!I.test(f()))return;while(I.test(f())){if(o=parseInt(f(),10),null===r)r=o;else{if(0==r)return;r=10*r+o}if(r>255)return;h++}c[l]=256*c[l]+r,i++,2!=i&&4!=i||l++}if(4!=i)return;break}if(":"==f()){if(h++,!f())return}else if(f())return;c[l++]=e}else{if(null!==u)return;h++,l++,u=l}}if(null!==u){a=l-u,l=7;while(0!=l&&a>0)s=c[l],c[l--]=c[u+a-1],c[u+--a]=s}else if(8!=l)return;return c},W=function(t){for(var e=null,n=1,i=null,r=0,o=0;o<8;o++)0!==t[o]?(r>n&&(e=i,n=r),i=null,r=0):(null===i&&(i=o),++r);return r>n&&(e=i,n=r),e},U=function(t){var e,n,i,r;if("number"==typeof t){for(e=[],n=0;n<4;n++)e.unshift(t%256),t=S(t/256);return e.join(".")}if("object"==typeof t){for(e="",i=W(t),n=0;n<8;n++)r&&0===t[n]||(r&&(r=!1),i===n?(e+=n?":":"::",r=!0):(e+=t[n].toString(16),n<7&&(e+=":")));return"["+e+"]"}return t},Y={},q=f({},Y,{" ":1,'"':1,"<":1,">":1,"`":1}),X=f({},q,{"#":1,"?":1,"{":1,"}":1}),G=f({},X,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),K=function(t,e){var n=p(t,0);return n>32&&n<127&&!h(e,t)?t:encodeURIComponent(t)},Z={ftp:21,file:null,http:80,https:443,ws:80,wss:443},J=function(t){return h(Z,t.scheme)},Q=function(t){return""!=t.username||""!=t.password},tt=function(t){return!t.host||t.cannotBeABaseURL||"file"==t.scheme},et=function(t,e){var n;return 2==t.length&&E.test(t.charAt(0))&&(":"==(n=t.charAt(1))||!e&&"|"==n)},nt=function(t){var e;return t.length>1&&et(t.slice(0,2))&&(2==t.length||"/"===(e=t.charAt(2))||"\\"===e||"?"===e||"#"===e)},it=function(t){var e=t.path,n=e.length;!n||"file"==t.scheme&&1==n&&et(e[0],!0)||e.pop()},rt=function(t){return"."===t||"%2e"===t.toLowerCase()},ot=function(t){return t=t.toLowerCase(),".."===t||"%2e."===t||".%2e"===t||"%2e%2e"===t},at={},st={},ct={},lt={},ut={},ht={},ft={},dt={},pt={},vt={},mt={},gt={},bt={},yt={},wt={},xt={},Ot={},_t={},St={},Ct={},kt={},jt=function(t,e,n,r){var o,a,s,c,l=n||at,u=0,f="",p=!1,v=!1,m=!1;n||(t.scheme="",t.username="",t.password="",t.host=null,t.port=null,t.path=[],t.query=null,t.fragment=null,t.cannotBeABaseURL=!1,e=e.replace(V,"")),e=e.replace(R,""),o=d(e);while(u<=o.length){switch(a=o[u],l){case at:if(!a||!E.test(a)){if(n)return j;l=ct;continue}f+=a.toLowerCase(),l=st;break;case st:if(a&&(T.test(a)||"+"==a||"-"==a||"."==a))f+=a.toLowerCase();else{if(":"!=a){if(n)return j;f="",l=ct,u=0;continue}if(n&&(J(t)!=h(Z,f)||"file"==f&&(Q(t)||null!==t.port)||"file"==t.scheme&&!t.host))return;if(t.scheme=f,n)return void(J(t)&&Z[t.scheme]==t.port&&(t.port=null));f="","file"==t.scheme?l=yt:J(t)&&r&&r.scheme==t.scheme?l=lt:J(t)?l=dt:"/"==o[u+1]?(l=ut,u++):(t.cannotBeABaseURL=!0,t.path.push(""),l=St)}break;case ct:if(!r||r.cannotBeABaseURL&&"#"!=a)return j;if(r.cannotBeABaseURL&&"#"==a){t.scheme=r.scheme,t.path=r.path.slice(),t.query=r.query,t.fragment="",t.cannotBeABaseURL=!0,l=kt;break}l="file"==r.scheme?yt:ht;continue;case lt:if("/"!=a||"/"!=o[u+1]){l=ht;continue}l=pt,u++;break;case ut:if("/"==a){l=vt;break}l=_t;continue;case ht:if(t.scheme=r.scheme,a==i)t.username=r.username,t.password=r.password,t.host=r.host,t.port=r.port,t.path=r.path.slice(),t.query=r.query;else if("/"==a||"\\"==a&&J(t))l=ft;else if("?"==a)t.username=r.username,t.password=r.password,t.host=r.host,t.port=r.port,t.path=r.path.slice(),t.query="",l=Ct;else{if("#"!=a){t.username=r.username,t.password=r.password,t.host=r.host,t.port=r.port,t.path=r.path.slice(),t.path.pop(),l=_t;continue}t.username=r.username,t.password=r.password,t.host=r.host,t.port=r.port,t.path=r.path.slice(),t.query=r.query,t.fragment="",l=kt}break;case ft:if(!J(t)||"/"!=a&&"\\"!=a){if("/"!=a){t.username=r.username,t.password=r.password,t.host=r.host,t.port=r.port,l=_t;continue}l=vt}else l=pt;break;case dt:if(l=pt,"/"!=a||"/"!=f.charAt(u+1))continue;u++;break;case pt:if("/"!=a&&"\\"!=a){l=vt;continue}break;case vt:if("@"==a){p&&(f="%40"+f),p=!0,s=d(f);for(var g=0;g65535)return $;t.port=J(t)&&w===Z[t.scheme]?null:w,f=""}if(n)return;l=Ot;continue}return $}f+=a;break;case yt:if(t.scheme="file","/"==a||"\\"==a)l=wt;else{if(!r||"file"!=r.scheme){l=_t;continue}if(a==i)t.host=r.host,t.path=r.path.slice(),t.query=r.query;else if("?"==a)t.host=r.host,t.path=r.path.slice(),t.query="",l=Ct;else{if("#"!=a){nt(o.slice(u).join(""))||(t.host=r.host,t.path=r.path.slice(),it(t)),l=_t;continue}t.host=r.host,t.path=r.path.slice(),t.query=r.query,t.fragment="",l=kt}}break;case wt:if("/"==a||"\\"==a){l=xt;break}r&&"file"==r.scheme&&!nt(o.slice(u).join(""))&&(et(r.path[0],!0)?t.path.push(r.path[0]):t.host=r.host),l=_t;continue;case xt:if(a==i||"/"==a||"\\"==a||"?"==a||"#"==a){if(!n&&et(f))l=_t;else if(""==f){if(t.host="",n)return;l=Ot}else{if(c=F(t,f),c)return c;if("localhost"==t.host&&(t.host=""),n)return;f="",l=Ot}continue}f+=a;break;case Ot:if(J(t)){if(l=_t,"/"!=a&&"\\"!=a)continue}else if(n||"?"!=a)if(n||"#"!=a){if(a!=i&&(l=_t,"/"!=a))continue}else t.fragment="",l=kt;else t.query="",l=Ct;break;case _t:if(a==i||"/"==a||"\\"==a&&J(t)||!n&&("?"==a||"#"==a)){if(ot(f)?(it(t),"/"==a||"\\"==a&&J(t)||t.path.push("")):rt(f)?"/"==a||"\\"==a&&J(t)||t.path.push(""):("file"==t.scheme&&!t.path.length&&et(f)&&(t.host&&(t.host=""),f=f.charAt(0)+":"),t.path.push(f)),f="","file"==t.scheme&&(a==i||"?"==a||"#"==a))while(t.path.length>1&&""===t.path[0])t.path.shift();"?"==a?(t.query="",l=Ct):"#"==a&&(t.fragment="",l=kt)}else f+=K(a,X);break;case St:"?"==a?(t.query="",l=Ct):"#"==a?(t.fragment="",l=kt):a!=i&&(t.path[0]+=K(a,Y));break;case Ct:n||"#"!=a?a!=i&&("'"==a&&J(t)?t.query+="%27":t.query+="#"==a?"%23":K(a,Y)):(t.fragment="",l=kt);break;case kt:a!=i&&(t.fragment+=K(a,q));break}u++}},At=function(t){var e,n,i=u(this,At,"URL"),r=arguments.length>1?arguments[1]:void 0,a=String(t),s=O(i,{type:"URL"});if(void 0!==r)if(r instanceof At)e=_(r);else if(n=jt(e={},String(r)),n)throw TypeError(n);if(n=jt(s,a,null,e),n)throw TypeError(n);var c=s.searchParams=new w,l=x(c);l.updateSearchParams(s.query),l.updateURL=function(){s.query=String(c)||null},o||(i.href=Et.call(i),i.origin=Tt.call(i),i.protocol=It.call(i),i.username=Lt.call(i),i.password=Mt.call(i),i.host=Dt.call(i),i.hostname=Bt.call(i),i.port=Pt.call(i),i.pathname=Nt.call(i),i.search=Vt.call(i),i.searchParams=Rt.call(i),i.hash=Ft.call(i))},$t=At.prototype,Et=function(){var t=_(this),e=t.scheme,n=t.username,i=t.password,r=t.host,o=t.port,a=t.path,s=t.query,c=t.fragment,l=e+":";return null!==r?(l+="//",Q(t)&&(l+=n+(i?":"+i:"")+"@"),l+=U(r),null!==o&&(l+=":"+o)):"file"==e&&(l+="//"),l+=t.cannotBeABaseURL?a[0]:a.length?"/"+a.join("/"):"",null!==s&&(l+="?"+s),null!==c&&(l+="#"+c),l},Tt=function(){var t=_(this),e=t.scheme,n=t.port;if("blob"==e)try{return new URL(e.path[0]).origin}catch(i){return"null"}return"file"!=e&&J(t)?e+"://"+U(t.host)+(null!==n?":"+n:""):"null"},It=function(){return _(this).scheme+":"},Lt=function(){return _(this).username},Mt=function(){return _(this).password},Dt=function(){var t=_(this),e=t.host,n=t.port;return null===e?"":null===n?U(e):U(e)+":"+n},Bt=function(){var t=_(this).host;return null===t?"":U(t)},Pt=function(){var t=_(this).port;return null===t?"":String(t)},Nt=function(){var t=_(this),e=t.path;return t.cannotBeABaseURL?e[0]:e.length?"/"+e.join("/"):""},Vt=function(){var t=_(this).query;return t?"?"+t:""},Rt=function(){return _(this).searchParams},Ft=function(){var t=_(this).fragment;return t?"#"+t:""},zt=function(t,e){return{get:t,set:e,configurable:!0,enumerable:!0}};if(o&&c($t,{href:zt(Et,(function(t){var e=_(this),n=String(t),i=jt(e,n);if(i)throw TypeError(i);x(e.searchParams).updateSearchParams(e.query)})),origin:zt(Tt),protocol:zt(It,(function(t){var e=_(this);jt(e,String(t)+":",at)})),username:zt(Lt,(function(t){var e=_(this),n=d(String(t));if(!tt(e)){e.username="";for(var i=0;i1?arguments[1]:void 0,e.length)),i=String(t);return u?u.call(e,i,n):e.slice(n,n+i.length)===i}})},"2cf4":function(t,e,n){var i,r,o,a=n("da84"),s=n("d039"),c=n("c6b6"),l=n("0366"),u=n("1be4"),h=n("cc12"),f=n("1cdc"),d=a.location,p=a.setImmediate,v=a.clearImmediate,m=a.process,g=a.MessageChannel,b=a.Dispatch,y=0,w={},x="onreadystatechange",O=function(t){if(w.hasOwnProperty(t)){var e=w[t];delete w[t],e()}},_=function(t){return function(){O(t)}},S=function(t){O(t.data)},C=function(t){a.postMessage(t+"",d.protocol+"//"+d.host)};p&&v||(p=function(t){var e=[],n=1;while(arguments.length>n)e.push(arguments[n++]);return w[++y]=function(){("function"==typeof t?t:Function(t)).apply(void 0,e)},i(y),y},v=function(t){delete w[t]},"process"==c(m)?i=function(t){m.nextTick(_(t))}:b&&b.now?i=function(t){b.now(_(t))}:g&&!f?(r=new g,o=r.port2,r.port1.onmessage=S,i=l(o.postMessage,o,1)):!a.addEventListener||"function"!=typeof postMessage||a.importScripts||s(C)||"file:"===d.protocol?i=x in h("script")?function(t){u.appendChild(h("script"))[x]=function(){u.removeChild(this),O(t)}}:function(t){setTimeout(_(t),0)}:(i=C,a.addEventListener("message",S,!1))),t.exports={set:p,clear:v}},"2d00":function(t,e,n){var i,r,o=n("da84"),a=n("342f"),s=o.process,c=s&&s.versions,l=c&&c.v8;l?(i=l.split("."),r=i[0]+i[1]):a&&(i=a.match(/Edge\/(\d+)/),(!i||i[1]>=74)&&(i=a.match(/Chrome\/(\d+)/),i&&(r=i[1]))),t.exports=r&&+r},"2f62":function(t,e,n){"use strict";(function(t){ +/*! + * vuex v3.5.1 + * (c) 2020 Evan You + * @license MIT + */ +function n(t){var e=Number(t.version.split(".")[0]);if(e>=2)t.mixin({beforeCreate:i});else{var n=t.prototype._init;t.prototype._init=function(t){void 0===t&&(t={}),t.init=t.init?[i].concat(t.init):i,n.call(this,t)}}function i(){var t=this.$options;t.store?this.$store="function"===typeof t.store?t.store():t.store:t.parent&&t.parent.$store&&(this.$store=t.parent.$store)}}var i="undefined"!==typeof window?window:"undefined"!==typeof t?t:{},r=i.__VUE_DEVTOOLS_GLOBAL_HOOK__;function o(t){r&&(t._devtoolHook=r,r.emit("vuex:init",t),r.on("vuex:travel-to-state",(function(e){t.replaceState(e)})),t.subscribe((function(t,e){r.emit("vuex:mutation",t,e)}),{prepend:!0}),t.subscribeAction((function(t,e){r.emit("vuex:action",t,e)}),{prepend:!0}))}function a(t,e){return t.filter(e)[0]}function s(t,e){if(void 0===e&&(e=[]),null===t||"object"!==typeof t)return t;var n=a(e,(function(e){return e.original===t}));if(n)return n.copy;var i=Array.isArray(t)?[]:{};return e.push({original:t,copy:i}),Object.keys(t).forEach((function(n){i[n]=s(t[n],e)})),i}function c(t,e){Object.keys(t).forEach((function(n){return e(t[n],n)}))}function l(t){return null!==t&&"object"===typeof t}function u(t){return t&&"function"===typeof t.then}function h(t,e){return function(){return t(e)}}var f=function(t,e){this.runtime=e,this._children=Object.create(null),this._rawModule=t;var n=t.state;this.state=("function"===typeof n?n():n)||{}},d={namespaced:{configurable:!0}};d.namespaced.get=function(){return!!this._rawModule.namespaced},f.prototype.addChild=function(t,e){this._children[t]=e},f.prototype.removeChild=function(t){delete this._children[t]},f.prototype.getChild=function(t){return this._children[t]},f.prototype.hasChild=function(t){return t in this._children},f.prototype.update=function(t){this._rawModule.namespaced=t.namespaced,t.actions&&(this._rawModule.actions=t.actions),t.mutations&&(this._rawModule.mutations=t.mutations),t.getters&&(this._rawModule.getters=t.getters)},f.prototype.forEachChild=function(t){c(this._children,t)},f.prototype.forEachGetter=function(t){this._rawModule.getters&&c(this._rawModule.getters,t)},f.prototype.forEachAction=function(t){this._rawModule.actions&&c(this._rawModule.actions,t)},f.prototype.forEachMutation=function(t){this._rawModule.mutations&&c(this._rawModule.mutations,t)},Object.defineProperties(f.prototype,d);var p=function(t){this.register([],t,!1)};function v(t,e,n){if(e.update(n),n.modules)for(var i in n.modules){if(!e.getChild(i))return void 0;v(t.concat(i),e.getChild(i),n.modules[i])}}p.prototype.get=function(t){return t.reduce((function(t,e){return t.getChild(e)}),this.root)},p.prototype.getNamespace=function(t){var e=this.root;return t.reduce((function(t,n){return e=e.getChild(n),t+(e.namespaced?n+"/":"")}),"")},p.prototype.update=function(t){v([],this.root,t)},p.prototype.register=function(t,e,n){var i=this;void 0===n&&(n=!0);var r=new f(e,n);if(0===t.length)this.root=r;else{var o=this.get(t.slice(0,-1));o.addChild(t[t.length-1],r)}e.modules&&c(e.modules,(function(e,r){i.register(t.concat(r),e,n)}))},p.prototype.unregister=function(t){var e=this.get(t.slice(0,-1)),n=t[t.length-1],i=e.getChild(n);i&&i.runtime&&e.removeChild(n)},p.prototype.isRegistered=function(t){var e=this.get(t.slice(0,-1)),n=t[t.length-1];return e.hasChild(n)};var m;var g=function(t){var e=this;void 0===t&&(t={}),!m&&"undefined"!==typeof window&&window.Vue&&T(window.Vue);var n=t.plugins;void 0===n&&(n=[]);var i=t.strict;void 0===i&&(i=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new p(t),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new m,this._makeLocalGettersCache=Object.create(null);var r=this,a=this,s=a.dispatch,c=a.commit;this.dispatch=function(t,e){return s.call(r,t,e)},this.commit=function(t,e,n){return c.call(r,t,e,n)},this.strict=i;var l=this._modules.root.state;O(this,l,[],this._modules.root),x(this,l),n.forEach((function(t){return t(e)}));var u=void 0!==t.devtools?t.devtools:m.config.devtools;u&&o(this)},b={state:{configurable:!0}};function y(t,e,n){return e.indexOf(t)<0&&(n&&n.prepend?e.unshift(t):e.push(t)),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}function w(t,e){t._actions=Object.create(null),t._mutations=Object.create(null),t._wrappedGetters=Object.create(null),t._modulesNamespaceMap=Object.create(null);var n=t.state;O(t,n,[],t._modules.root,!0),x(t,n,e)}function x(t,e,n){var i=t._vm;t.getters={},t._makeLocalGettersCache=Object.create(null);var r=t._wrappedGetters,o={};c(r,(function(e,n){o[n]=h(e,t),Object.defineProperty(t.getters,n,{get:function(){return t._vm[n]},enumerable:!0})}));var a=m.config.silent;m.config.silent=!0,t._vm=new m({data:{$$state:e},computed:o}),m.config.silent=a,t.strict&&A(t),i&&(n&&t._withCommit((function(){i._data.$$state=null})),m.nextTick((function(){return i.$destroy()})))}function O(t,e,n,i,r){var o=!n.length,a=t._modules.getNamespace(n);if(i.namespaced&&(t._modulesNamespaceMap[a],t._modulesNamespaceMap[a]=i),!o&&!r){var s=$(e,n.slice(0,-1)),c=n[n.length-1];t._withCommit((function(){m.set(s,c,i.state)}))}var l=i.context=_(t,a,n);i.forEachMutation((function(e,n){var i=a+n;C(t,i,e,l)})),i.forEachAction((function(e,n){var i=e.root?n:a+n,r=e.handler||e;k(t,i,r,l)})),i.forEachGetter((function(e,n){var i=a+n;j(t,i,e,l)})),i.forEachChild((function(i,o){O(t,e,n.concat(o),i,r)}))}function _(t,e,n){var i=""===e,r={dispatch:i?t.dispatch:function(n,i,r){var o=E(n,i,r),a=o.payload,s=o.options,c=o.type;return s&&s.root||(c=e+c),t.dispatch(c,a)},commit:i?t.commit:function(n,i,r){var o=E(n,i,r),a=o.payload,s=o.options,c=o.type;s&&s.root||(c=e+c),t.commit(c,a,s)}};return Object.defineProperties(r,{getters:{get:i?function(){return t.getters}:function(){return S(t,e)}},state:{get:function(){return $(t.state,n)}}}),r}function S(t,e){if(!t._makeLocalGettersCache[e]){var n={},i=e.length;Object.keys(t.getters).forEach((function(r){if(r.slice(0,i)===e){var o=r.slice(i);Object.defineProperty(n,o,{get:function(){return t.getters[r]},enumerable:!0})}})),t._makeLocalGettersCache[e]=n}return t._makeLocalGettersCache[e]}function C(t,e,n,i){var r=t._mutations[e]||(t._mutations[e]=[]);r.push((function(e){n.call(t,i.state,e)}))}function k(t,e,n,i){var r=t._actions[e]||(t._actions[e]=[]);r.push((function(e){var r=n.call(t,{dispatch:i.dispatch,commit:i.commit,getters:i.getters,state:i.state,rootGetters:t.getters,rootState:t.state},e);return u(r)||(r=Promise.resolve(r)),t._devtoolHook?r.catch((function(e){throw t._devtoolHook.emit("vuex:error",e),e})):r}))}function j(t,e,n,i){t._wrappedGetters[e]||(t._wrappedGetters[e]=function(t){return n(i.state,i.getters,t.state,t.getters)})}function A(t){t._vm.$watch((function(){return this._data.$$state}),(function(){0}),{deep:!0,sync:!0})}function $(t,e){return e.reduce((function(t,e){return t[e]}),t)}function E(t,e,n){return l(t)&&t.type&&(n=e,e=t,t=t.type),{type:t,payload:e,options:n}}function T(t){m&&t===m||(m=t,n(m))}b.state.get=function(){return this._vm._data.$$state},b.state.set=function(t){0},g.prototype.commit=function(t,e,n){var i=this,r=E(t,e,n),o=r.type,a=r.payload,s=(r.options,{type:o,payload:a}),c=this._mutations[o];c&&(this._withCommit((function(){c.forEach((function(t){t(a)}))})),this._subscribers.slice().forEach((function(t){return t(s,i.state)})))},g.prototype.dispatch=function(t,e){var n=this,i=E(t,e),r=i.type,o=i.payload,a={type:r,payload:o},s=this._actions[r];if(s){try{this._actionSubscribers.slice().filter((function(t){return t.before})).forEach((function(t){return t.before(a,n.state)}))}catch(l){0}var c=s.length>1?Promise.all(s.map((function(t){return t(o)}))):s[0](o);return new Promise((function(t,e){c.then((function(e){try{n._actionSubscribers.filter((function(t){return t.after})).forEach((function(t){return t.after(a,n.state)}))}catch(l){0}t(e)}),(function(t){try{n._actionSubscribers.filter((function(t){return t.error})).forEach((function(e){return e.error(a,n.state,t)}))}catch(l){0}e(t)}))}))}},g.prototype.subscribe=function(t,e){return y(t,this._subscribers,e)},g.prototype.subscribeAction=function(t,e){var n="function"===typeof t?{before:t}:t;return y(n,this._actionSubscribers,e)},g.prototype.watch=function(t,e,n){var i=this;return this._watcherVM.$watch((function(){return t(i.state,i.getters)}),e,n)},g.prototype.replaceState=function(t){var e=this;this._withCommit((function(){e._vm._data.$$state=t}))},g.prototype.registerModule=function(t,e,n){void 0===n&&(n={}),"string"===typeof t&&(t=[t]),this._modules.register(t,e),O(this,this.state,t,this._modules.get(t),n.preserveState),x(this,this.state)},g.prototype.unregisterModule=function(t){var e=this;"string"===typeof t&&(t=[t]),this._modules.unregister(t),this._withCommit((function(){var n=$(e.state,t.slice(0,-1));m.delete(n,t[t.length-1])})),w(this)},g.prototype.hasModule=function(t){return"string"===typeof t&&(t=[t]),this._modules.isRegistered(t)},g.prototype.hotUpdate=function(t){this._modules.update(t),w(this,!0)},g.prototype._withCommit=function(t){var e=this._committing;this._committing=!0,t(),this._committing=e},Object.defineProperties(g.prototype,b);var I=V((function(t,e){var n={};return P(e).forEach((function(e){var i=e.key,r=e.val;n[i]=function(){var e=this.$store.state,n=this.$store.getters;if(t){var i=R(this.$store,"mapState",t);if(!i)return;e=i.context.state,n=i.context.getters}return"function"===typeof r?r.call(this,e,n):e[r]},n[i].vuex=!0})),n})),L=V((function(t,e){var n={};return P(e).forEach((function(e){var i=e.key,r=e.val;n[i]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var i=this.$store.commit;if(t){var o=R(this.$store,"mapMutations",t);if(!o)return;i=o.context.commit}return"function"===typeof r?r.apply(this,[i].concat(e)):i.apply(this.$store,[r].concat(e))}})),n})),M=V((function(t,e){var n={};return P(e).forEach((function(e){var i=e.key,r=e.val;r=t+r,n[i]=function(){if(!t||R(this.$store,"mapGetters",t))return this.$store.getters[r]},n[i].vuex=!0})),n})),D=V((function(t,e){var n={};return P(e).forEach((function(e){var i=e.key,r=e.val;n[i]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var i=this.$store.dispatch;if(t){var o=R(this.$store,"mapActions",t);if(!o)return;i=o.context.dispatch}return"function"===typeof r?r.apply(this,[i].concat(e)):i.apply(this.$store,[r].concat(e))}})),n})),B=function(t){return{mapState:I.bind(null,t),mapGetters:M.bind(null,t),mapMutations:L.bind(null,t),mapActions:D.bind(null,t)}};function P(t){return N(t)?Array.isArray(t)?t.map((function(t){return{key:t,val:t}})):Object.keys(t).map((function(e){return{key:e,val:t[e]}})):[]}function N(t){return Array.isArray(t)||l(t)}function V(t){return function(e,n){return"string"!==typeof e?(n=e,e=""):"/"!==e.charAt(e.length-1)&&(e+="/"),t(e,n)}}function R(t,e,n){var i=t._modulesNamespaceMap[n];return i}function F(t){void 0===t&&(t={});var e=t.collapsed;void 0===e&&(e=!0);var n=t.filter;void 0===n&&(n=function(t,e,n){return!0});var i=t.transformer;void 0===i&&(i=function(t){return t});var r=t.mutationTransformer;void 0===r&&(r=function(t){return t});var o=t.actionFilter;void 0===o&&(o=function(t,e){return!0});var a=t.actionTransformer;void 0===a&&(a=function(t){return t});var c=t.logMutations;void 0===c&&(c=!0);var l=t.logActions;void 0===l&&(l=!0);var u=t.logger;return void 0===u&&(u=console),function(t){var h=s(t.state);"undefined"!==typeof u&&(c&&t.subscribe((function(t,o){var a=s(o);if(n(t,h,a)){var c=W(),l=r(t),f="mutation "+t.type+c;z(u,f,e),u.log("%c prev state","color: #9E9E9E; font-weight: bold",i(h)),u.log("%c mutation","color: #03A9F4; font-weight: bold",l),u.log("%c next state","color: #4CAF50; font-weight: bold",i(a)),H(u)}h=a})),l&&t.subscribeAction((function(t,n){if(o(t,n)){var i=W(),r=a(t),s="action "+t.type+i;z(u,s,e),u.log("%c action","color: #03A9F4; font-weight: bold",r),H(u)}})))}}function z(t,e,n){var i=n?t.groupCollapsed:t.group;try{i.call(t,e)}catch(r){t.log(e)}}function H(t){try{t.groupEnd()}catch(e){t.log("—— log end ——")}}function W(){var t=new Date;return" @ "+Y(t.getHours(),2)+":"+Y(t.getMinutes(),2)+":"+Y(t.getSeconds(),2)+"."+Y(t.getMilliseconds(),3)}function U(t,e){return new Array(e+1).join(t)}function Y(t,e){return U("0",e-t.toString().length)+t}var q={Store:g,install:T,version:"3.5.1",mapState:I,mapMutations:L,mapGetters:M,mapActions:D,createNamespacedHelpers:B,createLogger:F};e["a"]=q}).call(this,n("c8ba"))},"2fa4":function(t,e,n){"use strict";n("20f6");var i=n("80d2");e["a"]=Object(i["h"])("spacer","div","v-spacer")},"310e":function(t,e,n){t.exports=function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(i,r,function(e){return t[e]}.bind(null,r));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s="fb15")}({"01f9":function(t,e,n){"use strict";var i=n("2d00"),r=n("5ca1"),o=n("2aba"),a=n("32e9"),s=n("84f2"),c=n("41a0"),l=n("7f20"),u=n("38fd"),h=n("2b4c")("iterator"),f=!([].keys&&"next"in[].keys()),d="@@iterator",p="keys",v="values",m=function(){return this};t.exports=function(t,e,n,g,b,y,w){c(n,e,g);var x,O,_,S=function(t){if(!f&&t in A)return A[t];switch(t){case p:return function(){return new n(this,t)};case v:return function(){return new n(this,t)}}return function(){return new n(this,t)}},C=e+" Iterator",k=b==v,j=!1,A=t.prototype,$=A[h]||A[d]||b&&A[b],E=$||S(b),T=b?k?S("entries"):E:void 0,I="Array"==e&&A.entries||$;if(I&&(_=u(I.call(new t)),_!==Object.prototype&&_.next&&(l(_,C,!0),i||"function"==typeof _[h]||a(_,h,m))),k&&$&&$.name!==v&&(j=!0,E=function(){return $.call(this)}),i&&!w||!f&&!j&&A[h]||a(A,h,E),s[e]=E,s[C]=m,b)if(x={values:k?E:S(v),keys:y?E:S(p),entries:T},w)for(O in x)O in A||o(A,O,x[O]);else r(r.P+r.F*(f||j),e,x);return x}},"02f4":function(t,e,n){var i=n("4588"),r=n("be13");t.exports=function(t){return function(e,n){var o,a,s=String(r(e)),c=i(n),l=s.length;return c<0||c>=l?t?"":void 0:(o=s.charCodeAt(c),o<55296||o>56319||c+1===l||(a=s.charCodeAt(c+1))<56320||a>57343?t?s.charAt(c):o:t?s.slice(c,c+2):a-56320+(o-55296<<10)+65536)}}},"0390":function(t,e,n){"use strict";var i=n("02f4")(!0);t.exports=function(t,e,n){return e+(n?i(t,e).length:1)}},"0bfb":function(t,e,n){"use strict";var i=n("cb7c");t.exports=function(){var t=i(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},"0d58":function(t,e,n){var i=n("ce10"),r=n("e11e");t.exports=Object.keys||function(t){return i(t,r)}},1495:function(t,e,n){var i=n("86cc"),r=n("cb7c"),o=n("0d58");t.exports=n("9e1e")?Object.defineProperties:function(t,e){r(t);var n,a=o(e),s=a.length,c=0;while(s>c)i.f(t,n=a[c++],e[n]);return t}},"214f":function(t,e,n){"use strict";n("b0c5");var i=n("2aba"),r=n("32e9"),o=n("79e5"),a=n("be13"),s=n("2b4c"),c=n("520a"),l=s("species"),u=!o((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),h=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();t.exports=function(t,e,n){var f=s(t),d=!o((function(){var e={};return e[f]=function(){return 7},7!=""[t](e)})),p=d?!o((function(){var e=!1,n=/a/;return n.exec=function(){return e=!0,null},"split"===t&&(n.constructor={},n.constructor[l]=function(){return n}),n[f](""),!e})):void 0;if(!d||!p||"replace"===t&&!u||"split"===t&&!h){var v=/./[f],m=n(a,f,""[t],(function(t,e,n,i,r){return e.exec===c?d&&!r?{done:!0,value:v.call(e,n,i)}:{done:!0,value:t.call(n,e,i)}:{done:!1}})),g=m[0],b=m[1];i(String.prototype,t,g),r(RegExp.prototype,f,2==e?function(t,e){return b.call(t,this,e)}:function(t){return b.call(t,this)})}}},"230e":function(t,e,n){var i=n("d3f4"),r=n("7726").document,o=i(r)&&i(r.createElement);t.exports=function(t){return o?r.createElement(t):{}}},"23c6":function(t,e,n){var i=n("2d95"),r=n("2b4c")("toStringTag"),o="Arguments"==i(function(){return arguments}()),a=function(t,e){try{return t[e]}catch(n){}};t.exports=function(t){var e,n,s;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=a(e=Object(t),r))?n:o?i(e):"Object"==(s=i(e))&&"function"==typeof e.callee?"Arguments":s}},2621:function(t,e){e.f=Object.getOwnPropertySymbols},"2aba":function(t,e,n){var i=n("7726"),r=n("32e9"),o=n("69a8"),a=n("ca5a")("src"),s=n("fa5b"),c="toString",l=(""+s).split(c);n("8378").inspectSource=function(t){return s.call(t)},(t.exports=function(t,e,n,s){var c="function"==typeof n;c&&(o(n,"name")||r(n,"name",e)),t[e]!==n&&(c&&(o(n,a)||r(n,a,t[e]?""+t[e]:l.join(String(e)))),t===i?t[e]=n:s?t[e]?t[e]=n:r(t,e,n):(delete t[e],r(t,e,n)))})(Function.prototype,c,(function(){return"function"==typeof this&&this[a]||s.call(this)}))},"2aeb":function(t,e,n){var i=n("cb7c"),r=n("1495"),o=n("e11e"),a=n("613b")("IE_PROTO"),s=function(){},c="prototype",l=function(){var t,e=n("230e")("iframe"),i=o.length,r="<",a=">";e.style.display="none",n("fab2").appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write(r+"script"+a+"document.F=Object"+r+"/script"+a),t.close(),l=t.F;while(i--)delete l[c][o[i]];return l()};t.exports=Object.create||function(t,e){var n;return null!==t?(s[c]=i(t),n=new s,s[c]=null,n[a]=t):n=l(),void 0===e?n:r(n,e)}},"2b4c":function(t,e,n){var i=n("5537")("wks"),r=n("ca5a"),o=n("7726").Symbol,a="function"==typeof o,s=t.exports=function(t){return i[t]||(i[t]=a&&o[t]||(a?o:r)("Symbol."+t))};s.store=i},"2d00":function(t,e){t.exports=!1},"2d95":function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},"2fdb":function(t,e,n){"use strict";var i=n("5ca1"),r=n("d2c8"),o="includes";i(i.P+i.F*n("5147")(o),"String",{includes:function(t){return!!~r(this,t,o).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},"32e9":function(t,e,n){var i=n("86cc"),r=n("4630");t.exports=n("9e1e")?function(t,e,n){return i.f(t,e,r(1,n))}:function(t,e,n){return t[e]=n,t}},"38fd":function(t,e,n){var i=n("69a8"),r=n("4bf8"),o=n("613b")("IE_PROTO"),a=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=r(t),i(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?a:null}},"41a0":function(t,e,n){"use strict";var i=n("2aeb"),r=n("4630"),o=n("7f20"),a={};n("32e9")(a,n("2b4c")("iterator"),(function(){return this})),t.exports=function(t,e,n){t.prototype=i(a,{next:r(1,n)}),o(t,e+" Iterator")}},"456d":function(t,e,n){var i=n("4bf8"),r=n("0d58");n("5eda")("keys",(function(){return function(t){return r(i(t))}}))},4588:function(t,e){var n=Math.ceil,i=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?i:n)(t)}},4630:function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},"4bf8":function(t,e,n){var i=n("be13");t.exports=function(t){return Object(i(t))}},5147:function(t,e,n){var i=n("2b4c")("match");t.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[i]=!1,!"/./"[t](e)}catch(r){}}return!0}},"520a":function(t,e,n){"use strict";var i=n("0bfb"),r=RegExp.prototype.exec,o=String.prototype.replace,a=r,s="lastIndex",c=function(){var t=/a/,e=/b*/g;return r.call(t,"a"),r.call(e,"a"),0!==t[s]||0!==e[s]}(),l=void 0!==/()??/.exec("")[1],u=c||l;u&&(a=function(t){var e,n,a,u,h=this;return l&&(n=new RegExp("^"+h.source+"$(?!\\s)",i.call(h))),c&&(e=h[s]),a=r.call(h,t),c&&a&&(h[s]=h.global?a.index+a[0].length:e),l&&a&&a.length>1&&o.call(a[0],n,(function(){for(u=1;u1?arguments[1]:void 0)}}),n("9c6c")("includes")},6821:function(t,e,n){var i=n("626a"),r=n("be13");t.exports=function(t){return i(r(t))}},"69a8":function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},"6a99":function(t,e,n){var i=n("d3f4");t.exports=function(t,e){if(!i(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!i(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!i(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!i(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")}},7333:function(t,e,n){"use strict";var i=n("9e1e"),r=n("0d58"),o=n("2621"),a=n("52a7"),s=n("4bf8"),c=n("626a"),l=Object.assign;t.exports=!l||n("79e5")((function(){var t={},e={},n=Symbol(),i="abcdefghijklmnopqrst";return t[n]=7,i.split("").forEach((function(t){e[t]=t})),7!=l({},t)[n]||Object.keys(l({},e)).join("")!=i}))?function(t,e){var n=s(t),l=arguments.length,u=1,h=o.f,f=a.f;while(l>u){var d,p=c(arguments[u++]),v=h?r(p).concat(h(p)):r(p),m=v.length,g=0;while(m>g)d=v[g++],i&&!f.call(p,d)||(n[d]=p[d])}return n}:l},7726:function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},"77f1":function(t,e,n){var i=n("4588"),r=Math.max,o=Math.min;t.exports=function(t,e){return t=i(t),t<0?r(t+e,0):o(t,e)}},"79e5":function(t,e){t.exports=function(t){try{return!!t()}catch(e){return!0}}},"7f20":function(t,e,n){var i=n("86cc").f,r=n("69a8"),o=n("2b4c")("toStringTag");t.exports=function(t,e,n){t&&!r(t=n?t:t.prototype,o)&&i(t,o,{configurable:!0,value:e})}},8378:function(t,e){var n=t.exports={version:"2.6.11"};"number"==typeof __e&&(__e=n)},"84f2":function(t,e){t.exports={}},"86cc":function(t,e,n){var i=n("cb7c"),r=n("c69a"),o=n("6a99"),a=Object.defineProperty;e.f=n("9e1e")?Object.defineProperty:function(t,e,n){if(i(t),e=o(e,!0),i(n),r)try{return a(t,e,n)}catch(s){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},"9b43":function(t,e,n){var i=n("d8e8");t.exports=function(t,e,n){if(i(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,i){return t.call(e,n,i)};case 3:return function(n,i,r){return t.call(e,n,i,r)}}return function(){return t.apply(e,arguments)}}},"9c6c":function(t,e,n){var i=n("2b4c")("unscopables"),r=Array.prototype;void 0==r[i]&&n("32e9")(r,i,{}),t.exports=function(t){r[i][t]=!0}},"9def":function(t,e,n){var i=n("4588"),r=Math.min;t.exports=function(t){return t>0?r(i(t),9007199254740991):0}},"9e1e":function(t,e,n){t.exports=!n("79e5")((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},a352:function(t,e){t.exports=n("aa47")},a481:function(t,e,n){"use strict";var i=n("cb7c"),r=n("4bf8"),o=n("9def"),a=n("4588"),s=n("0390"),c=n("5f1b"),l=Math.max,u=Math.min,h=Math.floor,f=/\$([$&`']|\d\d?|<[^>]*>)/g,d=/\$([$&`']|\d\d?)/g,p=function(t){return void 0===t?t:String(t)};n("214f")("replace",2,(function(t,e,n,v){return[function(i,r){var o=t(this),a=void 0==i?void 0:i[e];return void 0!==a?a.call(i,o,r):n.call(String(o),i,r)},function(t,e){var r=v(n,t,this,e);if(r.done)return r.value;var h=i(t),f=String(this),d="function"===typeof e;d||(e=String(e));var g=h.global;if(g){var b=h.unicode;h.lastIndex=0}var y=[];while(1){var w=c(h,f);if(null===w)break;if(y.push(w),!g)break;var x=String(w[0]);""===x&&(h.lastIndex=s(f,o(h.lastIndex),b))}for(var O="",_=0,S=0;S=_&&(O+=f.slice(_,k)+T,_=k+C.length)}return O+f.slice(_)}];function m(t,e,i,o,a,s){var c=i+t.length,l=o.length,u=d;return void 0!==a&&(a=r(a),u=f),n.call(s,u,(function(n,r){var s;switch(r.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,i);case"'":return e.slice(c);case"<":s=a[r.slice(1,-1)];break;default:var u=+r;if(0===u)return n;if(u>l){var f=h(u/10);return 0===f?n:f<=l?void 0===o[f-1]?r.charAt(1):o[f-1]+r.charAt(1):n}s=o[u-1]}return void 0===s?"":s}))}}))},aae3:function(t,e,n){var i=n("d3f4"),r=n("2d95"),o=n("2b4c")("match");t.exports=function(t){var e;return i(t)&&(void 0!==(e=t[o])?!!e:"RegExp"==r(t))}},ac6a:function(t,e,n){for(var i=n("cadf"),r=n("0d58"),o=n("2aba"),a=n("7726"),s=n("32e9"),c=n("84f2"),l=n("2b4c"),u=l("iterator"),h=l("toStringTag"),f=c.Array,d={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},p=r(d),v=0;vu)if(s=c[u++],s!=s)return!0}else for(;l>u;u++)if((t||u in c)&&c[u]===n)return t||u||0;return!t&&-1}}},c649:function(t,e,n){"use strict";(function(t){n.d(e,"c",(function(){return l})),n.d(e,"a",(function(){return s})),n.d(e,"b",(function(){return r})),n.d(e,"d",(function(){return c}));n("a481");function i(){return"undefined"!==typeof window?window.console:t.console}var r=i();function o(t){var e=Object.create(null);return function(n){var i=e[n];return i||(e[n]=t(n))}}var a=/-(\w)/g,s=o((function(t){return t.replace(a,(function(t,e){return e?e.toUpperCase():""}))}));function c(t){null!==t.parentElement&&t.parentElement.removeChild(t)}function l(t,e,n){var i=0===n?t.children[0]:t.children[n-1].nextSibling;t.insertBefore(e,i)}}).call(this,n("c8ba"))},c69a:function(t,e,n){t.exports=!n("9e1e")&&!n("79e5")((function(){return 7!=Object.defineProperty(n("230e")("div"),"a",{get:function(){return 7}}).a}))},c8ba:function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(i){"object"===typeof window&&(n=window)}t.exports=n},ca5a:function(t,e){var n=0,i=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+i).toString(36))}},cadf:function(t,e,n){"use strict";var i=n("9c6c"),r=n("d53b"),o=n("84f2"),a=n("6821");t.exports=n("01f9")(Array,"Array",(function(t,e){this._t=a(t),this._i=0,this._k=e}),(function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,r(1)):r(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])}),"values"),o.Arguments=o.Array,i("keys"),i("values"),i("entries")},cb7c:function(t,e,n){var i=n("d3f4");t.exports=function(t){if(!i(t))throw TypeError(t+" is not an object!");return t}},ce10:function(t,e,n){var i=n("69a8"),r=n("6821"),o=n("c366")(!1),a=n("613b")("IE_PROTO");t.exports=function(t,e){var n,s=r(t),c=0,l=[];for(n in s)n!=a&&i(s,n)&&l.push(n);while(e.length>c)i(s,n=e[c++])&&(~o(l,n)||l.push(n));return l}},d2c8:function(t,e,n){var i=n("aae3"),r=n("be13");t.exports=function(t,e,n){if(i(e))throw TypeError("String#"+n+" doesn't accept regex!");return String(r(t))}},d3f4:function(t,e){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},d53b:function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},d8e8:function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},e11e:function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},f559:function(t,e,n){"use strict";var i=n("5ca1"),r=n("9def"),o=n("d2c8"),a="startsWith",s=""[a];i(i.P+i.F*n("5147")(a),"String",{startsWith:function(t){var e=o(this,t,a),n=r(Math.min(arguments.length>1?arguments[1]:void 0,e.length)),i=String(t);return s?s.call(e,i,n):e.slice(n,n+i.length)===i}})},f6fd:function(t,e){(function(t){var e="currentScript",n=t.getElementsByTagName("script");e in t||Object.defineProperty(t,e,{get:function(){try{throw new Error}catch(i){var t,e=(/.*at [^\(]*\((.*):.+:.+\)$/gi.exec(i.stack)||[!1])[1];for(t in n)if(n[t].src==e||"interactive"==n[t].readyState)return n[t];return null}}})})(document)},f751:function(t,e,n){var i=n("5ca1");i(i.S+i.F,"Object",{assign:n("7333")})},fa5b:function(t,e,n){t.exports=n("5537")("native-function-to-string",Function.toString)},fab2:function(t,e,n){var i=n("7726").document;t.exports=i&&i.documentElement},fb15:function(t,e,n){"use strict";var i;(n.r(e),"undefined"!==typeof window)&&(n("f6fd"),(i=window.document.currentScript)&&(i=i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(n.p=i[1]));n("f751"),n("f559"),n("ac6a"),n("cadf"),n("456d");function r(t){if(Array.isArray(t))return t}function o(t,e){if("undefined"!==typeof Symbol&&Symbol.iterator in Object(t)){var n=[],i=!0,r=!1,o=void 0;try{for(var a,s=t[Symbol.iterator]();!(i=(a=s.next()).done);i=!0)if(n.push(a.value),e&&n.length===e)break}catch(c){r=!0,o=c}finally{try{i||null==s["return"]||s["return"]()}finally{if(r)throw o}}return n}}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n=o?r.length:r.indexOf(t)}));return n?a.filter((function(t){return-1!==t})):a}function w(t,e){var n=this;this.$nextTick((function(){return n.$emit(t.toLowerCase(),e)}))}function x(t){var e=this;return function(n){null!==e.realList&&e["onDrag"+t](n),w.call(e,t,n)}}function O(t){return["transition-group","TransitionGroup"].includes(t)}function _(t){if(!t||1!==t.length)return!1;var e=l(t,1),n=e[0].componentOptions;return!!n&&O(n.tag)}function S(t,e,n){return t[n]||(e[n]?e[n]():void 0)}function C(t,e,n){var i=0,r=0,o=S(e,n,"header");o&&(i=o.length,t=t?[].concat(d(o),d(t)):d(o));var a=S(e,n,"footer");return a&&(r=a.length,t=t?[].concat(d(t),d(a)):d(a)),{children:t,headerOffset:i,footerOffset:r}}function k(t,e){var n=null,i=function(t,e){n=g(n,t,e)},r=Object.keys(t).filter((function(t){return"id"===t||t.startsWith("data-")})).reduce((function(e,n){return e[n]=t[n],e}),{});if(i("attrs",r),!e)return n;var o=e.on,a=e.props,s=e.attrs;return i("on",o),i("props",a),Object.assign(n.attrs,s),n}var j=["Start","Add","Remove","Update","End"],A=["Choose","Unchoose","Sort","Filter","Clone"],$=["Move"].concat(j,A).map((function(t){return"on"+t})),E=null,T={options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:function(t){return t}},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},I={name:"draggable",inheritAttrs:!1,props:T,data:function(){return{transitionMode:!1,noneFunctionalComponentMode:!1}},render:function(t){var e=this.$slots.default;this.transitionMode=_(e);var n=C(e,this.$slots,this.$scopedSlots),i=n.children,r=n.headerOffset,o=n.footerOffset;this.headerOffset=r,this.footerOffset=o;var a=k(this.$attrs,this.componentData);return t(this.getTag(),a,i)},created:function(){null!==this.list&&null!==this.value&&m["b"].error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&m["b"].warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&m["b"].warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted:function(){var t=this;if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase()&&!this.getIsFunctional(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error("Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ".concat(this.getTag()));var e={};j.forEach((function(n){e["on"+n]=x.call(t,n)})),A.forEach((function(n){e["on"+n]=w.bind(t,n)}));var n=Object.keys(this.$attrs).reduce((function(e,n){return e[Object(m["a"])(n)]=t.$attrs[n],e}),{}),i=Object.assign({},this.options,n,e,{onMove:function(e,n){return t.onDragMove(e,n)}});!("draggable"in i)&&(i.draggable=">*"),this._sortable=new v.a(this.rootContainer,i),this.computeIndexes()},beforeDestroy:function(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer:function(){return this.transitionMode?this.$el.children[0]:this.$el},realList:function(){return this.list?this.list:this.value}},watch:{options:{handler:function(t){this.updateOptions(t)},deep:!0},$attrs:{handler:function(t){this.updateOptions(t)},deep:!0},realList:function(){this.computeIndexes()}},methods:{getIsFunctional:function(){var t=this._vnode.fnOptions;return t&&t.functional},getTag:function(){return this.tag||this.element},updateOptions:function(t){for(var e in t){var n=Object(m["a"])(e);-1===$.indexOf(n)&&this._sortable.option(n,t[e])}},getChildrenNodes:function(){if(this.noneFunctionalComponentMode)return this.$children[0].$slots.default;var t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes:function(){var t=this;this.$nextTick((function(){t.visibleIndexes=y(t.getChildrenNodes(),t.rootContainer.children,t.transitionMode,t.footerOffset)}))},getUnderlyingVm:function(t){var e=b(this.getChildrenNodes()||[],t);if(-1===e)return null;var n=this.realList[e];return{index:e,element:n}},getUnderlyingPotencialDraggableComponent:function(t){var e=t.__vue__;return e&&e.$options&&O(e.$options._componentTag)?e.$parent:!("realList"in e)&&1===e.$children.length&&"realList"in e.$children[0]?e.$children[0]:e},emitChanges:function(t){var e=this;this.$nextTick((function(){e.$emit("change",t)}))},alterList:function(t){if(this.list)t(this.list);else{var e=d(this.value);t(e),this.$emit("input",e)}},spliceList:function(){var t=arguments,e=function(e){return e.splice.apply(e,d(t))};this.alterList(e)},updatePosition:function(t,e){var n=function(n){return n.splice(e,0,n.splice(t,1)[0])};this.alterList(n)},getRelatedContextFromMoveEvent:function(t){var e=t.to,n=t.related,i=this.getUnderlyingPotencialDraggableComponent(e);if(!i)return{component:i};var r=i.realList,o={list:r,component:i};if(e!==n&&r&&i.getUnderlyingVm){var a=i.getUnderlyingVm(n);if(a)return Object.assign(a,o)}return o},getVmIndex:function(t){var e=this.visibleIndexes,n=e.length;return t>n-1?n:e[t]},getComponent:function(){return this.$slots.default[0].componentInstance},resetTransitionData:function(t){if(this.noTransitionOnDrag&&this.transitionMode){var e=this.getChildrenNodes();e[t].data=null;var n=this.getComponent();n.children=[],n.kept=void 0}},onDragStart:function(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),E=t.item},onDragAdd:function(t){var e=t.item._underlying_vm_;if(void 0!==e){Object(m["d"])(t.item);var n=this.getVmIndex(t.newIndex);this.spliceList(n,0,e),this.computeIndexes();var i={element:e,newIndex:n};this.emitChanges({added:i})}},onDragRemove:function(t){if(Object(m["c"])(this.rootContainer,t.item,t.oldIndex),"clone"!==t.pullMode){var e=this.context.index;this.spliceList(e,1);var n={element:this.context.element,oldIndex:e};this.resetTransitionData(e),this.emitChanges({removed:n})}else Object(m["d"])(t.clone)},onDragUpdate:function(t){Object(m["d"])(t.item),Object(m["c"])(t.from,t.item,t.oldIndex);var e=this.context.index,n=this.getVmIndex(t.newIndex);this.updatePosition(e,n);var i={element:this.context.element,oldIndex:e,newIndex:n};this.emitChanges({moved:i})},updateProperty:function(t,e){t.hasOwnProperty(e)&&(t[e]+=this.headerOffset)},computeFutureIndex:function(t,e){if(!t.element)return 0;var n=d(e.to.children).filter((function(t){return"none"!==t.style["display"]})),i=n.indexOf(e.related),r=t.component.getVmIndex(i),o=-1!==n.indexOf(E);return o||!e.willInsertAfter?r:r+1},onDragMove:function(t,e){var n=this.move;if(!n||!this.realList)return!0;var i=this.getRelatedContextFromMoveEvent(t),r=this.context,o=this.computeFutureIndex(i,t);Object.assign(r,{futureIndex:o});var a=Object.assign({},t,{relatedContext:i,draggedContext:r});return n(a,e)},onDragEnd:function(){this.computeIndexes(),E=null}}};"undefined"!==typeof window&&"Vue"in window&&window.Vue.component("draggable",I);var L=I;e["default"]=L}})["default"]},3206:function(t,e,n){"use strict";n.d(e,"a",(function(){return s})),n.d(e,"b",(function(){return c}));n("99af");var i=n("ade3"),r=n("2b0e"),o=n("d9bd");function a(t,e){return function(){return Object(o["c"])("The ".concat(t," component must be used inside a ").concat(e))}}function s(t,e,n){var o=e&&n?{register:a(e,n),unregister:a(e,n)}:null;return r["a"].extend({name:"registrable-inject",inject:Object(i["a"])({},t,{default:o})})}function c(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return r["a"].extend({name:"registrable-provide",methods:e?{}:{register:null,unregister:null},provide:function(){return Object(i["a"])({},t,e?this:{register:this.register,unregister:this.unregister})}})}},"326d":function(t,e,n){"use strict";var i=n("e449");e["a"]=i["a"]},3408:function(t,e,n){},3410:function(t,e,n){var i=n("23e7"),r=n("d039"),o=n("7b0b"),a=n("e163"),s=n("e177"),c=r((function(){a(1)}));i({target:"Object",stat:!0,forced:c,sham:!s},{getPrototypeOf:function(t){return a(o(t))}})},"342f":function(t,e,n){var i=n("d066");t.exports=i("navigator","userAgent")||""},"34c3":function(t,e,n){"use strict";n("498a");var i=n("2b0e");e["a"]=i["a"].extend({name:"v-list-item-icon",functional:!0,render:function(t,e){var n=e.data,i=e.children;return n.staticClass="v-list-item__icon ".concat(n.staticClass||"").trim(),t("div",n,i)}})},"35a1":function(t,e,n){var i=n("f5df"),r=n("3f8c"),o=n("b622"),a=o("iterator");t.exports=function(t){if(void 0!=t)return t[a]||t["@@iterator"]||r[i(t)]}},"368e":function(t,e,n){},"36a7":function(t,e,n){},"37e8":function(t,e,n){var i=n("83ab"),r=n("9bf2"),o=n("825a"),a=n("df75");t.exports=i?Object.defineProperties:function(t,e){o(t);var n,i=a(e),s=i.length,c=0;while(s>c)r.f(t,n=i[c++],e[n]);return t}},3835:function(t,e,n){"use strict";function i(t){if(Array.isArray(t))return t}n.d(e,"a",(function(){return s}));n("a4d3"),n("e01a"),n("d28b"),n("d3b7"),n("3ca3"),n("ddb0");function r(t,e){if("undefined"!==typeof Symbol&&Symbol.iterator in Object(t)){var n=[],i=!0,r=!1,o=void 0;try{for(var a,s=t[Symbol.iterator]();!(i=(a=s.next()).done);i=!0)if(n.push(a.value),e&&n.length===e)break}catch(c){r=!0,o=c}finally{try{i||null==s["return"]||s["return"]()}finally{if(r)throw o}}return n}}var o=n("06c5");function a(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function s(t,e){return i(t)||r(t,e)||Object(o["a"])(t,e)||a()}},"38cb":function(t,e,n){"use strict";n("fb6a"),n("a9e3");var i=n("53ca"),r=n("a9ad"),o=n("7560"),a=n("3206"),s=n("80d2"),c=n("d9bd"),l=n("58df"),u=Object(l["a"])(r["a"],Object(a["a"])("form"),o["a"]);e["a"]=u.extend({name:"validatable",props:{disabled:Boolean,error:Boolean,errorCount:{type:[Number,String],default:1},errorMessages:{type:[String,Array],default:function(){return[]}},messages:{type:[String,Array],default:function(){return[]}},readonly:Boolean,rules:{type:Array,default:function(){return[]}},success:Boolean,successMessages:{type:[String,Array],default:function(){return[]}},validateOnBlur:Boolean,value:{required:!1}},data:function(){return{errorBucket:[],hasColor:!1,hasFocused:!1,hasInput:!1,isFocused:!1,isResetting:!1,lazyValue:this.value,valid:!1}},computed:{computedColor:function(){if(!this.isDisabled)return this.color?this.color:this.isDark&&!this.appIsDark?"white":"primary"},hasError:function(){return this.internalErrorMessages.length>0||this.errorBucket.length>0||this.error},hasSuccess:function(){return this.internalSuccessMessages.length>0||this.success},externalError:function(){return this.internalErrorMessages.length>0||this.error},hasMessages:function(){return this.validationTarget.length>0},hasState:function(){return!this.isDisabled&&(this.hasSuccess||this.shouldValidate&&this.hasError)},internalErrorMessages:function(){return this.genInternalMessages(this.errorMessages)},internalMessages:function(){return this.genInternalMessages(this.messages)},internalSuccessMessages:function(){return this.genInternalMessages(this.successMessages)},internalValue:{get:function(){return this.lazyValue},set:function(t){this.lazyValue=t,this.$emit("input",t)}},isDisabled:function(){return this.disabled||!!this.form&&this.form.disabled},isInteractive:function(){return!this.isDisabled&&!this.isReadonly},isReadonly:function(){return this.readonly||!!this.form&&this.form.readonly},shouldValidate:function(){return!!this.externalError||!this.isResetting&&(this.validateOnBlur?this.hasFocused&&!this.isFocused:this.hasInput||this.hasFocused)},validations:function(){return this.validationTarget.slice(0,Number(this.errorCount))},validationState:function(){if(!this.isDisabled)return this.hasError&&this.shouldValidate?"error":this.hasSuccess?"success":this.hasColor?this.computedColor:void 0},validationTarget:function(){return this.internalErrorMessages.length>0?this.internalErrorMessages:this.successMessages.length>0?this.internalSuccessMessages:this.messages.length>0?this.internalMessages:this.shouldValidate?this.errorBucket:[]}},watch:{rules:{handler:function(t,e){Object(s["i"])(t,e)||this.validate()},deep:!0},internalValue:function(){this.hasInput=!0,this.validateOnBlur||this.$nextTick(this.validate)},isFocused:function(t){t||this.isDisabled||(this.hasFocused=!0,this.validateOnBlur&&this.$nextTick(this.validate))},isResetting:function(){var t=this;setTimeout((function(){t.hasInput=!1,t.hasFocused=!1,t.isResetting=!1,t.validate()}),0)},hasError:function(t){this.shouldValidate&&this.$emit("update:error",t)},value:function(t){this.lazyValue=t}},beforeMount:function(){this.validate()},created:function(){this.form&&this.form.register(this)},beforeDestroy:function(){this.form&&this.form.unregister(this)},methods:{genInternalMessages:function(t){return t?Array.isArray(t)?t:[t]:[]},reset:function(){this.isResetting=!0,this.internalValue=Array.isArray(this.internalValue)?[]:void 0},resetValidation:function(){this.isResetting=!0},validate:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],e=arguments.length>1?arguments[1]:void 0,n=[];e=e||this.internalValue,t&&(this.hasInput=this.hasFocused=!0);for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:[];return Object(r["a"])(Object(i["b"])(["absolute","fixed"])).extend({name:"applicationable",props:{app:Boolean},computed:{applicationProperty:function(){return t}},watch:{app:function(t,e){e?this.removeApplication(!0):this.callUpdate()},applicationProperty:function(t,e){this.$vuetify.application.unregister(this._uid,e)}},activated:function(){this.callUpdate()},created:function(){for(var t=0,n=e.length;t0&&void 0!==arguments[0]&&arguments[0];(t||this.app)&&this.$vuetify.application.unregister(this._uid,this.applicationProperty)},updateApplication:function(){return 0}}})}},"3ad0":function(t,e,n){},"3bbe":function(t,e,n){var i=n("861d");t.exports=function(t){if(!i(t)&&null!==t)throw TypeError("Can't set "+String(t)+" as a prototype");return t}},"3c93":function(t,e,n){},"3ca3":function(t,e,n){"use strict";var i=n("6547").charAt,r=n("69f3"),o=n("7dd0"),a="String Iterator",s=r.set,c=r.getterFor(a);o(String,"String",(function(t){s(this,{type:a,string:String(t),index:0})}),(function(){var t,e=c(this),n=e.string,r=e.index;return r>=n.length?{value:void 0,done:!0}:(t=i(n,r),e.index+=t.length,{value:t,done:!1})}))},"3ea3":function(t,e,n){var i=n("23e7"),r=n("f748"),o=Math.abs,a=Math.pow;i({target:"Math",stat:!0},{cbrt:function(t){return r(t=+t)*a(o(t),1/3)}})},"3f8c":function(t,e){t.exports={}},4069:function(t,e,n){var i=n("44d2");i("flat")},"408a":function(t,e,n){var i=n("c6b6");t.exports=function(t){if("number"!=typeof t&&"Number"!=i(t))throw TypeError("Incorrect invocation");return+t}},"40dc":function(t,e,n){"use strict";n("a9e3"),n("b680"),n("c7cd");var i=n("5530"),r=(n("8b0d"),n("71d9")),o=n("53ca");function a(t,e){var n=e.modifiers||{},i=n.self,r=void 0!==i&&i,a=e.value,s="object"===Object(o["a"])(a)&&a.options||{passive:!0},c="function"===typeof a||"handleEvent"in a?a:a.handler,l=r?t:e.arg?document.querySelector(e.arg):window;l&&(l.addEventListener("scroll",c,s),t._onScroll={handler:c,options:s,target:r?void 0:l})}function s(t){if(t._onScroll){var e=t._onScroll,n=e.handler,i=e.options,r=e.target,o=void 0===r?t:r;o.removeEventListener("scroll",n,i),delete t._onScroll}}var c={inserted:a,unbind:s},l=c,u=n("3a66"),h=n("d9bd"),f=n("2b0e"),d=f["a"].extend({name:"scrollable",directives:{Scroll:c},props:{scrollTarget:String,scrollThreshold:[String,Number]},data:function(){return{currentScroll:0,currentThreshold:0,isActive:!1,isScrollingUp:!1,previousScroll:0,savedScroll:0,target:null}},computed:{canScroll:function(){return"undefined"!==typeof window},computedScrollThreshold:function(){return this.scrollThreshold?Number(this.scrollThreshold):300}},watch:{isScrollingUp:function(){this.savedScroll=this.savedScroll||this.currentScroll},isActive:function(){this.savedScroll=0}},mounted:function(){this.scrollTarget&&(this.target=document.querySelector(this.scrollTarget),this.target||Object(h["c"])("Unable to locate element with identifier ".concat(this.scrollTarget),this))},methods:{onScroll:function(){var t=this;this.canScroll&&(this.previousScroll=this.currentScroll,this.currentScroll=this.target?this.target.scrollTop:window.pageYOffset,this.isScrollingUp=this.currentScrollt.computedScrollThreshold&&t.thresholdMet()})))},thresholdMet:function(){}}}),p=n("d10f"),v=n("f2e7"),m=n("80d2"),g=n("58df"),b=Object(g["a"])(r["a"],d,p["a"],v["a"],Object(u["a"])("top",["clippedLeft","clippedRight","computedHeight","invertedScroll","isExtended","isProminent","value"]));e["a"]=b.extend({name:"v-app-bar",directives:{Scroll:l},props:{clippedLeft:Boolean,clippedRight:Boolean,collapseOnScroll:Boolean,elevateOnScroll:Boolean,fadeImgOnScroll:Boolean,hideOnScroll:Boolean,invertedScroll:Boolean,scrollOffScreen:Boolean,shrinkOnScroll:Boolean,value:{type:Boolean,default:!0}},data:function(){return{isActive:this.value}},computed:{applicationProperty:function(){return this.bottom?"bottom":"top"},canScroll:function(){return d.options.computed.canScroll.call(this)&&(this.invertedScroll||this.elevateOnScroll||this.hideOnScroll||this.collapseOnScroll||this.isBooted||!this.value)},classes:function(){return Object(i["a"])(Object(i["a"])({},r["a"].options.computed.classes.call(this)),{},{"v-toolbar--collapse":this.collapse||this.collapseOnScroll,"v-app-bar":!0,"v-app-bar--clipped":this.clippedLeft||this.clippedRight,"v-app-bar--fade-img-on-scroll":this.fadeImgOnScroll,"v-app-bar--elevate-on-scroll":this.elevateOnScroll,"v-app-bar--fixed":!this.absolute&&(this.app||this.fixed),"v-app-bar--hide-shadow":this.hideShadow,"v-app-bar--is-scrolled":this.currentScroll>0,"v-app-bar--shrink-on-scroll":this.shrinkOnScroll})},computedContentHeight:function(){if(!this.shrinkOnScroll)return r["a"].options.computed.computedContentHeight.call(this);var t=this.computedOriginalHeight,e=this.dense?48:56,n=t,i=n-e,o=i/this.computedScrollThreshold,a=this.currentScroll*o;return Math.max(e,n-a)},computedFontSize:function(){if(this.isProminent){var t=this.dense?96:128,e=t-this.computedContentHeight,n=.00347;return Number((1.5-e*n).toFixed(2))}},computedLeft:function(){return!this.app||this.clippedLeft?0:this.$vuetify.application.left},computedMarginTop:function(){return this.app?this.$vuetify.application.bar:0},computedOpacity:function(){if(this.fadeImgOnScroll){var t=Math.max((this.computedScrollThreshold-this.currentScroll)/this.computedScrollThreshold,0);return Number(parseFloat(t).toFixed(2))}},computedOriginalHeight:function(){var t=r["a"].options.computed.computedContentHeight.call(this);return this.isExtended&&(t+=parseInt(this.extensionHeight)),t},computedRight:function(){return!this.app||this.clippedRight?0:this.$vuetify.application.right},computedScrollThreshold:function(){return this.scrollThreshold?Number(this.scrollThreshold):this.computedOriginalHeight-(this.dense?48:56)},computedTransform:function(){if(!this.canScroll||this.elevateOnScroll&&0===this.currentScroll&&this.isActive)return 0;if(this.isActive)return 0;var t=this.scrollOffScreen?this.computedHeight:this.computedContentHeight;return this.bottom?t:-t},hideShadow:function(){return this.elevateOnScroll&&this.isExtended?this.currentScroll0:r["a"].options.computed.isCollapsed.call(this)},isProminent:function(){return r["a"].options.computed.isProminent.call(this)||this.shrinkOnScroll},styles:function(){return Object(i["a"])(Object(i["a"])({},r["a"].options.computed.styles.call(this)),{},{fontSize:Object(m["f"])(this.computedFontSize,"rem"),marginTop:Object(m["f"])(this.computedMarginTop),transform:"translateY(".concat(Object(m["f"])(this.computedTransform),")"),left:Object(m["f"])(this.computedLeft),right:Object(m["f"])(this.computedRight)})}},watch:{canScroll:"onScroll",computedTransform:function(){this.canScroll&&(this.clippedLeft||this.clippedRight)&&this.callUpdate()},invertedScroll:function(t){this.isActive=!t||0!==this.currentScroll}},created:function(){this.invertedScroll&&(this.isActive=!1)},methods:{genBackground:function(){var t=r["a"].options.methods.genBackground.call(this);return t.data=this._b(t.data||{},t.tag,{style:{opacity:this.computedOpacity}}),t},updateApplication:function(){return this.invertedScroll?0:this.computedHeight+this.computedTransform},thresholdMet:function(){this.invertedScroll?this.isActive=this.currentScroll>this.computedScrollThreshold:(this.hideOnScroll&&(this.isActive=this.isScrollingUp||this.currentScroll1?arguments[1]:void 0)}})},"466d":function(t,e,n){"use strict";var i=n("d784"),r=n("825a"),o=n("50c4"),a=n("1d80"),s=n("8aa5"),c=n("14c3");i("match",1,(function(t,e,n){return[function(e){var n=a(this),i=void 0==e?void 0:e[t];return void 0!==i?i.call(e,n):new RegExp(e)[t](String(n))},function(t){var i=n(e,t,this);if(i.done)return i.value;var a=r(t),l=String(this);if(!a.global)return c(a,l);var u=a.unicode;a.lastIndex=0;var h,f=[],d=0;while(null!==(h=c(a,l))){var p=String(h[0]);f[d]=p,""===p&&(a.lastIndex=s(l,o(a.lastIndex),u)),d++}return 0===d?null:f}]}))},4804:function(t,e,n){},"480e":function(t,e,n){"use strict";n("7db0");var i=n("7560");e["a"]=i["a"].extend({name:"v-theme-provider",props:{root:Boolean},computed:{isDark:function(){return this.root?this.rootIsDark:i["a"].options.computed.isDark.call(this)}},render:function(){return this.$slots.default&&this.$slots.default.find((function(t){return!t.isComment&&" "!==t.text}))}})},4840:function(t,e,n){var i=n("825a"),r=n("1c0b"),o=n("b622"),a=o("species");t.exports=function(t,e){var n,o=i(t).constructor;return void 0===o||void 0==(n=i(o)[a])?e:r(n)}},4930:function(t,e,n){var i=n("d039");t.exports=!!Object.getOwnPropertySymbols&&!i((function(){return!String(Symbol())}))},"498a":function(t,e,n){"use strict";var i=n("23e7"),r=n("58a8").trim,o=n("c8d2");i({target:"String",proto:!0,forced:o("trim")},{trim:function(){return r(this)}})},"4ad4":function(t,e,n){"use strict";n("caad"),n("45fc"),n("b0c0"),n("b64b");var i=n("53ca"),r=n("16b7"),o=n("f2e7"),a=n("58df"),s=n("80d2"),c=n("d9bd"),l=Object(a["a"])(r["a"],o["a"]);e["a"]=l.extend({name:"activatable",props:{activator:{default:null,validator:function(t){return["string","object"].includes(Object(i["a"])(t))}},disabled:Boolean,internalActivator:Boolean,openOnHover:Boolean,openOnFocus:Boolean},data:function(){return{activatorElement:null,activatorNode:[],events:["click","mouseenter","mouseleave","focus"],listeners:{}}},watch:{activator:"resetActivator",openOnFocus:"resetActivator",openOnHover:"resetActivator"},mounted:function(){var t=Object(s["p"])(this,"activator",!0);t&&["v-slot","normal"].includes(t)&&Object(c["b"])('The activator slot must be bound, try \'