mirror of
https://github.com/overte-org/overte.git
synced 2025-04-09 15:22:27 +02:00
Merge remote-tracking branch 'upstream/master' into update
This commit is contained in:
commit
7bfbf3c99c
161 changed files with 2256 additions and 900 deletions
308
.github/workflows/master_build.yml
vendored
308
.github/workflows/master_build.yml
vendored
|
@ -3,114 +3,60 @@ name: Master CI Build
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- gha-master-ci
|
||||
# FIXME: Change target branch to "master" before merging into "master" branch.
|
||||
|
||||
env:
|
||||
#APP_NAME: gpu-frame-player
|
||||
APP_NAME: interface
|
||||
BUILD_TYPE: Release
|
||||
BUCKET_NAME: hifi-gh-builds
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
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 }}'
|
||||
# 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: PRODUCTION
|
||||
RELEASE_DYNAMODB_V2: ReleaseManager2-ReleaseQueue-prod
|
||||
RELEASE_NUMBER: ${{ github.run_number }}
|
||||
STABLE_BUILD: 0
|
||||
UPLOAD_BUCKET: athena-public
|
||||
|
||||
|
||||
# OSX specific variables
|
||||
# OSX-specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.11'
|
||||
|
||||
# WIN32 specific variables
|
||||
# WIN-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
|
||||
os: [windows-latest, macOS-latest, ubuntu-18.04]
|
||||
# build_type: [full, client]
|
||||
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}}
|
||||
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
|
||||
- name: Report Build Number
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Build number: $BUILD_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`
|
||||
echo ::set-env name=JOB_NAME::"build (${{matrix.os}}, ${{matrix.build_type}})"
|
||||
# Linux build variables
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-"* ]]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::tgz
|
||||
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
|
||||
|
@ -118,8 +64,8 @@ jobs:
|
|||
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"
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
|
@ -127,40 +73,28 @@ jobs:
|
|||
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=CMAKE_EXTRA::"-A x64"
|
||||
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"
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-win-symbols.zip"
|
||||
# echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}}
|
||||
# echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx"
|
||||
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
|
||||
- name: Configure build environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo ::set-env name=ARTIFACT_PATTERN::HighFidelity-Beta-*.$INSTALLER_EXT
|
||||
echo ::set-env name=ARTIFACT_PATTERN::Vircadia-Alpha-*.$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
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-$BUILD_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
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-Interface-$BUILD_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'
|
||||
- name: Clear working directory
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: rm -rf ./*
|
||||
|
@ -168,89 +102,119 @@ jobs:
|
|||
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
|
||||
- name: Install dependencies
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
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
|
||||
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: Install Python modules
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
shell: bash
|
||||
run: $PYTHON_EXEC -m pip install boto3 PyGithub
|
||||
- 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 -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY $CMAKE_EXTRA
|
||||
- name: Build Application
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY -DBYPASS_SIGNING:BOOLEAN=TRUE $CMAKE_EXTRA
|
||||
- name: Build application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Console
|
||||
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 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
|
||||
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 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'
|
||||
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: Sign installer (Windows)
|
||||
# if: startsWith(matrix.os, 'windows')
|
||||
# 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: Output system stats
|
||||
if: ${{ always() }}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Disk usage:"
|
||||
df -h
|
||||
- name: Output installer logs
|
||||
if: failure() && startsWith(matrix.os, 'windows')
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cat ./_CPack_Packages/win64/NSIS/NSISOutput.log
|
||||
- name: Upload artifact
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
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 }}
|
||||
#- name: Archive symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# 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: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# 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=$BUILD_NUMBER"
|
||||
#- name: Debug list symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# shell: bash
|
||||
# run: |
|
||||
# unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
#- name: Upload debug list symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# uses: actions/upload-artifact@v1
|
||||
# with:
|
||||
# name: symbols
|
||||
# path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}
|
||||
|
|
|
@ -112,11 +112,12 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
packetReceiver.registerListenerForTypes(
|
||||
{ PacketType::MixedAudio, PacketType::SilentAudioFrame },
|
||||
this, "handleAudioPacket");
|
||||
PacketReceiver::makeUnsourcedListenerReference<Agent>(this, &Agent::handleAudioPacket));
|
||||
packetReceiver.registerListenerForTypes(
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
PacketReceiver::makeSourcedListenerReference<Agent>(this, &Agent::handleOctreePacket));
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat,
|
||||
PacketReceiver::makeUnsourcedListenerReference<Agent>(this, &Agent::handleSelectedAudioFormat));
|
||||
|
||||
// 100Hz timer for audio
|
||||
const int TARGET_INTERVAL_MSEC = 10; // 10ms
|
||||
|
|
|
@ -118,8 +118,10 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
setUpStatusToMonitor();
|
||||
}
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::CreateAssignment, this, "handleCreateAssignmentPacket");
|
||||
packetReceiver.registerListener(PacketType::StopNode, this, "handleStopNodePacket");
|
||||
packetReceiver.registerListener(PacketType::CreateAssignment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleCreateAssignmentPacket));
|
||||
packetReceiver.registerListener(PacketType::StopNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleStopNodePacket));
|
||||
}
|
||||
|
||||
void AssignmentClient::stopAssignmentClient() {
|
||||
|
|
|
@ -72,7 +72,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClientMonitor>(this, &AssignmentClientMonitor::handleChildStatusPacket));
|
||||
|
||||
adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks));
|
||||
// use QProcess to fork off a process for each of the child assignment clients
|
||||
|
|
|
@ -308,7 +308,8 @@ AssetServer::AssetServer(ReceivedMessage& message) :
|
|||
|
||||
// Queue all requests until the Asset Server is fully setup
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::AssetGet, PacketType::AssetGetInfo, PacketType::AssetUpload, PacketType::AssetMappingOperation }, this, "queueRequests");
|
||||
packetReceiver.registerListenerForTypes({ PacketType::AssetGet, PacketType::AssetGetInfo, PacketType::AssetUpload, PacketType::AssetMappingOperation },
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::queueRequests));
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
updateConsumedCores();
|
||||
|
@ -464,10 +465,14 @@ void AssetServer::completeSetup() {
|
|||
qCDebug(asset_server) << "Overriding temporary queuing packet handler.";
|
||||
// We're fully setup, override the request queueing handler and replay all requests
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet");
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
|
||||
packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload");
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperation, this, "handleAssetMappingOperation");
|
||||
packetReceiver.registerListener(PacketType::AssetGet,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetGet));
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfo,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetGetInfo));
|
||||
packetReceiver.registerListener(PacketType::AssetUpload,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetUpload));
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperation,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetMappingOperation));
|
||||
|
||||
replayRequests();
|
||||
}
|
||||
|
|
|
@ -101,20 +101,23 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
PacketType::InjectorGainSet,
|
||||
PacketType::AudioSoloRequest,
|
||||
PacketType::StopInjector },
|
||||
this, "queueAudioPacket");
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::queueAudioPacket)
|
||||
);
|
||||
|
||||
// packets whose consequences are global should be processed on the main thread
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleMuteEnvironmentPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeMuteRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleNodeMuteRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleKillAvatarPacket));
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedMicrophoneAudioNoEcho,
|
||||
PacketType::ReplicatedMicrophoneAudioWithEcho,
|
||||
PacketType::ReplicatedInjectAudio,
|
||||
PacketType::ReplicatedSilentAudioFrame
|
||||
},
|
||||
this, "queueReplicatedAudioPacket"
|
||||
PacketType::ReplicatedSilentAudioFrame },
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioMixer>(this, &AudioMixer::queueReplicatedAudioPacket)
|
||||
);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||
|
|
|
@ -71,26 +71,38 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting");
|
||||
packetReceiver.registerListener(PacketType::AvatarQuery, this, "handleAvatarQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarData,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
packetReceiver.registerListener(PacketType::AdjustAvatarSorting,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAdjustAvatarSorting));
|
||||
packetReceiver.registerListener(PacketType::AvatarQuery,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAvatarQueryPacket));
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAvatarIdentityPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleKillAvatarPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleNodeIgnoreRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleRadiusIgnoreRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleRequestsDomainListDataPacket));
|
||||
packetReceiver.registerListener(PacketType::SetAvatarTraits,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "queueIncomingPacket");
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleOctreePacket));
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedAvatarIdentity,
|
||||
PacketType::ReplicatedKillAvatar
|
||||
}, this, "handleReplicatedPacket");
|
||||
}, PacketReceiver::makeUnsourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleReplicatedPacket));
|
||||
|
||||
packetReceiver.registerListener(PacketType::ReplicatedBulkAvatarData, this, "handleReplicatedBulkAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::ReplicatedBulkAvatarData,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleReplicatedBulkAvatarPacket));
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch);
|
||||
|
|
|
@ -59,8 +59,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
|
|||
PacketType::ChallengeOwnership,
|
||||
PacketType::ChallengeOwnershipRequest,
|
||||
PacketType::ChallengeOwnershipReply },
|
||||
this,
|
||||
"handleEntityPacket");
|
||||
PacketReceiver::makeSourcedListenerReference<EntityServer>(this, &EntityServer::handleEntityPacket));
|
||||
|
||||
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
|
||||
_dynamicDomainVerificationTimer.setSingleShot(true);
|
||||
|
|
|
@ -25,9 +25,12 @@ MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(mess
|
|||
{
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessages");
|
||||
packetReceiver.registerListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesData,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessages));
|
||||
packetReceiver.registerListener(PacketType::MessagesSubscribe,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessagesSubscribe));
|
||||
packetReceiver.registerListener(PacketType::MessagesUnsubscribe,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessagesUnsubscribe));
|
||||
}
|
||||
|
||||
void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
|
|
|
@ -1122,8 +1122,10 @@ void OctreeServer::run() {
|
|||
|
||||
void OctreeServer::domainSettingsRequestComplete() {
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataNack,
|
||||
PacketReceiver::makeSourcedListenerReference<OctreeServer>(this, &OctreeServer::handleOctreeDataNackPacket));
|
||||
packetReceiver.registerListener(getMyQueryMessageType(),
|
||||
PacketReceiver::makeSourcedListenerReference<OctreeServer>(this, &OctreeServer::handleOctreeQueryPacket));
|
||||
|
||||
qDebug(octree_server) << "Received domain settings";
|
||||
|
||||
|
|
|
@ -83,13 +83,18 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleOctreePacket));
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat,
|
||||
PacketReceiver::makeUnsourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleSelectedAudioFormat));
|
||||
|
||||
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");
|
||||
packetReceiver.registerListener(PacketType::ReloadEntityServerScript,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleReloadEntityServerScriptPacket));
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatus,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleEntityScriptGetStatusPacket));
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleEntityServerScriptLogPacket));
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleEntityScriptCallMethodPacket));
|
||||
|
||||
static const int LOG_INTERVAL = MSECS_PER_SECOND / 10;
|
||||
auto timer = new QTimer(this);
|
||||
|
|
|
@ -202,9 +202,9 @@
|
|||
; The Inner invocation has written an uninstaller binary for us.
|
||||
; We need to sign it if it's a production or PR build.
|
||||
!if @PRODUCTION_BUILD@ == 1
|
||||
!if @BYPASS_SIGNING@ == 1
|
||||
!if @BYPASS_SIGNING@ == TRUE
|
||||
!warning "BYPASS_SIGNING set - installer will not be signed"
|
||||
!else
|
||||
!else
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://timestamp.comodoca.com?td=sha256 /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!endif
|
||||
!endif
|
||||
|
|
2
debian/rules
vendored
2
debian/rules
vendored
|
@ -6,7 +6,7 @@
|
|||
|
||||
override_dh_auto_configure:
|
||||
mkdir obj-$(DEB_TARGET_MULTIARCH)
|
||||
(cd obj-$(DEB_TARGET_MULTIARCH) && cmake .. -DCMAKE_INSTALL_PREFIX=/opt/hifi -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON "-GUnix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCLIENT_ONLY=ON -DDOWNLOAD_SERVERLESS_CONTENT=ON -DCMAKE_CXX_COMPILER=/usr/lib/llvm-7/bin/clang\+\+ -DOpenGL_GL_PREFERENCE=GLVND)
|
||||
(cd obj-$(DEB_TARGET_MULTIARCH) && cmake .. -DCMAKE_INSTALL_PREFIX=/opt/hifi -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON "-GUnix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCLIENT_ONLY=ON -DDOWNLOAD_SERVERLESS_CONTENT=ON -DCMAKE_CXX_COMPILER=/usr/lib/llvm-7/bin/clang\+\+ -DOpenGL_GL_PREFERENCE=LEGACY)
|
||||
|
||||
override_dh_auto_build:
|
||||
(cd obj-$(DEB_TARGET_MULTIARCH) && make -j4)
|
||||
|
|
|
@ -500,6 +500,31 @@ function prepareAccessTokenPrompt(callback) {
|
|||
});
|
||||
}
|
||||
|
||||
function createDomainIDPrompt(callback) {
|
||||
swal({
|
||||
title: 'Finish Registering Domain',
|
||||
type: 'input',
|
||||
text: 'Enter a label for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>This is a required step for registration.</br></br>',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Create",
|
||||
closeOnConfirm: false,
|
||||
html: true
|
||||
}, function (inputValue) {
|
||||
if (inputValue === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("Please enter a valid label for your machine.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(inputValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMetaverseUrl(callback) {
|
||||
$.ajax('/api/metaverse_info', {
|
||||
success: function(data) {
|
||||
|
|
|
@ -40,6 +40,8 @@ $(document).ready(function(){
|
|||
|
||||
// call our method to setup the place names table
|
||||
setupPlacesTable();
|
||||
// hide the places table for now because we do not want that interacted with from the domain-server
|
||||
$('#' + Settings.PLACES_TABLE_ID).hide();
|
||||
|
||||
setupDomainNetworkingSettings();
|
||||
// setupDomainLabelSetting();
|
||||
|
@ -363,7 +365,7 @@ $(document).ready(function(){
|
|||
confirmButtonText: "Create",
|
||||
closeOnConfirm: false,
|
||||
html: true
|
||||
}, function(inputValue){
|
||||
}, function (inputValue) {
|
||||
if (inputValue === false) {
|
||||
swal.close();
|
||||
|
||||
|
@ -373,7 +375,7 @@ $(document).ready(function(){
|
|||
}
|
||||
} else {
|
||||
// we're going to change the alert to a new one with a spinner while we create this domain
|
||||
showSpinnerAlert('Creating domain ID');
|
||||
// showSpinnerAlert('Creating domain ID');
|
||||
createNewDomainID(inputValue, justConnected);
|
||||
}
|
||||
});
|
||||
|
@ -385,7 +387,12 @@ $(document).ready(function(){
|
|||
"label": label
|
||||
}
|
||||
|
||||
$.post("/api/domains", domainJSON, function(data){
|
||||
$.post("/api/domains", domainJSON, function(data) {
|
||||
if (data.status === "failure") {
|
||||
failedToCreateDomainID(data, justConnected);
|
||||
return;
|
||||
}
|
||||
|
||||
// we successfully created a domain ID, set it on that field
|
||||
var domainID = data.domain.domainId;
|
||||
console.log("Setting domain id to ", data, domainID);
|
||||
|
@ -406,40 +413,50 @@ $(document).ready(function(){
|
|||
text: successText,
|
||||
html: true,
|
||||
confirmButtonText: 'Save'
|
||||
}, function(){
|
||||
}, function () {
|
||||
saveSettings();
|
||||
});
|
||||
}, 'json').fail(function(){
|
||||
}, 'json').fail(function (data) {
|
||||
failedToCreateDomainID(data, justConnected);
|
||||
});
|
||||
}
|
||||
|
||||
function failedToCreateDomainID(data, justConnected) {
|
||||
var errorText = "There was a problem creating your new domain ID. Do you want to try again or";
|
||||
|
||||
var errorText = "There was a problem creating your new domain ID. Do you want to try again or";
|
||||
if (data && data.status === "failure") {
|
||||
errorText = "Error: " + data.error + "</br>Do you want to try again or";
|
||||
console.log("Error: " + data.error);
|
||||
} else {
|
||||
console.log("Error: Failed to post to metaverse.");
|
||||
}
|
||||
|
||||
if (justConnected) {
|
||||
errorText += " just save your new access token?</br></br>You can always create a new domain ID later.";
|
||||
if (justConnected) {
|
||||
errorText += " just save your new access token?</br></br>You can always create a new domain ID later.";
|
||||
} else {
|
||||
errorText += " cancel?"
|
||||
}
|
||||
|
||||
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel
|
||||
swal({
|
||||
title: '',
|
||||
type: 'error',
|
||||
text: errorText,
|
||||
html: true,
|
||||
confirmButtonText: 'Try again',
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false
|
||||
}, function (isConfirm) {
|
||||
if (isConfirm) {
|
||||
// they want to try creating a domain ID again
|
||||
showDomainCreationAlert(justConnected);
|
||||
} else {
|
||||
errorText += " cancel?"
|
||||
}
|
||||
|
||||
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel
|
||||
swal({
|
||||
title: '',
|
||||
type: 'error',
|
||||
text: errorText,
|
||||
html: true,
|
||||
confirmButtonText: 'Try again',
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false
|
||||
}, function(isConfirm){
|
||||
if (isConfirm) {
|
||||
// they want to try creating a domain ID again
|
||||
showDomainCreationAlert(justConnected);
|
||||
} else {
|
||||
// they want to cancel
|
||||
if (justConnected) {
|
||||
// since they just connected we need to save the access token here
|
||||
saveSettings();
|
||||
}
|
||||
// they want to cancel
|
||||
if (justConnected) {
|
||||
// since they just connected we need to save the access token here
|
||||
saveSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -711,8 +728,8 @@ $(document).ready(function(){
|
|||
name: 'places',
|
||||
label: 'Places',
|
||||
html_id: Settings.PLACES_TABLE_ID,
|
||||
help: "The following places currently point to this domain.</br>To point places to this domain, "
|
||||
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>My Places</a> "
|
||||
help: "To point places to this domain, "
|
||||
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>Places</a> "
|
||||
+ "page in your Metaverse account.",
|
||||
read_only: true,
|
||||
can_add_new_rows: false,
|
||||
|
@ -745,9 +762,10 @@ $(document).ready(function(){
|
|||
var errorEl = createDomainLoadingError("There was an error retrieving your places.");
|
||||
$("#" + Settings.PLACES_TABLE_ID).after(errorEl);
|
||||
|
||||
var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
|
||||
temporaryPlaceButton.hide();
|
||||
$('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
|
||||
// DISABLE TEMP PLACE NAME BUTTON...
|
||||
// var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
|
||||
// temporaryPlaceButton.hide();
|
||||
// $('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
|
||||
if (accessTokenIsSet()) {
|
||||
appendAddButtonToPlacesTable();
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step col-md-8 col-centered" style="display: none;">
|
||||
<!-- <div class="wizard-step col-md-8 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
@ -51,7 +51,7 @@
|
|||
<button type="button" class="btn btn-md btn-block btn-primary next-button">Next</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="wizard-step col-md-9 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
|
@ -204,7 +204,7 @@
|
|||
</div>
|
||||
<br />
|
||||
<div id="automatic-threading-options-row" class="row">
|
||||
<p class="col-md-2">
|
||||
<p class="col-md-12">
|
||||
<label>
|
||||
<input id="enable-automatic-threading" name="threading-checkbox" type="checkbox" value="true"> Enable Automatic Threading
|
||||
</label>
|
||||
|
|
|
@ -50,6 +50,7 @@ $(document).ready(function(){
|
|||
prepareAccessTokenPrompt(function(accessToken) {
|
||||
Metaverse.accessToken = accessToken;
|
||||
saveAccessToken();
|
||||
promptToCreateDomainID();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -171,6 +172,45 @@ function setupWizardSteps() {
|
|||
$(currentStep).show();
|
||||
}
|
||||
|
||||
function promptToCreateDomainID() {
|
||||
setTimeout(function () {
|
||||
createDomainIDPrompt(function (label) {
|
||||
var domainJSON = {
|
||||
"label": label
|
||||
};
|
||||
|
||||
$.post("/api/domains", domainJSON, function (data) {
|
||||
if (data.status === "failure") {
|
||||
swal.showInputError("Error: " + data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
swal.close();
|
||||
|
||||
// we successfully created a domain ID, set it on that field
|
||||
var domainID = data.domain.domainId;
|
||||
console.log("Setting domain ID to ", data, domainID);
|
||||
|
||||
var formJSON = {
|
||||
"metaverse": {
|
||||
"id": domainID
|
||||
}
|
||||
};
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON, goToNextStep);
|
||||
}, 'json').fail(function (data) {
|
||||
if (data && data.status === "failure") {
|
||||
swal.showInputError("Error: " + data.error);
|
||||
} else {
|
||||
swal.showInputError("Error: Failed to post to metaverse.");
|
||||
}
|
||||
console.log("Failed to create domain ID...");
|
||||
});
|
||||
});
|
||||
}, 500); // Apparently swal needs time before opening another prompt.
|
||||
}
|
||||
|
||||
function updatePlaceNameLink(address) {
|
||||
if (address) {
|
||||
var url = URLs.PLACE_URL + '/' + address;
|
||||
|
@ -341,7 +381,7 @@ function saveAccessToken() {
|
|||
$(this).blur();
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON, goToNextStep);
|
||||
postSettings(formJSON);
|
||||
}
|
||||
|
||||
function getSettingDescriptionForKey(groupKey, settingKey) {
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
#include <openssl/x509.h>
|
||||
#include <random>
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QMetaMethod>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
|
|
|
@ -340,7 +340,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Domain Server");
|
||||
parser.setApplicationDescription("Vircadia Domain Server");
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
|
@ -778,32 +778,51 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
|
||||
// register as the packet receiver for the types we want
|
||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacket");
|
||||
packetReceiver.registerListener(PacketType::RequestAssignment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processRequestAssignmentPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainListRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServer>(this, &DomainServer::processListRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processPathQueryPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeJsonStats,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServer>(this, &DomainServer::processNodeJSONStatsPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processNodeDisconnectRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processAvatarZonePresencePacket));
|
||||
|
||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processSettingsRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeKickRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processNodeKickRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processUsernameFromIDRequestPacket));
|
||||
|
||||
// register the gatekeeper for the packets it needs to receive
|
||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainConnectRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processConnectRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPing,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processICEPingPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processICEPingReplyPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processICEPeerInformationPacket));
|
||||
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK");
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processICEServerHeartbeatDenialPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processICEServerHeartbeatACK));
|
||||
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileRequest, this, "processOctreeDataRequestMessage");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataPersist, this, "processOctreeDataPersistMessage");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processOctreeDataRequestMessage));
|
||||
packetReceiver.registerListener(PacketType::OctreeDataPersist,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processOctreeDataPersistMessage));
|
||||
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacementRequest");
|
||||
packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl, this, "handleDomainContentReplacementFromURLRequest");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::handleOctreeFileReplacementRequest));
|
||||
packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::handleDomainContentReplacementFromURLRequest));
|
||||
|
||||
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
||||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||
|
|
|
@ -212,6 +212,8 @@ private:
|
|||
|
||||
/// guard read/write access from multiple threads to settings
|
||||
QReadWriteLock _settingsLock { QReadWriteLock::Recursive };
|
||||
|
||||
friend class DomainServer;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainServerSettingsManager_h
|
||||
|
|
|
@ -239,7 +239,7 @@ target_openssl()
|
|||
|
||||
target_bullet()
|
||||
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
add_crashpad()
|
||||
target_breakpad()
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
"channels": [
|
||||
{ "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" },
|
||||
|
||||
|
@ -72,46 +72,20 @@
|
|||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPerson", "!Keyboard.Shift"],
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
{ "from": "Keyboard.Left",
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"],
|
||||
"to": "Actions.LATERAL_LEFT"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraThirdPerson", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraLookAt", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraSelfie", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
{ "from": "Keyboard.Right",
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"],
|
||||
"to": "Actions.LATERAL_RIGHT"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
|
@ -119,53 +93,18 @@
|
|||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPerson", "!Keyboard.Control"],
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
{ "from": "Keyboard.A",
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"],
|
||||
"to": "Actions.LATERAL_LEFT"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraThirdPerson", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraLookAt", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraSelfie", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraFirstPerson",
|
||||
"to": "Actions.Yaw"
|
||||
{ "from": "Keyboard.D",
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"],
|
||||
"to": "Actions.LATERAL_RIGHT"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
|
@ -173,39 +112,12 @@
|
|||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraFirstPersonLookat",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraThirdPerson",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraLookAt",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraSelfie",
|
||||
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
|
||||
"when": "Keyboard.RightMouseButton",
|
||||
"when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"],
|
||||
"to": "Actions.DeltaYaw",
|
||||
"filters":
|
||||
[
|
||||
|
@ -213,8 +125,17 @@
|
|||
]
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
|
||||
"to": "Actions.DeltaYaw",
|
||||
"when": "Application.CaptureMouse",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "scale", "scale": 0.2 }
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
|
||||
"when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "Keyboard.RightMouseButton"],
|
||||
"when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"],
|
||||
"to": "Actions.DeltaPitch",
|
||||
"filters":
|
||||
[
|
||||
|
@ -222,6 +143,15 @@
|
|||
]
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
|
||||
"to": "Actions.DeltaPitch",
|
||||
"when": "Application.CaptureMouse",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "scale", "scale": 0.2 }
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
|
||||
"when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"],
|
||||
"to": "Actions.DeltaPitch",
|
||||
|
|
BIN
interface/resources/fonts/vircadia_glyphs.ttf
Normal file
BIN
interface/resources/fonts/vircadia_glyphs.ttf
Normal file
Binary file not shown.
|
@ -333,6 +333,8 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: childrenRect.height;
|
||||
// FIXME: Reuse or remove wallet-related code.
|
||||
visible: false;
|
||||
|
||||
Rectangle {
|
||||
id: walletHeaderContainer;
|
||||
|
|
|
@ -27,6 +27,7 @@ Item {
|
|||
width: parent.width
|
||||
|
||||
property string title: "Controls"
|
||||
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve"]
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
@ -244,7 +245,7 @@ Item {
|
|||
source: InputConfiguration.configurationLayout(box.textAt(box.currentIndex));
|
||||
onLoaded: {
|
||||
if (loader.item.hasOwnProperty("pluginName")) {
|
||||
if (box.textAt(box.currentIndex) === "HTC Vive") {
|
||||
if (openVRDevices.indexOf(box.textAt(box.currentIndex)) !== -1) {
|
||||
loader.item.pluginName = "OpenVR";
|
||||
} else {
|
||||
loader.item.pluginName = box.textAt(box.currentIndex);
|
||||
|
@ -298,7 +299,7 @@ Item {
|
|||
loader.source = "";
|
||||
var selectedDevice = box.textAt(box.currentIndex);
|
||||
var source = "";
|
||||
if (selectedDevice == "HTC Vive") {
|
||||
if (openVRDevices.indexOf(selectedDevice) !== -1) {
|
||||
source = InputConfiguration.configurationLayout("OpenVR");
|
||||
} else {
|
||||
source = InputConfiguration.configurationLayout(selectedDevice);
|
||||
|
|
|
@ -681,6 +681,9 @@ private:
|
|||
* <tr><td><code>CameraIndependent</code></td><td>number</td><td>number</td><td>The camera is in independent mode.</td></tr>
|
||||
* <tr><td><code>CameraEntity</code></td><td>number</td><td>number</td><td>The camera is in entity mode.</td></tr>
|
||||
* <tr><td><code>InHMD</code></td><td>number</td><td>number</td><td>The user is in HMD mode.</td></tr>
|
||||
* <tr><td><code>CaptureMouse</code></td><td>number</td><td>number</td><td>The mouse is captured. In this mode,
|
||||
* the mouse is invisible and cannot leave the bounds of Interface, as long as Interface is the active window and
|
||||
* no menu item is selected.</td></tr>
|
||||
* <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement (walking) controls are
|
||||
* enabled.</td></tr>
|
||||
* <tr><td><code>StrafeEnabled</code></td><td>number</td><td>number</td><td>Strafing is enabled</td></tr>
|
||||
|
@ -716,6 +719,7 @@ static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid";
|
|||
static const QString STATE_LEFT_HAND_DOMINANT = "LeftHandDominant";
|
||||
static const QString STATE_RIGHT_HAND_DOMINANT = "RightHandDominant";
|
||||
static const QString STATE_STRAFE_ENABLED = "StrafeEnabled";
|
||||
static const QString STATE_CAPTURE_MOUSE = "CaptureMouse";
|
||||
|
||||
// Statically provided display and input plugins
|
||||
extern DisplayPluginList getDisplayPlugins();
|
||||
|
@ -919,7 +923,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_FIRST_PERSON_LOOK_AT, STATE_CAMERA_THIRD_PERSON,
|
||||
STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE,
|
||||
STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, STATE_CAPTURE_MOUSE,
|
||||
STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED,
|
||||
STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } });
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
|
@ -1892,6 +1896,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAPTURE_MOUSE, []() -> float {
|
||||
return qApp->getCamera().getCaptureMouse() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
|
||||
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
|
||||
});
|
||||
|
@ -2407,6 +2414,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
updateSystemTabletMode();
|
||||
|
||||
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
|
||||
connect(&_myCamera, &Camera::captureMouseChanged, this, &Application::captureMouseChanged);
|
||||
|
||||
DependencyManager::get<PickManager>()->setShouldPickHUDOperator([]() { return DependencyManager::get<HMDScriptingInterface>()->isHMDMode(); });
|
||||
DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([this](const glm::vec3& intersection) {
|
||||
|
@ -4704,6 +4712,11 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const {
|
|||
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||
PROFILE_RANGE(app_input_mouse, __FUNCTION__);
|
||||
|
||||
if (_ignoreMouseMove) {
|
||||
_ignoreMouseMove = false;
|
||||
return;
|
||||
}
|
||||
|
||||
maybeToggleMenuVisible(event);
|
||||
|
||||
auto& compositor = getApplicationCompositor();
|
||||
|
@ -4749,7 +4762,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
if (_keyboardMouseDevice->isActive()) {
|
||||
_keyboardMouseDevice->mouseMoveEvent(event);
|
||||
_keyboardMouseDevice->mouseMoveEvent(event, _captureMouse, _mouseCaptureTarget);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5950,6 +5963,20 @@ void Application::cameraModeChanged() {
|
|||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
bool Application::shouldCaptureMouse() const {
|
||||
return _captureMouse && _glWidget->isActiveWindow() && !ui::Menu::isSomeSubmenuShown();
|
||||
}
|
||||
|
||||
void Application::captureMouseChanged(bool captureMouse) {
|
||||
_captureMouse = captureMouse;
|
||||
if (_captureMouse) {
|
||||
_glWidget->setCursor(QCursor(Qt::BlankCursor));
|
||||
} else {
|
||||
_mouseCaptureTarget = QPointF(NAN, NAN);
|
||||
_glWidget->unsetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::changeViewAsNeeded(float boomLength) {
|
||||
// Switch between first and third person views as needed
|
||||
// This is called when the boom length has changed
|
||||
|
@ -6300,6 +6327,15 @@ void Application::update(float deltaTime) {
|
|||
PROFILE_ASYNC_END(app, "Scene Loading", "");
|
||||
}
|
||||
|
||||
if (shouldCaptureMouse()) {
|
||||
QPoint point = _glWidget->mapToGlobal(_glWidget->geometry().center());
|
||||
if (QCursor::pos() != point) {
|
||||
_mouseCaptureTarget = point;
|
||||
_ignoreMouseMove = true;
|
||||
QCursor::setPos(point);
|
||||
}
|
||||
}
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
|
@ -6355,8 +6391,8 @@ void Application::update(float deltaTime) {
|
|||
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));
|
||||
myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_YAW));
|
||||
myAvatar->setDriveKey(MyAvatar::DELTA_PITCH, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_PITCH));
|
||||
myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_YAW));
|
||||
myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -431,6 +431,7 @@ public slots:
|
|||
void cycleCamera();
|
||||
void cameraModeChanged();
|
||||
void cameraMenuChanged();
|
||||
void captureMouseChanged(bool captureMouse);
|
||||
void toggleOverlays();
|
||||
void setOverlaysVisible(bool visible);
|
||||
Q_INVOKABLE void centerUI();
|
||||
|
@ -603,6 +604,7 @@ private:
|
|||
|
||||
void maybeToggleMenuVisible(QMouseEvent* event) const;
|
||||
void toggleTabletUI(bool shouldOpen = false) const;
|
||||
bool shouldCaptureMouse() const;
|
||||
|
||||
void userKickConfirmation(const QUuid& nodeID);
|
||||
|
||||
|
@ -756,7 +758,9 @@ private:
|
|||
|
||||
bool _settingsLoaded { false };
|
||||
|
||||
bool _fakedMouseEvent { false };
|
||||
bool _captureMouse { false };
|
||||
bool _ignoreMouseMove { false };
|
||||
QPointF _mouseCaptureTarget { NAN, NAN };
|
||||
|
||||
bool _isMissingSequenceNumbers { false };
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@ class FancyCamera : public Camera {
|
|||
* @property {ViewFrustum} frustum - The camera frustum.
|
||||
* @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the
|
||||
* camera is in entity mode.
|
||||
* @property {boolean} captureMouse - The mouse capture state. When <code>true</code>, the mouse is invisible and cannot leave the bounds of
|
||||
* Interface, as long as Interface is the active window and no menu item is selected. When <code>false</code>, the mouse
|
||||
* behaves normally.
|
||||
* @property {number} sensitivity - The current camera sensitivity. Must be positive.
|
||||
*/
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
|
||||
|
|
|
@ -288,8 +288,10 @@ Wallet::Wallet() {
|
|||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
_passphrase = new QString("");
|
||||
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership,
|
||||
PacketReceiver::makeSourcedListenerReference<Wallet>(this, &Wallet::handleChallengeOwnershipPacket));
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<Wallet>(this, &Wallet::handleChallengeOwnershipPacket));
|
||||
|
||||
connect(ledger.data(), &Ledger::accountResult, this, [](QJsonObject result) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
|
|
|
@ -81,7 +81,7 @@ int main(int argc, const char* argv[]) {
|
|||
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
|
||||
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
|
||||
QCommandLineOption runServerOption("runServer", "Whether to run the server");
|
||||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
|
||||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content <path>", "serverContentPath");
|
||||
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
|
||||
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
|
||||
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
|
||||
|
|
|
@ -25,7 +25,8 @@ OctreePacketProcessor::OctreePacketProcessor():
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
const PacketReceiver::PacketTypeList octreePackets =
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete };
|
||||
packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket");
|
||||
packetReceiver.registerDirectListenerForTypes(octreePackets,
|
||||
PacketReceiver::makeSourcedListenerReference<OctreePacketProcessor>(this, &OctreePacketProcessor::handleOctreePacket));
|
||||
}
|
||||
|
||||
OctreePacketProcessor::~OctreePacketProcessor() { }
|
||||
|
|
|
@ -44,7 +44,8 @@ ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
|
|||
// This packet listener handles the packet containing information about the latest zone ID in which we are allowed to share.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacketOnClient");
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence,
|
||||
PacketReceiver::makeUnsourcedListenerReference<ScreenshareScriptingInterface>(this, &ScreenshareScriptingInterface::processAvatarZonePresencePacketOnClient));
|
||||
};
|
||||
|
||||
ScreenshareScriptingInterface::~ScreenshareScriptingInterface() {
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
//
|
||||
|
||||
#include "DomainConnectionModel.h"
|
||||
#include <QLoggingCategory>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QMetaMethod>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
|
|
@ -358,6 +358,16 @@ void setupPreferences() {
|
|||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->float { return qApp->getCamera().getSensitivity(); };
|
||||
auto setter = [myAvatar](float value) { qApp->getCamera().setSensitivity(value); };
|
||||
auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Camera Sensitivity", getter, setter);
|
||||
preference->setMin(0.01f);
|
||||
preference->setMax(5.0f);
|
||||
preference->setStep(0.1);
|
||||
preference->setDecimals(2);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int { return myAvatar->getControlScheme(); };
|
||||
auto setter = [myAvatar](int index) { myAvatar->setControlScheme(index); };
|
||||
|
|
|
@ -274,7 +274,7 @@ private: \
|
|||
* of 2 if smaller than 128 pixels, or a multiple of 128 if greater than 128 pixels.
|
||||
* <em>Read-only.</em>
|
||||
* @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.
|
||||
* maximum allowed dimensions of 8192 pixels on desktop or 2048 pixels on mobile.
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} gpuBuffers - The number of OpenGL buffer objects managed by the GPU back-end.
|
||||
* <em>Read-only.</em>
|
||||
|
|
|
@ -81,7 +81,8 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply,
|
||||
PacketReceiver::makeSourcedListenerReference<ContextOverlayInterface>(this, &ContextOverlayInterface::handleChallengeOwnershipReplyPacket));
|
||||
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -369,13 +369,20 @@ AudioClient::AudioClient() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "processStreamStatsPacket");
|
||||
packetReceiver.registerListener(PacketType::AudioEnvironment, this, "handleAudioEnvironmentDataPacket");
|
||||
packetReceiver.registerListener(PacketType::SilentAudioFrame, this, "handleAudioDataPacket");
|
||||
packetReceiver.registerListener(PacketType::MixedAudio, this, "handleAudioDataPacket");
|
||||
packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket");
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
packetReceiver.registerListener(PacketType::AudioStreamStats,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioIOStats>(&_stats, &AudioIOStats::processStreamStatsPacket));
|
||||
packetReceiver.registerListener(PacketType::AudioEnvironment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioEnvironmentDataPacket));
|
||||
packetReceiver.registerListener(PacketType::SilentAudioFrame,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioDataPacket));
|
||||
packetReceiver.registerListener(PacketType::MixedAudio,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioDataPacket));
|
||||
packetReceiver.registerListener(PacketType::NoisyMute,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleNoisyMutePacket));
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleMuteEnvironmentPacket));
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleSelectedAudioFormat));
|
||||
|
||||
auto& domainHandler = nodeList->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] {
|
||||
|
|
|
@ -123,10 +123,14 @@ AvatarHashMap::AvatarHashMap() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar");
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraits, this, "processBulkAvatarTraits");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarData,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processAvatarDataPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processKillAvatar));
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processAvatarIdentityPacket));
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraits,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processBulkAvatarTraits));
|
||||
|
||||
connect(nodeList.data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) :
|
|||
}
|
||||
});
|
||||
|
||||
nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride");
|
||||
nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits,
|
||||
PacketReceiver::makeSourcedListenerReference<ClientTraitsHandler>(this, &ClientTraitsHandler::processTraitOverride));
|
||||
}
|
||||
|
||||
void ClientTraitsHandler::markTraitUpdated(AvatarTraits::TraitType updatedTrait) {
|
||||
|
|
|
@ -13,7 +13,7 @@ include_hifi_library_headers(ktx)
|
|||
include_hifi_library_headers(render)
|
||||
include_hifi_library_headers(procedural)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
|
||||
GroupSources("src/display-plugins")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Created by Benjamin Arnold on 5/27/14.
|
||||
// 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
|
||||
|
@ -213,7 +214,6 @@ void CompositorHelper::setAllowMouseCapture(bool capture) {
|
|||
_allowMouseCapture = capture;
|
||||
emit allowMouseCaptureChanged();
|
||||
}
|
||||
_allowMouseCapture = capture;
|
||||
}
|
||||
|
||||
void CompositorHelper::handleLeaveEvent() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis Arnold on 2015/06/13
|
||||
// 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
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
EntityEditPacketSender::EntityEditPacketSender() {
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerDirectListener(PacketType::EntityEditNack, this, "processEntityEditNackPacket");
|
||||
packetReceiver.registerDirectListener(PacketType::EntityEditNack,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityEditPacketSender>(this, &EntityEditPacketSender::processEntityEditNackPacket));
|
||||
}
|
||||
|
||||
void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
|
|
@ -1214,7 +1214,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* ],
|
||||
* strokeWidths: [ 0.1, 0.1, 0.1 ],
|
||||
* color: { red: 255, green: 0, blue: 0 }, // Use just the red channel from the image.
|
||||
* textures: "http://hifi-production.s3.amazonaws.com/DomainContent/Toybox/flowArts/trails.png",
|
||||
* textures: "https://cdn-1.vircadia.com/us-e-1/DomainContent/Toybox/flowArts/trails.png",
|
||||
* isUVModeStretch: true,
|
||||
* lifetime: 300 // Delete after 5 minutes.
|
||||
* });
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
EntityScriptServerLogClient::EntityScriptServerLogClient() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServerLogClient>(this, &EntityScriptServerLogClient::handleEntityServerScriptLogPacket));
|
||||
|
||||
QObject::connect(nodeList.data(), &NodeList::nodeActivated, this, &EntityScriptServerLogClient::nodeActivated);
|
||||
QObject::connect(nodeList.data(), &NodeList::nodeKilled, this, &EntityScriptServerLogClient::nodeKilled);
|
||||
|
|
|
@ -59,7 +59,8 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
|
|||
connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged);
|
||||
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptingInterface>(this, &EntityScriptingInterface::handleEntityScriptCallMethodPacket));
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
|
||||
|
|
|
@ -709,6 +709,8 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Finds all domain and avatar entities that intersect a sphere.
|
||||
* <p><strong>Note:</strong> Server entity scripts only find entities that have a server entity script
|
||||
* running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.</p>
|
||||
* @function Entities.findEntities
|
||||
* @param {Vec3} center - The point about which to search.
|
||||
* @param {number} radius - The radius within which to search.
|
||||
|
@ -723,6 +725,8 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Finds all domain and avatar entities whose axis-aligned boxes intersect a search axis-aligned box.
|
||||
* <p><strong>Note:</strong> Server entity scripts only find entities that have a server entity script
|
||||
* running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.</p>
|
||||
* @function Entities.findEntitiesInBox
|
||||
* @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values.
|
||||
* @param {Vec3} dimensions - The dimensions of the search AA box.
|
||||
|
@ -734,6 +738,8 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Finds all domain and avatar entities whose axis-aligned boxes intersect a search frustum.
|
||||
* <p><strong>Note:</strong> Server entity scripts only find entities that have a server entity script
|
||||
* running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.</p>
|
||||
* @function Entities.findEntitiesInFrustum
|
||||
* @param {ViewFrustum} frustum - The frustum to search in. The <code>position</code>, <code>orientation</code>,
|
||||
* <code>projection</code>, and <code>centerRadius</code> properties must be specified. The <code>fieldOfView</code>
|
||||
|
@ -749,6 +755,8 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Finds all domain and avatar entities of a particular type that intersect a sphere.
|
||||
* <p><strong>Note:</strong> Server entity scripts only find entities that have a server entity script
|
||||
* running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.</p>
|
||||
* @function Entities.findEntitiesByType
|
||||
* @param {Entities.EntityType} entityType - The type of entity to search for.
|
||||
* @param {Vec3} center - The point about which to search.
|
||||
|
@ -764,6 +772,8 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Finds all domain and avatar entities with a particular name that intersect a sphere.
|
||||
* <p><strong>Note:</strong> Server entity scripts only find entities that have a server entity script
|
||||
* running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.</p>
|
||||
* @function Entities.findEntitiesByName
|
||||
* @param {string} entityName - The name of the entity to search for.
|
||||
* @param {Vec3} center - The point about which to search.
|
||||
|
|
|
@ -1586,12 +1586,16 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
|
|||
int targetIndex = weightedIndex;
|
||||
hfmModel.blendshapeChannelNames.push_back("target_" + QString::number(weightedIndex));
|
||||
|
||||
if (!names.isEmpty() && names.contains(keys[weightedIndex])) {
|
||||
if (!names.isEmpty()) {
|
||||
targetIndex = names.indexOf(keys[weightedIndex]);
|
||||
if (targetIndex == -1) {
|
||||
continue; // Ignore blendshape targets not present in glTF file.
|
||||
}
|
||||
indexFromMapping = values[weightedIndex].first;
|
||||
weight = values[weightedIndex].second;
|
||||
hfmModel.blendshapeChannelNames[weightedIndex] = keys[weightedIndex];
|
||||
}
|
||||
|
||||
HFMBlendshape& blendshape = mesh.blendshapes[indexFromMapping];
|
||||
auto target = primitive.targets[targetIndex];
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@ set(TARGET_NAME gl)
|
|||
setup_hifi_library(Gui Widgets)
|
||||
link_hifi_libraries(shared)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
|
||||
|
|
|
@ -74,6 +74,10 @@ static void* getGlProcessAddress(const char *namez) {
|
|||
|
||||
#else
|
||||
|
||||
|
||||
typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int *value);
|
||||
PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC QueryCurrentRendererIntegerMESA;
|
||||
|
||||
static void* getGlProcessAddress(const char *namez) {
|
||||
return (void*)glXGetProcAddressARB((const GLubyte*)namez);
|
||||
}
|
||||
|
@ -92,6 +96,10 @@ void gl::initModuleGl() {
|
|||
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB");
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
QueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)getGlProcessAddress("glXQueryCurrentRendererIntegerMESA");
|
||||
#endif
|
||||
|
||||
#if defined(USE_GLES)
|
||||
gladLoadGLES2Loader(getGlProcessAddress);
|
||||
#else
|
||||
|
@ -124,3 +132,14 @@ void gl::setSwapInterval(int interval) {
|
|||
Q_UNUSED(interval);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool gl::queryCurrentRendererIntegerMESA(int attr, unsigned int *value) {
|
||||
#if defined(Q_OS_LINUX)
|
||||
if (QueryCurrentRendererIntegerMESA) {
|
||||
return QueryCurrentRendererIntegerMESA(attr, value);
|
||||
}
|
||||
#endif
|
||||
|
||||
*value = 0;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace gl {
|
|||
void initModuleGl();
|
||||
int getSwapInterval();
|
||||
void setSwapInterval(int swapInterval);
|
||||
bool queryCurrentRendererIntegerMESA(int attr, unsigned int *value);
|
||||
}
|
||||
|
||||
#endif // hifi_gpu_GPUConfig_h
|
||||
|
|
|
@ -3,6 +3,6 @@ setup_hifi_library(Concurrent)
|
|||
link_hifi_libraries(shared gl gpu shaders)
|
||||
GroupSources("src")
|
||||
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
//
|
||||
#include "GLBackend.h"
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include "gl/Config.h"
|
||||
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
|
@ -105,13 +107,27 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
};
|
||||
|
||||
#define GL_GET_INTEGER(NAME) glGetIntegerv(GL_##NAME, &const_cast<GLint&>(NAME));
|
||||
|
||||
|
||||
#define BYTES_PER_KIB 1024L
|
||||
#define BYTES_PER_MIB (1024L * BYTES_PER_KIB)
|
||||
|
||||
GLint GLBackend::MAX_TEXTURE_IMAGE_UNITS{ 0 };
|
||||
GLint GLBackend::MAX_UNIFORM_BUFFER_BINDINGS{ 0 };
|
||||
GLint GLBackend::MAX_COMBINED_UNIFORM_BLOCKS{ 0 };
|
||||
GLint GLBackend::MAX_COMBINED_TEXTURE_IMAGE_UNITS{ 0 };
|
||||
GLint GLBackend::MAX_UNIFORM_BLOCK_SIZE{ 0 };
|
||||
GLint GLBackend::UNIFORM_BUFFER_OFFSET_ALIGNMENT{ 1 };
|
||||
GLint GLBackend::GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX{ 0 };
|
||||
GLint GLBackend::GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX{ 0 };
|
||||
GLint GLBackend::GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX{ 0 };
|
||||
GLint GLBackend::TEXTURE_FREE_MEMORY_ATI{ 0 };
|
||||
|
||||
size_t GLBackend::_totalMemory{ 0 };
|
||||
size_t GLBackend::_dedicatedMemory{ 0 };
|
||||
GLBackend::VideoCardType GLBackend::_videoCard{ GLBackend::Unknown };
|
||||
|
||||
|
||||
#define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
|
||||
|
||||
void GLBackend::init() {
|
||||
static std::once_flag once;
|
||||
|
@ -132,13 +148,59 @@ void GLBackend::init() {
|
|||
GL_GET_INTEGER(MAX_UNIFORM_BLOCK_SIZE);
|
||||
GL_GET_INTEGER(UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
|
||||
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
|
||||
unsigned int mem;
|
||||
|
||||
if (vendor.contains("NVIDIA") ) {
|
||||
qCDebug(gpugllogging) << "NVIDIA card detected";
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
|
||||
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: " << GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: " << GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: " << GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
|
||||
|
||||
_totalMemory = GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX * BYTES_PER_KIB;
|
||||
_dedicatedMemory = GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX * BYTES_PER_KIB;
|
||||
_videoCard = NVIDIA;
|
||||
|
||||
|
||||
} else if (vendor.contains("ATI")) {
|
||||
qCDebug(gpugllogging) << "ATI card detected";
|
||||
GL_GET_INTEGER(TEXTURE_FREE_MEMORY_ATI);
|
||||
|
||||
_totalMemory = TEXTURE_FREE_MEMORY_ATI * BYTES_PER_KIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
_videoCard = ATI;
|
||||
} else if ( ::gl::queryCurrentRendererIntegerMESA(GLX_RENDERER_VIDEO_MEMORY_MESA, &mem) ) {
|
||||
// This works only on Linux. queryCurrentRendererIntegerMESA will return false if the
|
||||
// function is not supported because we're not on Linux, or for any other reason.
|
||||
qCDebug(gpugllogging) << "MESA card detected";
|
||||
_totalMemory = mem * BYTES_PER_MIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
_videoCard = MESA;
|
||||
} else {
|
||||
qCCritical(gpugllogging) << "Don't know how to get memory for OpenGL vendor " << vendor << "; renderer " << renderer << ", trying fallback";
|
||||
_videoCard = Unknown;
|
||||
_dedicatedMemory = gpu->getMemory();
|
||||
_totalMemory = _dedicatedMemory;
|
||||
}
|
||||
|
||||
qCDebug(gpugllogging) << "dedicated: " << _dedicatedMemory;
|
||||
qCDebug(gpugllogging) << "total: " << _totalMemory;
|
||||
|
||||
|
||||
LOG_GL_CONTEXT_INFO(gpugllogging, contextInfo);
|
||||
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
|
||||
|
||||
|
||||
// From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers.
|
||||
qCDebug(gpugllogging) << "GPU:";
|
||||
qCDebug(gpugllogging) << "\tcard:" << gpu->getName();
|
||||
qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver();
|
||||
qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB";
|
||||
qCDebug(gpugllogging) << "\ttotal memory:" << (_totalMemory / BYTES_PER_KIB) << "KB";
|
||||
qCDebug(gpugllogging) << "\tdedicated memory:" << (_dedicatedMemory / BYTES_PER_KIB) << "KB";
|
||||
qCDebug(gpugllogging) << "\tavailable memory:" << (getAvailableMemory() / BYTES_PER_KIB) << "KB";
|
||||
qCDebug(gpugllogging) << "Limits:";
|
||||
qCDebug(gpugllogging) << "\tmax textures:" << MAX_TEXTURE_IMAGE_UNITS;
|
||||
qCDebug(gpugllogging) << "\tmax texture binding:" << MAX_COMBINED_TEXTURE_IMAGE_UNITS;
|
||||
|
@ -152,6 +214,41 @@ void GLBackend::init() {
|
|||
});
|
||||
}
|
||||
|
||||
size_t GLBackend::getAvailableMemory() {
|
||||
GLint mem;
|
||||
|
||||
switch( _videoCard ) {
|
||||
case NVIDIA:
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &mem);
|
||||
return mem * BYTES_PER_KIB;
|
||||
case ATI:
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &mem);
|
||||
return mem * BYTES_PER_KIB;
|
||||
case MESA:
|
||||
return 0; // Don't know the current value
|
||||
case Unknown:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
bool GLBackend::availableMemoryKnown() {
|
||||
switch( _videoCard ) {
|
||||
case NVIDIA:
|
||||
return true;
|
||||
case ATI:
|
||||
return true;
|
||||
case MESA:
|
||||
return false;
|
||||
case Unknown:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GLBackend::GLBackend(bool syncCache) {
|
||||
_pipeline._cameraCorrectionBuffer._buffer->flush();
|
||||
initShaderBinaryCache();
|
||||
|
|
|
@ -67,6 +67,13 @@ protected:
|
|||
GLBackend();
|
||||
|
||||
public:
|
||||
enum VideoCardType {
|
||||
ATI,
|
||||
NVIDIA,
|
||||
MESA,
|
||||
Unknown
|
||||
};
|
||||
|
||||
#if defined(USE_GLES)
|
||||
// https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGet.xhtml
|
||||
static const GLint MIN_REQUIRED_TEXTURE_IMAGE_UNITS = 16;
|
||||
|
@ -89,6 +96,25 @@ public:
|
|||
static GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS;
|
||||
static GLint MAX_UNIFORM_BLOCK_SIZE;
|
||||
static GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT;
|
||||
static GLint GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
|
||||
static GLint GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX;
|
||||
static GLint GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
|
||||
static GLint TEXTURE_FREE_MEMORY_ATI;
|
||||
|
||||
|
||||
static size_t _totalMemory;
|
||||
static size_t _dedicatedMemory;
|
||||
static VideoCardType _videoCard;
|
||||
|
||||
|
||||
static size_t getTotalMemory() { return _totalMemory; }
|
||||
static size_t getDedicatedMemory() { return _dedicatedMemory; }
|
||||
|
||||
static size_t getAvailableMemory();
|
||||
static bool availableMemoryKnown();
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ~GLBackend();
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#define MAX_RESOURCE_TEXTURES_PER_FRAME 2
|
||||
#define NO_BUFFER_WORK_SLEEP_TIME_MS 2
|
||||
#define THREADED_TEXTURE_BUFFERING 1
|
||||
#define MAX_AUTO_FRACTION_OF_TOTAL_MEMORY 0.8f
|
||||
#define AUTO_RESERVE_TEXTURE_MEMORY MB_TO_BYTES(64)
|
||||
|
||||
static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB);
|
||||
|
||||
|
@ -183,9 +185,25 @@ void GLTextureTransferEngineDefault::manageMemory() {
|
|||
void GLTextureTransferEngineDefault::updateMemoryPressure() {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
|
||||
|
||||
bool useAvailableGlMemory = false;
|
||||
size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
|
||||
|
||||
if (0 == allowedMemoryAllocation) {
|
||||
allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY;
|
||||
// Automatic allocation
|
||||
|
||||
if (GLBackend::availableMemoryKnown()) {
|
||||
// If we know how much is free, then we use that
|
||||
useAvailableGlMemory = true;
|
||||
} else {
|
||||
// We don't know how much is free, so leave some reasonable spare room
|
||||
// and hope it works.
|
||||
allowedMemoryAllocation = GLBackend::getTotalMemory() * MAX_AUTO_FRACTION_OF_TOTAL_MEMORY;
|
||||
|
||||
if (0 == allowedMemoryAllocation) {
|
||||
// Last resort, if we failed to detect
|
||||
allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any defunct textures (weak pointers that no longer have a valid texture)
|
||||
|
@ -205,7 +223,7 @@ void GLTextureTransferEngineDefault::updateMemoryPressure() {
|
|||
idealMemoryAllocation += texture->evalTotalSize();
|
||||
// Track how much we're actually using
|
||||
totalVariableMemoryAllocation += gltexture->size();
|
||||
if (vartexture->canDemote()) {
|
||||
if (!gltexture->_gpuObject.getImportant() && vartexture->canDemote()) {
|
||||
canDemote |= true;
|
||||
}
|
||||
if (vartexture->canPromote()) {
|
||||
|
@ -218,7 +236,22 @@ void GLTextureTransferEngineDefault::updateMemoryPressure() {
|
|||
|
||||
Backend::textureResourceIdealGPUMemSize.set(idealMemoryAllocation);
|
||||
size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation;
|
||||
float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation;
|
||||
float pressure = 0;
|
||||
|
||||
if (useAvailableGlMemory) {
|
||||
size_t totalMem = GLBackend::getTotalMemory();
|
||||
size_t availMem = GLBackend::getAvailableMemory();
|
||||
|
||||
if (availMem >= AUTO_RESERVE_TEXTURE_MEMORY) {
|
||||
availMem -= AUTO_RESERVE_TEXTURE_MEMORY;
|
||||
} else {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
pressure = ((float)totalMem - (float)availMem) / (float)totalMem;
|
||||
} else {
|
||||
pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation;
|
||||
}
|
||||
|
||||
// If we're oversubscribed we need to demote textures IMMEDIATELY
|
||||
if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) {
|
||||
|
@ -470,7 +503,7 @@ void GLTextureTransferEngineDefault::processDemotes(size_t reliefRequired, const
|
|||
for (const auto& texture : strongTextures) {
|
||||
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
|
||||
if (vargltexture->canDemote()) {
|
||||
if (!gltexture->_gpuObject.getImportant() && vargltexture->canDemote()) {
|
||||
demoteQueue.push({ texture, (float)gltexture->size() });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@ if (UNIX)
|
|||
endif(UNIX)
|
||||
GroupSources("src")
|
||||
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
|
||||
|
|
|
@ -571,6 +571,9 @@ public:
|
|||
void setExternalRecycler(const ExternalRecycler& recycler);
|
||||
ExternalRecycler getExternalRecycler() const;
|
||||
|
||||
bool getImportant() const { return _important; }
|
||||
void setImportant(bool important) { _important = important; }
|
||||
|
||||
const GPUObjectPointer gpuObject {};
|
||||
|
||||
ExternalUpdates getUpdates() const;
|
||||
|
@ -632,6 +635,7 @@ protected:
|
|||
bool _autoGenerateMips = false;
|
||||
bool _isIrradianceValid = false;
|
||||
bool _defined = false;
|
||||
bool _important = false;
|
||||
|
||||
static TexturePointer create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Sam Gateau on 4/27/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
|
||||
|
@ -36,13 +37,12 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputC
|
|||
_inputDevice->_axisStateMap[MOUSE_AXIS_X].value = _lastCursor.x();
|
||||
_inputDevice->_axisStateMap[MOUSE_AXIS_Y].value = _lastCursor.y();
|
||||
|
||||
QPoint currentMove = _lastCursor - _previousCursor;
|
||||
updateDeltaAxisValue(MOUSE_AXIS_X_POS, currentMove.x() > 0 ? currentMove.x() : 0.0f);
|
||||
updateDeltaAxisValue(MOUSE_AXIS_X_NEG, currentMove.x() < 0 ? -currentMove.x() : 0.0f);
|
||||
updateDeltaAxisValue(MOUSE_AXIS_X_POS, _accumulatedMove.x() > 0 ? _accumulatedMove.x() : 0.0f);
|
||||
updateDeltaAxisValue(MOUSE_AXIS_X_NEG, _accumulatedMove.x() < 0 ? -_accumulatedMove.x() : 0.0f);
|
||||
// Y mouse is inverted positive is pointing up the screen
|
||||
updateDeltaAxisValue(MOUSE_AXIS_Y_POS, currentMove.y() < 0 ? -currentMove.y() : 0.0f);
|
||||
updateDeltaAxisValue(MOUSE_AXIS_Y_NEG, currentMove.y() > 0 ? currentMove.y() : 0.0f);
|
||||
_previousCursor = _lastCursor;
|
||||
updateDeltaAxisValue(MOUSE_AXIS_Y_POS, _accumulatedMove.y() < 0 ? -_accumulatedMove.y() : 0.0f);
|
||||
updateDeltaAxisValue(MOUSE_AXIS_Y_NEG, _accumulatedMove.y() > 0 ? _accumulatedMove.y() : 0.0f);
|
||||
_accumulatedMove = QPoint(0.0f, 0.0f);
|
||||
});
|
||||
|
||||
// For touch event, we need to check that the last event is not too long ago
|
||||
|
@ -113,9 +113,16 @@ void KeyboardMouseDevice::eraseMouseClicked() {
|
|||
_inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel());
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) {
|
||||
void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, bool capture, QPointF captureTarget) {
|
||||
QPoint currentPos = event->pos();
|
||||
|
||||
if (!capture) {
|
||||
_accumulatedMove += currentPos - _lastCursor;
|
||||
} else if (!isNaN(captureTarget.x())) {
|
||||
QPointF change = event->globalPos() - captureTarget;
|
||||
_accumulatedMove += QPoint(change.x(), change.y());
|
||||
}
|
||||
|
||||
// FIXME - this has the characteristic that it will show large jumps when you move the cursor
|
||||
// outside of the application window, because we don't get MouseEvents when the cursor is outside
|
||||
// of the application window.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Sam Gateau on 4/27/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
|
||||
|
@ -79,7 +80,7 @@ public:
|
|||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event, bool capture, QPointF captureTarget);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void eraseMouseClicked();
|
||||
|
@ -123,7 +124,7 @@ public:
|
|||
|
||||
protected:
|
||||
QPoint _lastCursor;
|
||||
QPoint _previousCursor;
|
||||
QPoint _accumulatedMove;
|
||||
QPoint _mousePressPos;
|
||||
quint64 _mousePressTime;
|
||||
qreal _lastTotalScaleFactor;
|
||||
|
|
|
@ -43,10 +43,14 @@ AssetClient::AssetClient() {
|
|||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperationReply, this, "handleAssetMappingOperationReply");
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfoReply, this, "handleAssetGetInfoReply");
|
||||
packetReceiver.registerListener(PacketType::AssetGetReply, this, "handleAssetGetReply", true);
|
||||
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperationReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetMappingOperationReply));
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfoReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetGetInfoReply));
|
||||
packetReceiver.registerListener(PacketType::AssetGetReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetGetReply), true);
|
||||
packetReceiver.registerListener(PacketType::AssetUploadReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetUploadReply));
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &AssetClient::handleNodeKilled);
|
||||
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,
|
||||
|
|
|
@ -76,9 +76,9 @@ const quint16 DOMAIN_SERVER_EXPORTER_PORT =
|
|||
|
||||
const quint16 DOMAIN_SERVER_METADATA_EXPORTER_PORT =
|
||||
QProcessEnvironment::systemEnvironment()
|
||||
.contains("DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
.contains("VIRCADIA_DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
? QProcessEnvironment::systemEnvironment()
|
||||
.value("DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
.value("VIRCADIA_DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
.toUInt()
|
||||
: 9704;
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ EntityScriptClient::EntityScriptClient() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatusReply, this, "handleGetScriptStatusReply");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatusReply,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptClient>(this, &EntityScriptClient::handleGetScriptStatusReply));
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptClient::handleNodeKilled);
|
||||
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileSelector>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <StatTracker.h>
|
||||
|
@ -54,6 +55,7 @@ void FileResourceRequest::doSend() {
|
|||
} else {
|
||||
QFile file(filename);
|
||||
if (file.exists()) {
|
||||
setProperty("last-modified", toHttpDateString(QFileInfo(file).lastModified().toMSecsSinceEpoch()));
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
|
||||
if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) {
|
||||
|
|
|
@ -28,7 +28,8 @@ MessagesClient::MessagesClient() {
|
|||
});
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket");
|
||||
packetReceiver.registerListener(PacketType::MessagesData,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesClient>(this, &MessagesClient::handleMessagesPacket));
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);
|
||||
}
|
||||
|
||||
|
|
|
@ -139,20 +139,34 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
|
|||
startSTUNPublicSocketUpdate();
|
||||
|
||||
auto& packetReceiver = getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::DomainList, this, "processDomainServerList");
|
||||
packetReceiver.registerListener(PacketType::Ping, this, "processPingPacket");
|
||||
packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode");
|
||||
packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainConnectionDenied, &_domainHandler, "processDomainServerConnectionDeniedPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainSettings, &_domainHandler, "processSettingsPacketList");
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse");
|
||||
packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode");
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDReply, this, "processUsernameFromIDReply");
|
||||
packetReceiver.registerListener(PacketType::DomainList,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerList));
|
||||
packetReceiver.registerListener(PacketType::Ping,
|
||||
PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingPacket));
|
||||
packetReceiver.registerListener(PacketType::PingReply,
|
||||
PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingReplyPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPing,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processICEPingPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerAddedNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerAddedNode));
|
||||
packetReceiver.registerListener(PacketType::DomainServerConnectionToken,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerConnectionTokenPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainConnectionDenied,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processDomainServerConnectionDeniedPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainSettings,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processSettingsPacketList));
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processICEResponsePacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processDTLSRequirementPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processICEPingReplyPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathResponse,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerPathResponse));
|
||||
packetReceiver.registerListener(PacketType::DomainServerRemovedNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerRemovedNode));
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processUsernameFromIDReply));
|
||||
}
|
||||
|
||||
qint64 NodeList::sendStats(QJsonObject statsObject, HifiSockAddr destination) {
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
#include "PacketReceiver.h"
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QtCore/QMetaObject>
|
||||
#include <QtCore/QMutexLocker>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
@ -25,85 +26,57 @@ PacketReceiver::PacketReceiver(QObject* parent) : QObject(parent) {
|
|||
qRegisterMetaType<QSharedPointer<ReceivedMessage>>();
|
||||
}
|
||||
|
||||
bool PacketReceiver::registerListenerForTypes(PacketTypeList types, QObject* listener, const char* slot) {
|
||||
bool PacketReceiver::ListenerReference::invokeWithQt(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode) {
|
||||
ListenerReferencePointer thisPointer = sharedFromThis();
|
||||
return QMetaObject::invokeMethod(getObject(), [=]() {
|
||||
thisPointer->invokeDirectly(receivedMessagePointer, sourceNode);
|
||||
});
|
||||
}
|
||||
|
||||
bool PacketReceiver::registerListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener) {
|
||||
Q_ASSERT_X(!types.empty(), "PacketReceiver::registerListenerForTypes", "No types to register");
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListenerForTypes", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerListenerForTypes", "No slot to register");
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListenerForTypes", "No listener to register");
|
||||
|
||||
// Partition types based on whether they are sourced or not (non sourced in front)
|
||||
auto middle = std::partition(std::begin(types), std::end(types), [](PacketType type) {
|
||||
return PacketTypeEnum::getNonSourcedPackets().contains(type);
|
||||
});
|
||||
|
||||
QMetaMethod nonSourcedMethod, sourcedMethod;
|
||||
|
||||
// Check we have a valid method for non sourced types if any
|
||||
if (middle != std::begin(types)) {
|
||||
nonSourcedMethod = matchingMethodForListener(*std::begin(types), listener, slot);
|
||||
if (!nonSourcedMethod.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have a valid method for sourced types if any
|
||||
if (middle != std::end(types)) {
|
||||
sourcedMethod = matchingMethodForListener(*middle, listener, slot);
|
||||
if (!sourcedMethod.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Register non sourced types
|
||||
std::for_each(std::begin(types), middle, [this, &listener, &nonSourcedMethod](PacketType type) {
|
||||
registerVerifiedListener(type, listener, nonSourcedMethod);
|
||||
});
|
||||
|
||||
// Register sourced types
|
||||
std::for_each(middle, std::end(types), [this, &listener, &sourcedMethod](PacketType type) {
|
||||
registerVerifiedListener(type, listener, sourcedMethod);
|
||||
std::for_each(std::begin(types), std::end(types), [this, &listener](PacketType type) {
|
||||
registerVerifiedListener(type, listener);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketReceiver::registerDirectListener(PacketType type, QObject* listener, const char* slot) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListener", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerDirectListener", "No slot to register");
|
||||
void PacketReceiver::registerDirectListener(PacketType type, const ListenerReferencePointer& listener) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListener", "No listener to register");
|
||||
|
||||
bool success = registerListener(type, listener, slot);
|
||||
bool success = registerListener(type, listener);
|
||||
if (success) {
|
||||
QMutexLocker locker(&_directConnectSetMutex);
|
||||
|
||||
// if we successfully registered, add this object to the set of objects that are directly connected
|
||||
_directlyConnectedObjects.insert(listener);
|
||||
_directlyConnectedObjects.insert(listener->getObject());
|
||||
}
|
||||
}
|
||||
|
||||
void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types,
|
||||
QObject* listener, const char* slot) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListenerForTypes", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerDirectListenerForTypes", "No slot to register");
|
||||
void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListenerForTypes", "No listener to register");
|
||||
|
||||
// just call register listener for types to start
|
||||
bool success = registerListenerForTypes(std::move(types), listener, slot);
|
||||
bool success = registerListenerForTypes(std::move(types), listener);
|
||||
if (success) {
|
||||
QMutexLocker locker(&_directConnectSetMutex);
|
||||
|
||||
// if we successfully registered, add this object to the set of objects that are directly connected
|
||||
_directlyConnectedObjects.insert(listener);
|
||||
_directlyConnectedObjects.insert(listener->getObject());
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketReceiver::registerListener(PacketType type, QObject* listener, const char* slot,
|
||||
bool deliverPending) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListener", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerListener", "No slot to register");
|
||||
bool PacketReceiver::registerListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListener", "No listener to register");
|
||||
|
||||
QMetaMethod matchingMethod = matchingMethodForListener(type, listener, slot);
|
||||
bool matchingMethod = matchingMethodForListener(type, listener);
|
||||
|
||||
if (matchingMethod.isValid()) {
|
||||
if (matchingMethod) {
|
||||
qCDebug(networking) << "Registering a packet listener for packet list type" << type;
|
||||
registerVerifiedListener(type, listener, matchingMethod, deliverPending);
|
||||
registerVerifiedListener(type, listener, deliverPending);
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(networking) << "FAILED to Register a packet listener for packet list type" << type;
|
||||
|
@ -111,62 +84,23 @@ bool PacketReceiver::registerListener(PacketType type, QObject* listener, const
|
|||
}
|
||||
}
|
||||
|
||||
QMetaMethod PacketReceiver::matchingMethodForListener(PacketType type, QObject* object, const char* slot) const {
|
||||
Q_ASSERT_X(object, "PacketReceiver::matchingMethodForListener", "No object to call");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::matchingMethodForListener", "No slot to call");
|
||||
bool PacketReceiver::matchingMethodForListener(PacketType type, const ListenerReferencePointer& listener) const {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::matchingMethodForListener", "No listener to call");
|
||||
|
||||
// normalize the slot with the expected parameters
|
||||
static const QString SIGNATURE_TEMPLATE("%1(%2)");
|
||||
static const QString NON_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>";
|
||||
bool isSourced = listener->isSourced();
|
||||
bool isNonSourcedPacket = PacketTypeEnum::getNonSourcedPackets().contains(type);
|
||||
|
||||
QSet<QString> possibleSignatures {
|
||||
SIGNATURE_TEMPLATE.arg(slot, NON_SOURCED_MESSAGE_LISTENER_PARAMETERS)
|
||||
};
|
||||
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(type)) {
|
||||
static const QString SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>,QSharedPointer<Node>";
|
||||
static const QString TYPEDEF_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>,SharedNodePointer";
|
||||
|
||||
// a sourced packet must take the shared pointer to the ReceivedMessage but optionally could include
|
||||
// a shared pointer to the node
|
||||
possibleSignatures << SIGNATURE_TEMPLATE.arg(slot, TYPEDEF_SOURCED_MESSAGE_LISTENER_PARAMETERS);
|
||||
possibleSignatures << SIGNATURE_TEMPLATE.arg(slot, SOURCED_MESSAGE_LISTENER_PARAMETERS);
|
||||
assert(!isSourced || !isNonSourcedPacket);
|
||||
if (isSourced && isNonSourcedPacket) {
|
||||
qCDebug(networking) << "PacketReceiver::registerListener cannot support a sourced listener for type" << type;
|
||||
return false;
|
||||
}
|
||||
|
||||
int methodIndex = -1;
|
||||
|
||||
foreach(const QString& signature, possibleSignatures) {
|
||||
QByteArray normalizedSlot =
|
||||
QMetaObject::normalizedSignature(signature.toStdString().c_str());
|
||||
|
||||
// does the constructed normalized method exist?
|
||||
methodIndex = object->metaObject()->indexOfSlot(normalizedSlot.toStdString().c_str());
|
||||
|
||||
if (methodIndex >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (methodIndex < 0) {
|
||||
qCDebug(networking) << "PacketReceiver::registerListener expected a slot with one of the following signatures:"
|
||||
<< possibleSignatures.toList() << "- but such a slot was not found."
|
||||
<< "Could not complete listener registration for type" << type;
|
||||
}
|
||||
|
||||
Q_ASSERT(methodIndex >= 0);
|
||||
|
||||
// return the converted QMetaMethod
|
||||
if (methodIndex >= 0) {
|
||||
return object->metaObject()->method(methodIndex);
|
||||
} else {
|
||||
// if somehow (scripting?) something bad gets in here at runtime that doesn't hit the asserts above
|
||||
// return a non-valid QMetaMethod
|
||||
return QMetaMethod();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object, const QMetaMethod& slot, bool deliverPending) {
|
||||
Q_ASSERT_X(object, "PacketReceiver::registerVerifiedListener", "No object to register");
|
||||
void PacketReceiver::registerVerifiedListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerVerifiedListener", "No listener to register");
|
||||
QMutexLocker locker(&_packetListenerLock);
|
||||
|
||||
if (_messageListenerMap.contains(type)) {
|
||||
|
@ -175,7 +109,7 @@ void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object,
|
|||
}
|
||||
|
||||
// add the mapping
|
||||
_messageListenerMap[type] = { QPointer<QObject>(object), slot, deliverPending };
|
||||
_messageListenerMap[type] = { listener, deliverPending };
|
||||
}
|
||||
|
||||
void PacketReceiver::unregisterListener(QObject* listener) {
|
||||
|
@ -188,7 +122,7 @@ void PacketReceiver::unregisterListener(QObject* listener) {
|
|||
auto it = _messageListenerMap.begin();
|
||||
|
||||
while (it != _messageListenerMap.end()) {
|
||||
if (it.value().object == listener) {
|
||||
if (it.value().listener->getObject() == listener) {
|
||||
it = _messageListenerMap.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
@ -261,7 +195,7 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
||||
|
||||
auto it = _messageListenerMap.find(receivedMessage->getType());
|
||||
if (it != _messageListenerMap.end() && it->method.isValid()) {
|
||||
if (it != _messageListenerMap.end() && !it->listener.isNull()) {
|
||||
|
||||
auto listener = it.value();
|
||||
|
||||
|
@ -271,36 +205,19 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
|
||||
bool success = false;
|
||||
|
||||
Qt::ConnectionType connectionType;
|
||||
bool isDirectConnect = false;
|
||||
// check if this is a directly connected listener
|
||||
{
|
||||
QMutexLocker directConnectLocker(&_directConnectSetMutex);
|
||||
connectionType = _directlyConnectedObjects.contains(listener.object) ? Qt::DirectConnection : Qt::AutoConnection;
|
||||
isDirectConnect = _directlyConnectedObjects.contains(listener.listener->getObject());
|
||||
}
|
||||
|
||||
QMetaMethod metaMethod = listener.method;
|
||||
|
||||
static const QByteArray QSHAREDPOINTER_NODE_NORMALIZED = QMetaObject::normalizedType("QSharedPointer<Node>");
|
||||
static const QByteArray SHARED_NODE_NORMALIZED = QMetaObject::normalizedType("SharedNodePointer");
|
||||
|
||||
// one final check on the QPointer before we go to invoke
|
||||
if (listener.object) {
|
||||
if (metaMethod.parameterTypes().contains(SHARED_NODE_NORMALIZED)) {
|
||||
success = metaMethod.invoke(listener.object,
|
||||
connectionType,
|
||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage),
|
||||
Q_ARG(SharedNodePointer, matchingNode));
|
||||
|
||||
} else if (metaMethod.parameterTypes().contains(QSHAREDPOINTER_NODE_NORMALIZED)) {
|
||||
success = metaMethod.invoke(listener.object,
|
||||
connectionType,
|
||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage),
|
||||
Q_ARG(QSharedPointer<Node>, matchingNode));
|
||||
|
||||
if (listener.listener->getObject()) {
|
||||
if (isDirectConnect) {
|
||||
success = listener.listener->invokeDirectly(receivedMessage, matchingNode);
|
||||
} else {
|
||||
success = metaMethod.invoke(listener.object,
|
||||
connectionType,
|
||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage));
|
||||
success = listener.listener->invokeWithQt(receivedMessage, matchingNode);
|
||||
}
|
||||
} else {
|
||||
qCDebug(networking).nospace() << "Listener for packet " << receivedMessage->getType()
|
||||
|
@ -310,19 +227,19 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
// if it exists, remove the listener from _directlyConnectedObjects
|
||||
{
|
||||
QMutexLocker directConnectLocker(&_directConnectSetMutex);
|
||||
_directlyConnectedObjects.remove(listener.object);
|
||||
_directlyConnectedObjects.remove(listener.listener->getObject());
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
qCDebug(networking).nospace() << "Error delivering packet " << receivedMessage->getType() << " to listener "
|
||||
<< listener.object << "::" << qPrintable(listener.method.methodSignature());
|
||||
<< listener.listener->getObject();
|
||||
}
|
||||
|
||||
} else if (it == _messageListenerMap.end()) {
|
||||
qCWarning(networking) << "No listener found for packet type" << receivedMessage->getType();
|
||||
|
||||
// insert a dummy listener so we don't print this again
|
||||
_messageListenerMap.insert(receivedMessage->getType(), { nullptr, QMetaMethod(), false });
|
||||
_messageListenerMap.insert(receivedMessage->getType(), { ListenerReferencePointer(), false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QEnableSharedFromThis>
|
||||
|
||||
#include "NLPacket.h"
|
||||
#include "NLPacketList.h"
|
||||
|
@ -29,6 +29,7 @@
|
|||
#include "udt/PacketHeaders.h"
|
||||
|
||||
class EntityEditPacketSender;
|
||||
class Node;
|
||||
class OctreePacketProcessor;
|
||||
|
||||
namespace std {
|
||||
|
@ -42,6 +43,22 @@ namespace std {
|
|||
|
||||
class PacketReceiver : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class ListenerReference : public QEnableSharedFromThis<ListenerReference> {
|
||||
public:
|
||||
virtual bool invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode) = 0;
|
||||
bool invokeWithQt(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode);
|
||||
virtual bool isSourced() const = 0;
|
||||
virtual QObject* getObject() const = 0;
|
||||
};
|
||||
typedef QSharedPointer<ListenerReference> ListenerReferencePointer;
|
||||
|
||||
template<class T>
|
||||
static ListenerReferencePointer makeUnsourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>));
|
||||
|
||||
template <class T>
|
||||
static ListenerReferencePointer makeSourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>));
|
||||
|
||||
public:
|
||||
using PacketTypeList = std::vector<PacketType>;
|
||||
|
||||
|
@ -55,8 +72,8 @@ public:
|
|||
// If deliverPending is false, ReceivedMessage will only be delivered once all packets for the message have
|
||||
// been received. If deliverPending is true, ReceivedMessage will be delivered as soon as the first packet
|
||||
// for the message is received.
|
||||
bool registerListener(PacketType type, QObject* listener, const char* slot, bool deliverPending = false);
|
||||
bool registerListenerForTypes(PacketTypeList types, QObject* listener, const char* slot);
|
||||
bool registerListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending = false);
|
||||
bool registerListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener);
|
||||
void unregisterListener(QObject* listener);
|
||||
|
||||
void handleVerifiedPacket(std::unique_ptr<udt::Packet> packet);
|
||||
|
@ -64,9 +81,34 @@ public:
|
|||
void handleMessageFailure(HifiSockAddr from, udt::Packet::MessageNumber messageNumber);
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
class UnsourcedListenerReference : public ListenerReference {
|
||||
public:
|
||||
inline UnsourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>));
|
||||
virtual bool invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode);
|
||||
virtual bool isSourced() const { return false; }
|
||||
virtual QObject* getObject() const { return _target; }
|
||||
|
||||
private:
|
||||
QPointer<T> _target;
|
||||
void (T::*_slot)(QSharedPointer<ReceivedMessage>);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class SourcedListenerReference : public ListenerReference {
|
||||
public:
|
||||
inline SourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>));
|
||||
virtual bool invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode);
|
||||
virtual bool isSourced() const { return true; }
|
||||
virtual QObject* getObject() const { return _target; }
|
||||
|
||||
private:
|
||||
QPointer<T> _target;
|
||||
void (T::*_slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>);
|
||||
};
|
||||
|
||||
struct Listener {
|
||||
QPointer<QObject> object;
|
||||
QMetaMethod method;
|
||||
ListenerReferencePointer listener;
|
||||
bool deliverPending;
|
||||
};
|
||||
|
||||
|
@ -74,11 +116,11 @@ private:
|
|||
|
||||
// these are brutal hacks for now - ideally GenericThread / ReceivedPacketProcessor
|
||||
// should be changed to have a true event loop and be able to handle our QMetaMethod::invoke
|
||||
void registerDirectListenerForTypes(PacketTypeList types, QObject* listener, const char* slot);
|
||||
void registerDirectListener(PacketType type, QObject* listener, const char* slot);
|
||||
void registerDirectListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener);
|
||||
void registerDirectListener(PacketType type, const ListenerReferencePointer& listener);
|
||||
|
||||
QMetaMethod matchingMethodForListener(PacketType type, QObject* object, const char* slot) const;
|
||||
void registerVerifiedListener(PacketType type, QObject* listener, const QMetaMethod& slot, bool deliverPending = false);
|
||||
bool matchingMethodForListener(PacketType type, const ListenerReferencePointer& listener) const;
|
||||
void registerVerifiedListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending = false);
|
||||
|
||||
QMutex _packetListenerLock;
|
||||
QHash<PacketType, Listener> _messageListenerMap;
|
||||
|
@ -93,4 +135,42 @@ private:
|
|||
friend class OctreePacketProcessor;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::ListenerReferencePointer PacketReceiver::makeUnsourcedListenerReference(T* target, void (T::* slot)(QSharedPointer<ReceivedMessage>)) {
|
||||
return QSharedPointer<UnsourcedListenerReference<T>>::create(target, slot);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::ListenerReferencePointer PacketReceiver::makeSourcedListenerReference(T* target, void (T::* slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>)) {
|
||||
return QSharedPointer<SourcedListenerReference<T>>::create(target, slot);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::UnsourcedListenerReference<T>::UnsourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>)) :
|
||||
_target(target),_slot(slot) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool PacketReceiver::UnsourcedListenerReference<T>::invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>&) {
|
||||
if (_target.isNull()) {
|
||||
return false;
|
||||
}
|
||||
(_target->*_slot)(receivedMessagePointer);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::SourcedListenerReference<T>::SourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>)) :
|
||||
_target(target),_slot(slot) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool PacketReceiver::SourcedListenerReference<T>::invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode) {
|
||||
if (_target.isNull()) {
|
||||
return false;
|
||||
}
|
||||
(_target->*_slot)(receivedMessagePointer, sourceNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // hifi_PacketReceiver_h
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
#include <cmath>
|
||||
#include <assert.h>
|
||||
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
|
|
|
@ -15,8 +15,14 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <StatTracker.h>
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
QString ResourceRequest::toHttpDateString(uint64_t msecsSinceEpoch) {
|
||||
return QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch)
|
||||
.toString("ddd, dd MMM yyyy hh:mm:ss 'GMT'")
|
||||
.toLatin1();
|
||||
}
|
||||
|
||||
void ResourceRequest::send() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
|
|
@ -90,6 +90,7 @@ public:
|
|||
void setCacheEnabled(bool value) { _cacheEnabled = value; }
|
||||
void setByteRange(ByteRange byteRange) { _byteRange = byteRange; }
|
||||
|
||||
static QString toHttpDateString(uint64_t msecsSinceEpoch);
|
||||
public slots:
|
||||
void send();
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@ void OctreePersistThread::start() {
|
|||
cleanupOldReplacementBackups();
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileReply, this, "handleOctreeDataFileReply");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<OctreePersistThread>(this, &OctreePersistThread::handleOctreeDataFileReply));
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
|
|
@ -3,5 +3,5 @@ setup_hifi_library(Multimedia Network Qml Quick WebChannel WebSockets ${PLATFORM
|
|||
link_hifi_libraries(shared networking gl)
|
||||
|
||||
# Required for some low level GL interaction in the OffscreenQMLSurface
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
|
|
|
@ -260,6 +260,7 @@ void Font::read(QIODevice& in) {
|
|||
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
|
||||
_texture->setStoredMipFormat(formatMip);
|
||||
_texture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
|
||||
_texture->setImportant(true);
|
||||
}
|
||||
|
||||
void Font::setupGPU() {
|
||||
|
|
|
@ -85,11 +85,25 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
|
|||
|
||||
Lock lock(_containerLock);
|
||||
if (_scriptCache.contains(url) && !forceDownload) {
|
||||
auto scriptContent = _scriptCache[url];
|
||||
lock.unlock();
|
||||
qCDebug(scriptengine) << "Found script in cache:" << url.fileName();
|
||||
contentAvailable(url.toString(), scriptContent, true, true, STATUS_CACHED);
|
||||
} else {
|
||||
auto entry = _scriptCache[url];
|
||||
if (url.isLocalFile() || url.scheme().isEmpty()) {
|
||||
auto modifiedTime = QFileInfo(url.toLocalFile()).lastModified();
|
||||
QString localTime = ResourceRequest::toHttpDateString(modifiedTime.toMSecsSinceEpoch());
|
||||
QString cachedTime = entry["last-modified"].toString();
|
||||
if (cachedTime != localTime) {
|
||||
forceDownload = true;
|
||||
qCDebug(scriptengine) << "Found script in cache, but local file modified; reloading:" << url.fileName()
|
||||
<< "(memory:" << cachedTime << "disk:" << localTime << ")";
|
||||
}
|
||||
}
|
||||
if (!forceDownload) {
|
||||
lock.unlock();
|
||||
qCDebug(scriptengine) << "Found script in cache:" << url.fileName();
|
||||
contentAvailable(url.toString(), entry["data"].toString(), true, true, STATUS_CACHED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto& scriptRequest = _activeScriptRequests[url];
|
||||
bool alreadyWaiting = scriptRequest.scriptUsers.size() > 0;
|
||||
scriptRequest.scriptUsers.push_back(contentAvailable);
|
||||
|
@ -140,7 +154,10 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
|
|||
|
||||
_activeScriptRequests.remove(url);
|
||||
|
||||
_scriptCache[url] = scriptContent = req->getData();
|
||||
_scriptCache[url] = {
|
||||
{ "data", scriptContent = req->getData() },
|
||||
{ "last-modified", req->property("last-modified") },
|
||||
};
|
||||
} else {
|
||||
auto result = req->getResult();
|
||||
bool irrecoverable =
|
||||
|
@ -177,7 +194,7 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
|
|||
allCallbacks = scriptRequest.scriptUsers;
|
||||
|
||||
if (_scriptCache.contains(url)) {
|
||||
scriptContent = _scriptCache[url];
|
||||
scriptContent = _scriptCache[url]["data"].toString();
|
||||
}
|
||||
_activeScriptRequests.remove(url);
|
||||
qCWarning(scriptengine) << "Error loading script from URL (" << status <<")";
|
||||
|
|
|
@ -60,7 +60,7 @@ private:
|
|||
Mutex _containerLock;
|
||||
QMap<QUrl, ScriptRequest> _activeScriptRequests;
|
||||
|
||||
QHash<QUrl, QString> _scriptCache;
|
||||
QHash<QUrl, QVariantMap> _scriptCache;
|
||||
QMultiMap<QUrl, ScriptUser*> _scriptUsers;
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
|
|||
// This message was sent by one of our script engines, let's try to see if we can find the source.
|
||||
// Note that the first entry in the backtrace should be "print" and is somewhat useless to us
|
||||
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
|
||||
if (loggerInterface->showSourceDebugging()) {
|
||||
if (loggerInterface && loggerInterface->showSourceDebugging()) {
|
||||
QScriptContext* userContext = context;
|
||||
while (userContext && QScriptContextInfo(userContext).functionType() == QScriptContextInfo::NativeFunction) {
|
||||
userContext = userContext->parentContext();
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDateTime>
|
||||
|
@ -32,6 +36,40 @@ LogHandler& LogHandler::getInstance() {
|
|||
return staticInstance;
|
||||
}
|
||||
|
||||
LogHandler::LogHandler() {
|
||||
QString logOptions = qgetenv("VIRCADIA_LOG_OPTIONS").toLower();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// Enable color by default if we're on Unix, and output is a tty (so we're not being piped into something)
|
||||
//
|
||||
// On Windows the situation is more complex, and color is supported or not depending on version and
|
||||
// registry settings, so for now it's off by default and it's up to the user to do what's required.
|
||||
if (isatty(fileno(stdout))) {
|
||||
_useColor = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto optionList = logOptions.split(",");
|
||||
|
||||
for (auto option : optionList) {
|
||||
option = option.trimmed();
|
||||
|
||||
if (option == "color") {
|
||||
_useColor = true;
|
||||
} else if (option == "nocolor") {
|
||||
_useColor = false;
|
||||
} else if (option == "process_id") {
|
||||
_shouldOutputProcessID = true;
|
||||
} else if (option == "thread_id") {
|
||||
_shouldOutputThreadID = true;
|
||||
} else if (option == "milliseconds") {
|
||||
_shouldDisplayMilliseconds = true;
|
||||
} else if (option != "") {
|
||||
fprintf(stdout, "Unrecognized option in VIRCADIA_LOG_OPTIONS: '%s'\n", option.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* stringForLogType(LogMsgType msgType) {
|
||||
switch (msgType) {
|
||||
case LogInfo:
|
||||
|
@ -51,6 +89,29 @@ const char* stringForLogType(LogMsgType msgType) {
|
|||
}
|
||||
}
|
||||
|
||||
const char* colorForLogType(LogMsgType msgType) {
|
||||
switch (msgType) {
|
||||
case LogInfo:
|
||||
return "\u001b[37;1m"; // Bold white
|
||||
case LogDebug:
|
||||
return "";
|
||||
case LogWarning:
|
||||
return "\u001b[35;1m"; // Bright magenta
|
||||
case LogCritical:
|
||||
return "\u001b[31;1m"; // Bright red
|
||||
case LogFatal:
|
||||
return "\u001b[31;1m"; // Bright red
|
||||
case LogSuppressed:
|
||||
return "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char* colorReset() {
|
||||
return "\u001b[0m";
|
||||
}
|
||||
|
||||
// the following will produce 11/18 13:55:36
|
||||
const QString DATE_STRING_FORMAT = "MM/dd hh:mm:ss";
|
||||
|
||||
|
@ -133,7 +194,15 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
|
|||
|
||||
QString logMessage = QString("%1 %2\n").arg(prefixString, message.split('\n').join('\n' + prefixString + " "));
|
||||
|
||||
fprintf(stdout, "%s", qPrintable(logMessage));
|
||||
const char* color = "";
|
||||
const char* resetColor = "";
|
||||
|
||||
if (_useColor) {
|
||||
color = colorForLogType(type);
|
||||
resetColor = colorReset();
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s%s%s", color, qPrintable(logMessage), resetColor);
|
||||
#ifdef Q_OS_WIN
|
||||
// On windows, this will output log lines into the Visual Studio "output" tab
|
||||
OutputDebugStringA(qPrintable(logMessage));
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
void setupRepeatedMessageFlusher();
|
||||
|
||||
private:
|
||||
LogHandler() = default;
|
||||
LogHandler();
|
||||
~LogHandler() = default;
|
||||
|
||||
void flushRepeatedMessages();
|
||||
|
@ -66,6 +66,7 @@ private:
|
|||
bool _shouldOutputProcessID { false };
|
||||
bool _shouldOutputThreadID { false };
|
||||
bool _shouldDisplayMilliseconds { false };
|
||||
bool _useColor { false };
|
||||
|
||||
int _currentMessageID { 0 };
|
||||
struct RepeatedMessageRecord {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// interface/src
|
||||
//
|
||||
// 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
|
||||
|
@ -43,6 +44,8 @@ class Camera : public QObject {
|
|||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
||||
Q_PROPERTY(QString mode READ getModeString WRITE setModeString NOTIFY modeUpdated)
|
||||
Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT)
|
||||
Q_PROPERTY(bool captureMouse READ getCaptureMouse WRITE setCaptureMouse NOTIFY captureMouseChanged)
|
||||
Q_PROPERTY(float sensitivity READ getSensitivity WRITE setSensitivity)
|
||||
|
||||
public:
|
||||
Camera();
|
||||
|
@ -110,6 +113,37 @@ public slots:
|
|||
*/
|
||||
void setOrientation(const glm::quat& orientation);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the current mouse capture state.
|
||||
* @function Camera.getCaptureMouse
|
||||
* @returns {boolean} <code>true</code> if the mouse is captured (is invisible and cannot leave the bounds of Interface,
|
||||
* if Interface is the active window and no menu item is selected), <code>false</code> if the mouse is behaving normally.
|
||||
*/
|
||||
bool getCaptureMouse() const { return _captureMouse; }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the mouse capture state. When <code>true</code>, the mouse is invisible and cannot leave the bounds of
|
||||
* Interface, as long as Interface is the active window and no menu item is selected. When <code>false</code>, the mouse
|
||||
* behaves normally.
|
||||
* @function Camera.setCaptureMouse
|
||||
* @param {boolean} captureMouse - <code>true</code> to capture the mouse, <code>false</code> to release the mouse.
|
||||
*/
|
||||
void setCaptureMouse(bool captureMouse) { _captureMouse = captureMouse; emit captureMouseChanged(captureMouse); }
|
||||
|
||||
/**jsdoc
|
||||
* Gets the current camera sensitivity.
|
||||
* @function Camera.getSensitivity
|
||||
* @returns {number} The current camera sensitivity. Must be positive.
|
||||
*/
|
||||
float getSensitivity() const { return _sensitivity; }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the camera sensitivity. Higher values mean that the camera will be more sensitive to mouse movements.
|
||||
* @function Camera.setSensitivity
|
||||
* @param {number} sensitivity - The desired camera sensitivity. Must be positive.
|
||||
*/
|
||||
void setSensitivity(float sensitivity) { _sensitivity = glm::max(0.0f, sensitivity); }
|
||||
|
||||
/**jsdoc
|
||||
* Computes a {@link PickRay} based on the current camera configuration and the specified <code>x, y</code> position on the
|
||||
* screen. The {@link PickRay} can be used in functions such as {@link Entities.findRayIntersection} and
|
||||
|
@ -181,6 +215,20 @@ signals:
|
|||
*/
|
||||
void modeUpdated(const QString& newMode);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the camera mouse capture state changes.
|
||||
* @function Camera.captureMouseChanged
|
||||
* @param {boolean} newCaptureMouse - The new mouse capture state.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report mouse capture state changes.</caption>
|
||||
* function onCaptureMouseChanged(newCaptureMouse) {
|
||||
* print("The mouse capture has changed to " + newCaptureMouse);
|
||||
* }
|
||||
*
|
||||
* Camera.captureMouseChanged.connect(onCaptureMouseChanged);
|
||||
*/
|
||||
void captureMouseChanged(bool newCaptureMouse);
|
||||
|
||||
private:
|
||||
void recompose();
|
||||
void decompose();
|
||||
|
@ -194,6 +242,9 @@ private:
|
|||
glm::quat _orientation;
|
||||
bool _isKeepLookingAt{ false };
|
||||
glm::vec3 _lookingAt;
|
||||
|
||||
bool _captureMouse { false };
|
||||
float _sensitivity { 1.0f };
|
||||
};
|
||||
|
||||
#endif // hifi_Camera_h
|
||||
|
|
|
@ -4,5 +4,5 @@ link_hifi_libraries(shared networking qml gl audio audio-client plugins pointers
|
|||
include_hifi_library_headers(controllers)
|
||||
|
||||
# Required for some low level GL interaction in the OffscreenQMLSurface
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
target_opengl()
|
||||
|
|
|
@ -172,31 +172,31 @@ public:
|
|||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
|
||||
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
// Compatibility with QFileDialog::getOpenFileName
|
||||
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
// Compatibility with QFileDialog::getSaveFileName
|
||||
static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
// Compatibility with QFileDialog::getExistingDirectory
|
||||
static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant());
|
||||
Q_INVOKABLE ModalDialogListener* inputDialogAsync(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant());
|
||||
|
@ -209,12 +209,12 @@ public:
|
|||
// Compatibility with QInputDialog::getText
|
||||
static QString getText(void* ignored, const QString & title, const QString & label,
|
||||
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0,
|
||||
Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
Qt::WindowFlags flags = Qt::WindowFlags(), Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getText(OffscreenUi::ICON_NONE, title, label, text, ok);
|
||||
}
|
||||
// Compatibility with QInputDialog::getItem
|
||||
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = Qt::WindowFlags(),
|
||||
Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok);
|
||||
}
|
||||
|
@ -222,12 +222,12 @@ public:
|
|||
// Compatibility with QInputDialog::getText
|
||||
static ModalDialogListener* getTextAsync(void* ignored, const QString & title, const QString & label,
|
||||
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0,
|
||||
Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
Qt::WindowFlags flags = Qt::WindowFlags(), Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getTextAsync(OffscreenUi::ICON_NONE, title, label, text);
|
||||
}
|
||||
// Compatibility with QInputDialog::getItem
|
||||
static ModalDialogListener* getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = Qt::WindowFlags(),
|
||||
Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
|
||||
// Copyright 2019 Kasen IO
|
||||
//
|
||||
// Authored by: Humbletim (humbletim@gmail.com)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Supporting file containing all QtScript specific integration.
|
||||
// Supporting file containing all QtScript specific integration.
|
||||
|
||||
#ifndef EXAMPLE_SCRIPT_PLUGIN_H
|
||||
#define EXAMPLE_SCRIPT_PLUGIN_H
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
|
||||
// Copyright 2019 Kasen IO
|
||||
//
|
||||
// Authored by: Humbletim (humbletim@gmail.com)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Example of prototyping new JS APIs by leveraging the existing plugin system.
|
||||
// Example of prototyping new JS APIs by leveraging the existing plugin system.
|
||||
|
||||
#include "ExampleScriptPlugin.h"
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
lazy-validation>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field v-model="domainName" :rules="domainNameRules" label="Domain Display Name *" required hint="This is the name that shows on the listing" persistent-hint></v-text-field>
|
||||
<v-text-field v-model="domainName" :rules="domainNameRules" label="Location Name *" required hint="This is the name that shows on the listing" persistent-hint></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-text-field v-model="owner" :rules="ownerRules" label="Owner *" hint="Owner name to show in listing" required persistent-hint></v-text-field>
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
//
|
||||
// Created by Darlingnotin in 2019.
|
||||
// Copyright 2019 Darlingnotin
|
||||
//
|
||||
// App maintained in: https://github.com/kasenvr/Decentralized_GoTo_Experimental
|
||||
// App copied to: https://github.com/kasenvr/project-athena
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the ISC license.
|
||||
// See the accompanying file LICENSE or https://opensource.org/licenses/ISC
|
||||
|
|
|
@ -35,7 +35,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/audioMuteOverlay.js",
|
||||
"system/inspect.js",
|
||||
"system/keyboardShortcuts/keyboardShortcuts.js",
|
||||
"system/checkForUpdates.js"
|
||||
"system/checkForUpdates.js",
|
||||
"system/onFirstRun.js"
|
||||
];
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js",
|
||||
|
|
|
@ -16,7 +16,7 @@ var FRAME_RATE = 30; // 30 default
|
|||
|
||||
function getFrame(callback) {
|
||||
// A model exported from blender with a texture named 'Picture' on one face.
|
||||
var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx";
|
||||
var FRAME_URL = "https://cdn-1.vircadia.com/us-e-1/Developer/Tutorials/pictureFrame/finalFrame.fbx";
|
||||
|
||||
var model = ModelCache.prefetch(FRAME_URL);
|
||||
if (model.state === Resource.State.FINISHED) {
|
||||
|
|
34
scripts/system/create/audioFeedback/audioFeedback.js
Normal file
34
scripts/system/create/audioFeedback/audioFeedback.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// audioFeedback.js
|
||||
//
|
||||
// Created by Alezia Kurdis on September 30, 2020.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// This script add audio feedback (confirmation and rejection) for user interactions that require one.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
audioFeedback = (function() {
|
||||
var that = {};
|
||||
|
||||
var confirmationSound = SoundCache.getSound(Script.resolvePath("./sounds/confirmation.mp3"));
|
||||
var rejectionSound = SoundCache.getSound(Script.resolvePath("./sounds/rejection.mp3"));
|
||||
|
||||
that.confirmation = function() { //Play a confirmation sound
|
||||
var injector = Audio.playSound(confirmationSound, {
|
||||
"volume": 0.3,
|
||||
"localOnly": true
|
||||
});
|
||||
}
|
||||
|
||||
that.rejection = function() { //Play a rejection sound
|
||||
var injector = Audio.playSound(rejectionSound, {
|
||||
"volume": 0.3,
|
||||
"localOnly": true
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
})();
|
BIN
scripts/system/create/audioFeedback/sounds/confirmation.mp3
Normal file
BIN
scripts/system/create/audioFeedback/sounds/confirmation.mp3
Normal file
Binary file not shown.
BIN
scripts/system/create/audioFeedback/sounds/rejection.mp3
Normal file
BIN
scripts/system/create/audioFeedback/sounds/rejection.mp3
Normal file
Binary file not shown.
|
@ -34,7 +34,8 @@ Script.include([
|
|||
"../libraries/entityIconOverlayManager.js",
|
||||
"../libraries/gridTool.js",
|
||||
"entityList/entityList.js",
|
||||
"entitySelectionTool/entitySelectionTool.js"
|
||||
"entitySelectionTool/entitySelectionTool.js",
|
||||
"audioFeedback/audioFeedback.js"
|
||||
]);
|
||||
|
||||
var CreateWindow = Script.require('./modules/createWindow.js');
|
||||
|
@ -104,6 +105,8 @@ var entityIconOverlayManager = new EntityIconOverlayManager(['Light', 'ParticleE
|
|||
}
|
||||
});
|
||||
|
||||
var hmdMultiSelectMode = false;
|
||||
|
||||
var cameraManager = new CameraManager();
|
||||
|
||||
var grid = new Grid();
|
||||
|
@ -824,7 +827,7 @@ var toolBar = (function () {
|
|||
|
||||
HMD.displayModeChanged.connect(function() {
|
||||
if (isActive) {
|
||||
tablet.gotoHomeScreen();
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
that.setActive(false);
|
||||
});
|
||||
|
@ -1131,7 +1134,11 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) {
|
|||
var entity = entityIconOverlayManager.findEntity(data.overlayID);
|
||||
|
||||
if (entity !== null) {
|
||||
selectionManager.setSelections([entity], this);
|
||||
if (hmdMultiSelectMode) {
|
||||
selectionManager.addEntity(entity, true, this);
|
||||
} else {
|
||||
selectionManager.setSelections([entity], this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1689,11 +1696,12 @@ function recursiveDelete(entities, childrenList, deletedIDs, entityHostType) {
|
|||
}
|
||||
|
||||
function unparentSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
|
||||
var selectedEntities = selectionManager.selections;
|
||||
var parentCheck = false;
|
||||
|
||||
if (selectedEntities.length < 1) {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You must have an entity selected in order to unparent it.");
|
||||
return;
|
||||
}
|
||||
|
@ -1706,12 +1714,17 @@ function unparentSelectedEntities() {
|
|||
return true;
|
||||
});
|
||||
if (parentCheck) {
|
||||
audioFeedback.confirmation();
|
||||
if (selectedEntities.length > 1) {
|
||||
Window.notify("Entities unparented");
|
||||
} else {
|
||||
Window.notify("Entity unparented");
|
||||
}
|
||||
//Refresh
|
||||
entityListTool.sendUpdate();
|
||||
selectionManager._update(false, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
if (selectedEntities.length > 1) {
|
||||
Window.notify("Selected Entities have no parents");
|
||||
} else {
|
||||
|
@ -1719,13 +1732,15 @@ function unparentSelectedEntities() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
Window.notifyEditError("You have nothing selected to unparent");
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
}
|
||||
function parentSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
|
||||
var selectedEntities = selectionManager.selections;
|
||||
if (selectedEntities.length <= 1) {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You must have multiple entities selected in order to parent them");
|
||||
return;
|
||||
}
|
||||
|
@ -1742,16 +1757,22 @@ function parentSelectedEntities() {
|
|||
});
|
||||
|
||||
if (parentCheck) {
|
||||
audioFeedback.confirmation();
|
||||
Window.notify("Entities parented");
|
||||
//Refresh
|
||||
entityListTool.sendUpdate();
|
||||
selectionManager._update(false, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notify("Entities are already parented to last");
|
||||
}
|
||||
} else {
|
||||
Window.notifyEditError("You have nothing selected to parent");
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
}
|
||||
function deleteSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
|
||||
var deletedIDs = [];
|
||||
|
||||
SelectionManager.saveProperties();
|
||||
|
@ -1782,6 +1803,9 @@ function deleteSelectedEntities() {
|
|||
pushCommandForSelections([], savedProperties);
|
||||
entityListTool.deleteEntities(deletedIDs);
|
||||
}
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2339,6 +2363,15 @@ var PropertiesTool = function (opts) {
|
|||
};
|
||||
|
||||
function updateSelections(selectionUpdated, caller) {
|
||||
if (HMD.active && visible) {
|
||||
webView.setLandscape(true);
|
||||
} else {
|
||||
if (!visible) {
|
||||
hmdMultiSelectMode = false;
|
||||
webView.setLandscape(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (blockPropertyUpdates) {
|
||||
return;
|
||||
}
|
||||
|
@ -2939,4 +2972,22 @@ function zoneSortOrder(a, b) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
function getParentState(id) {
|
||||
var state = "NONE";
|
||||
var properties = Entities.getEntityProperties(id, ["parentID"]);
|
||||
var children = Entities.getChildrenIDs(id);
|
||||
if (properties.parentID !== Uuid.NULL) {
|
||||
if (children.length > 0) {
|
||||
state = "PARENT_CHILDREN";
|
||||
} else {
|
||||
state = "CHILDREN";
|
||||
}
|
||||
} else {
|
||||
if (children.length > 0) {
|
||||
state = "PARENT";
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// entityList.js
|
||||
//
|
||||
// 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
|
||||
|
@ -14,6 +15,7 @@
|
|||
|
||||
var PROFILING_ENABLED = false;
|
||||
var profileIndent = '';
|
||||
|
||||
const PROFILE_NOOP = function(_name, fn, args) {
|
||||
fn.apply(this, args);
|
||||
};
|
||||
|
@ -73,7 +75,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
that.setVisible = function(newVisible) {
|
||||
visible = newVisible;
|
||||
webView.setVisible(shouldUseEditTabletApp() && visible);
|
||||
entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
|
||||
entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
|
||||
};
|
||||
|
||||
that.isVisible = function() {
|
||||
|
@ -163,6 +165,15 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
}
|
||||
|
||||
that.sendUpdate = function() {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
if (HMD.active) {
|
||||
tablet.setLandscape(true);
|
||||
}
|
||||
emitJSONScriptEvent({
|
||||
"type": "confirmHMDstate",
|
||||
"isHmd": HMD.active
|
||||
});
|
||||
|
||||
PROFILE('Script-sendUpdate', function() {
|
||||
var entities = [];
|
||||
|
||||
|
@ -179,7 +190,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
PROFILE("getMultipleProperties", function () {
|
||||
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked',
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID',
|
||||
'skybox.url', 'ambientLight.url']);
|
||||
'skybox.url', 'ambientLight.url', 'created', 'lastEdited']);
|
||||
for (var i = 0; i < multipleProperties.length; i++) {
|
||||
var properties = multipleProperties[i];
|
||||
|
||||
|
@ -192,6 +203,17 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
} else if (properties.type === "Image") {
|
||||
url = properties.imageURL;
|
||||
}
|
||||
|
||||
var parentStatus = getParentState(ids[i]);
|
||||
var parentState = "";
|
||||
if (parentStatus === "PARENT") {
|
||||
parentState = "A";
|
||||
} else if (parentStatus === "CHILDREN") {
|
||||
parentState = "C";
|
||||
} else if (parentStatus === "PARENT_CHILDREN") {
|
||||
parentState = "B";
|
||||
}
|
||||
|
||||
entities.push({
|
||||
id: ids[i],
|
||||
name: properties.name,
|
||||
|
@ -211,7 +233,10 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
isBaked: entityIsBaked(properties),
|
||||
drawCalls: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.drawCalls) : ""),
|
||||
hasScript: properties.script !== ""
|
||||
hasScript: properties.script !== "",
|
||||
parentState: parentState,
|
||||
created: formatToStringDateTime(properties.created),
|
||||
lastEdited: formatToStringDateTime(properties.lastEdited)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -231,6 +256,22 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
});
|
||||
};
|
||||
|
||||
function formatToStringDateTime(timestamp) {
|
||||
var d = new Date(Math.floor(timestamp/1000));
|
||||
var dateTime = d.getUTCFullYear() + "-" + zeroPad((d.getUTCMonth() + 1), 2) + "-" + zeroPad(d.getUTCDate(), 2);
|
||||
dateTime = dateTime + " " + zeroPad(d.getUTCHours(), 2) + ":" + zeroPad(d.getUTCMinutes(), 2) + ":" + zeroPad(d.getUTCSeconds(), 2);
|
||||
dateTime = dateTime + "." + zeroPad(d.getUTCMilliseconds(), 3);
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
function zeroPad(num, size) {
|
||||
num = num.toString();
|
||||
while (num.length < size) {
|
||||
num = "0" + num;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
function onFileSaveChanged(filename) {
|
||||
Window.saveFileChanged.disconnect(onFileSaveChanged);
|
||||
if (filename !== "") {
|
||||
|
@ -302,6 +343,34 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
SelectionDisplay.toggleSpaceMode();
|
||||
} else if (data.type === 'keyUpEvent') {
|
||||
keyUpEventFromUIWindow(data.keyUpEvent);
|
||||
} else if (data.type === 'undo') {
|
||||
undoHistory.undo();
|
||||
} else if (data.type === 'redo') {
|
||||
undoHistory.redo();
|
||||
} else if (data.type === 'parent') {
|
||||
parentSelectedEntities();
|
||||
} else if (data.type === 'unparent') {
|
||||
unparentSelectedEntities();
|
||||
} else if (data.type === 'hmdMultiSelectMode') {
|
||||
hmdMultiSelectMode = data.value;
|
||||
} else if (data.type === 'selectAllInBox') {
|
||||
selectAllEntitiesInCurrentSelectionBox(false);
|
||||
} else if (data.type === 'selectAllTouchingBox') {
|
||||
selectAllEntitiesInCurrentSelectionBox(true);
|
||||
} else if (data.type === 'selectParent') {
|
||||
SelectionManager.selectParent();
|
||||
} else if (data.type === 'selectTopParent') {
|
||||
SelectionManager.selectTopParent();
|
||||
} else if (data.type === 'addChildrenToSelection') {
|
||||
SelectionManager.addChildrenToSelection();
|
||||
} else if (data.type === 'selectFamily') {
|
||||
SelectionManager.selectFamily();
|
||||
} else if (data.type === 'selectTopFamily') {
|
||||
SelectionManager.selectTopFamily();
|
||||
} else if (data.type === 'teleportToEntity') {
|
||||
SelectionManager.teleportToEntity();
|
||||
} else if (data.type === 'moveEntitySelectionToAvatar') {
|
||||
SelectionManager.moveEntitiesSelectionToAvatar();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 19 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
|
||||
|
@ -30,7 +31,9 @@
|
|||
<input type="button" id="visible" class="glyph" value="" />
|
||||
</div>
|
||||
<button id="toggle-space-mode" class="hifi-edit-button space-mode-local">Local</button>
|
||||
<input type="button" class="red glyph" id="delete" value="{" />
|
||||
<input type="button" class="vglyph" id="hmdmultiselect" value="I" style="display: none;" />
|
||||
<input type="button" class="normal" id="selection" value="Selection..." />
|
||||
<input type="button" class="normal" id="actions" value="Actions..." />
|
||||
</div>
|
||||
<div id="entity-list">
|
||||
<div id="filter-area">
|
||||
|
@ -88,5 +91,142 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entity-list-menu" id="actions-menu" >
|
||||
<button class="menu-button" id="undo" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Undo</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-Z</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="redo" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Redo</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-Y</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="hmdcut" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Cut</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-X</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="hmdcopy" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Copy</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-C</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="hmdpaste" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Paste</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-V</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="hmdduplicate" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Duplicate</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-D</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="delete" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Delete</div>
|
||||
<div class = "menu-item-shortcut">Del</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="parent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Parent Entities to the Last Selected</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-P</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="unparent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Unparent Entity</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-Shift-P</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="moveEntitySelectionToAvatar" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Move Selected Entities to Avatar</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="entity-list-menu" id="selection-menu" >
|
||||
<button class="menu-button" id="selectall" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select All</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-A</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectnone" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select None</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectinverse" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Inverse Selection</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-I</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="selectallinbox" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select All Entities In Box</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectalltouchingbox" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select All Entities Touching Box</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="selectparent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Parent</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selecttopparent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Top Parent</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="addchildrentoselection" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Add Children To Selection</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectfamily" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Family</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selecttopfamily" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Top Family</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="teleport-to-entity" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Teleport To Selected Entities</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div id="menuBackgroundOverlay" ></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 19 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
|
||||
|
@ -46,6 +47,14 @@ const COLUMNS = {
|
|||
alwaysShown: true,
|
||||
defaultSortOrder: ASCENDING_SORT,
|
||||
},
|
||||
parentState: {
|
||||
columnHeader: "A",
|
||||
vglyph: true,
|
||||
dropdownLabel: "Hierarchy",
|
||||
propertyID: "parentState",
|
||||
initialWidth: 0.08,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
name: {
|
||||
columnHeader: "Name",
|
||||
propertyID: "name",
|
||||
|
@ -133,6 +142,20 @@ const COLUMNS = {
|
|||
initialWidth: 0.06,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
created: {
|
||||
columnHeader: "Created (UTC)",
|
||||
dropdownLabel: "Creation Date",
|
||||
propertyID: "created",
|
||||
initialWidth: 0.38,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
lastEdited: {
|
||||
columnHeader: "Modified (UTC)",
|
||||
dropdownLabel: "Modification Date",
|
||||
propertyID: "lastEdited",
|
||||
initialWidth: 0.38,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
};
|
||||
|
||||
const FILTER_TYPES = [
|
||||
|
@ -164,6 +187,7 @@ let selectedEntities = [];
|
|||
|
||||
let entityList = null; // The ListView
|
||||
|
||||
let hmdMultiSelectMode = false;
|
||||
/**
|
||||
* @type EntityListContextMenu
|
||||
*/
|
||||
|
@ -198,7 +222,31 @@ let elEntityTable,
|
|||
elRefresh,
|
||||
elToggleLocked,
|
||||
elToggleVisible,
|
||||
elActionsMenu,
|
||||
elSelectionMenu,
|
||||
elMenuBackgroundOverlay,
|
||||
elHmdMultiSelect,
|
||||
elHmdCopy,
|
||||
elHmdCut,
|
||||
elHmdPaste,
|
||||
elHmdDuplicate,
|
||||
elUndo,
|
||||
elRedo,
|
||||
elParent,
|
||||
elUnparent,
|
||||
elDelete,
|
||||
elMoveEntitySelectionToAvatar,
|
||||
elSelectAll,
|
||||
elSelectInverse,
|
||||
elSelectNone,
|
||||
elSelectAllInBox,
|
||||
elSelectAllTouchingBox,
|
||||
elSelectParent,
|
||||
elSelectTopParent,
|
||||
elAddChildrenToSelection,
|
||||
elSelectFamily,
|
||||
elSelectTopFamily,
|
||||
elTeleportToEntity,
|
||||
elFilterTypeMultiselectBox,
|
||||
elFilterTypeText,
|
||||
elFilterTypeOptions,
|
||||
|
@ -233,7 +281,7 @@ const PROFILE = !ENABLE_PROFILING ? PROFILE_NOOP : function(name, fn, args) {
|
|||
console.log("PROFILE-Web " + profileIndent + "(" + name + ") End " + delta + "ms");
|
||||
};
|
||||
|
||||
function loaded() {
|
||||
function loaded() {
|
||||
openEventBridge(function() {
|
||||
elEntityTable = document.getElementById("entity-table");
|
||||
elEntityTableHeader = document.getElementById("entity-table-header");
|
||||
|
@ -241,8 +289,32 @@ function loaded() {
|
|||
elEntityTableScroll = document.getElementById("entity-table-scroll");
|
||||
elRefresh = document.getElementById("refresh");
|
||||
elToggleLocked = document.getElementById("locked");
|
||||
elToggleVisible = document.getElementById("visible");
|
||||
elToggleVisible = document.getElementById("visible");
|
||||
elHmdMultiSelect = document.getElementById("hmdmultiselect");
|
||||
elActionsMenu = document.getElementById("actions");
|
||||
elSelectionMenu = document.getElementById("selection");
|
||||
elMenuBackgroundOverlay = document.getElementById("menuBackgroundOverlay");
|
||||
elHmdCopy = document.getElementById("hmdcopy");
|
||||
elHmdCut = document.getElementById("hmdcut");
|
||||
elHmdPaste = document.getElementById("hmdpaste");
|
||||
elHmdDuplicate = document.getElementById("hmdduplicate");
|
||||
elUndo = document.getElementById("undo");
|
||||
elRedo = document.getElementById("redo");
|
||||
elParent = document.getElementById("parent");
|
||||
elUnparent = document.getElementById("unparent");
|
||||
elDelete = document.getElementById("delete");
|
||||
elMoveEntitySelectionToAvatar = document.getElementById("moveEntitySelectionToAvatar");
|
||||
elSelectAll = document.getElementById("selectall");
|
||||
elSelectInverse = document.getElementById("selectinverse");
|
||||
elSelectNone = document.getElementById("selectnone");
|
||||
elSelectAllInBox = document.getElementById("selectallinbox");
|
||||
elSelectAllTouchingBox = document.getElementById("selectalltouchingbox");
|
||||
elSelectParent = document.getElementById("selectparent");
|
||||
elSelectTopParent = document.getElementById("selecttopparent");
|
||||
elAddChildrenToSelection = document.getElementById("addchildrentoselection");
|
||||
elSelectFamily = document.getElementById("selectfamily");
|
||||
elSelectTopFamily = document.getElementById("selecttopfamily");
|
||||
elTeleportToEntity = document.getElementById("teleport-to-entity");
|
||||
elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box");
|
||||
elFilterTypeText = document.getElementById("filter-type-text");
|
||||
elFilterTypeOptions = document.getElementById("filter-type-options");
|
||||
|
@ -270,8 +342,156 @@ function loaded() {
|
|||
elExport.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'export'}));
|
||||
};
|
||||
elHmdMultiSelect.onclick = function() {
|
||||
if (hmdMultiSelectMode) {
|
||||
elHmdMultiSelect.className = "vglyph";
|
||||
hmdMultiSelectMode = false;
|
||||
} else {
|
||||
elHmdMultiSelect.className = "white vglyph";
|
||||
hmdMultiSelectMode = true;
|
||||
}
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'hmdMultiSelectMode', value: hmdMultiSelectMode }));
|
||||
};
|
||||
elActionsMenu.onclick = function() {
|
||||
document.getElementById("menuBackgroundOverlay").style.display = "block";
|
||||
document.getElementById("actions-menu").style.display = "block";
|
||||
};
|
||||
elSelectionMenu.onclick = function() {
|
||||
document.getElementById("menuBackgroundOverlay").style.display = "block";
|
||||
document.getElementById("selection-menu").style.display = "block";
|
||||
};
|
||||
elMenuBackgroundOverlay.onclick = function() {
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdCopy.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'copy' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdCut.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'cut' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdPaste.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'paste' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdDuplicate.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'duplicate' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elParent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elUnparent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elUndo.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'undo' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elRedo.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'redo' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elDelete.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elMoveEntitySelectionToAvatar.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'moveEntitySelectionToAvatar' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectAll.onclick = function() {
|
||||
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
selection = visibleEntityIDs;
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection, false);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": selection
|
||||
}));
|
||||
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectInverse.onclick = function() {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
visibleEntityIDs.forEach(function(id) {
|
||||
if (!selectedEntities.includes(id)) {
|
||||
selection.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection, false);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": selection
|
||||
}));
|
||||
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectNone.onclick = function() {
|
||||
updateSelectedEntities([], false);
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": []
|
||||
}));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectAllInBox.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectAllInBox' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectAllTouchingBox.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectAllTouchingBox' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectParent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectParent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectTopParent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectTopParent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elAddChildrenToSelection.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'addChildrenToSelection' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectFamily.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectFamily' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectTopFamily.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectTopFamily' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elTeleportToEntity.onclick = function () {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: "teleportToEntity" }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elToggleSpaceMode.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleSpaceMode' }));
|
||||
|
@ -330,9 +550,12 @@ function loaded() {
|
|||
elTh.setAttribute("id", thID);
|
||||
elTh.setAttribute("columnIndex", columnIndex);
|
||||
elTh.setAttribute("columnID", columnID);
|
||||
if (columnData.glyph) {
|
||||
if (columnData.glyph || columnData.vglyph) {
|
||||
let elGlyph = document.createElement("span");
|
||||
elGlyph.className = "glyph";
|
||||
if (columnData.vglyph) {
|
||||
elGlyph.className = "vglyph";
|
||||
}
|
||||
elGlyph.innerHTML = columnData.columnHeader;
|
||||
elTh.appendChild(elGlyph);
|
||||
} else {
|
||||
|
@ -538,7 +761,7 @@ function loaded() {
|
|||
let selection = [entityID];
|
||||
let controlKey = window.navigator.platform.startsWith("Mac") ? clickEvent.metaKey : clickEvent.ctrlKey;
|
||||
|
||||
if (controlKey) {
|
||||
if (controlKey || hmdMultiSelectMode) {
|
||||
let selectedIndex = selectedEntities.indexOf(entityID);
|
||||
if (selectedIndex >= 0) {
|
||||
selection = [];
|
||||
|
@ -632,10 +855,13 @@ function loaded() {
|
|||
isBaked: entity.isBaked,
|
||||
drawCalls: displayIfNonZero(entity.drawCalls),
|
||||
hasScript: entity.hasScript,
|
||||
parentState: entity.parentState,
|
||||
created: entity.created,
|
||||
lastEdited: entity.lastEdited,
|
||||
elRow: null, // if this entity has a visible row element assigned to it
|
||||
selected: false // if this entity is selected for edit regardless of having a visible row
|
||||
};
|
||||
|
||||
|
||||
entities.push(entityData);
|
||||
entitiesByID[entityData.id] = entityData;
|
||||
});
|
||||
|
@ -817,7 +1043,7 @@ function loaded() {
|
|||
|
||||
function updateSelectedEntities(selectedIDs, autoScroll) {
|
||||
let notFound = false;
|
||||
|
||||
|
||||
// reset all currently selected entities and their rows first
|
||||
selectedEntities.forEach(function(id) {
|
||||
let entity = entitiesByID[id];
|
||||
|
@ -828,7 +1054,7 @@ function loaded() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// then reset selected entities list with newly selected entities and set them selected
|
||||
selectedEntities = [];
|
||||
selectedIDs.forEach(function(id) {
|
||||
|
@ -891,6 +1117,8 @@ function loaded() {
|
|||
let elCell = elRow.childNodes[i];
|
||||
if (column.data.glyph) {
|
||||
elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null;
|
||||
} else if (column.data.vglyph) {
|
||||
elCell.innerHTML = itemData[column.data.propertyID];
|
||||
} else {
|
||||
let value = itemData[column.data.propertyID];
|
||||
if (column.data.format) {
|
||||
|
@ -978,6 +1206,9 @@ function loaded() {
|
|||
let column = columnsByID[columnID];
|
||||
let visible = column.elTh.style.visibility !== "hidden";
|
||||
let className = column.data.glyph ? "glyph" : "";
|
||||
if (column.data.vglyph) {
|
||||
className = "vglyph";
|
||||
}
|
||||
className += visible ? "" : " hidden";
|
||||
return className;
|
||||
}
|
||||
|
@ -1327,7 +1558,7 @@ function loaded() {
|
|||
break;
|
||||
}
|
||||
|
||||
if (controlKey && keyCodeString === "A") {
|
||||
if (controlKey && !shiftKey && !altKey && keyCodeString === "A") {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
|
@ -1350,6 +1581,32 @@ function loaded() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (controlKey && !shiftKey && !altKey && keyCodeString === "I") {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
visibleEntityIDs.forEach(function(id) {
|
||||
if (!selectedEntities.includes(id)) {
|
||||
selection.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": selection
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: 'keyUpEvent',
|
||||
|
@ -1364,10 +1621,12 @@ function loaded() {
|
|||
}
|
||||
}));
|
||||
}, false);
|
||||
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.type === "clearEntityList") {
|
||||
clearEntities();
|
||||
} else if (data.type === "selectionUpdate") {
|
||||
|
@ -1395,6 +1654,12 @@ function loaded() {
|
|||
removeEntities(data.ids);
|
||||
} else if (data.type === "setSpaceMode") {
|
||||
setSpaceMode(data.spaceMode);
|
||||
} else if (data.type === "confirmHMDstate") {
|
||||
if (data.isHmd) {
|
||||
document.getElementById("hmdmultiselect").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("hmdmultiselect").style.display = "none";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1419,4 +1684,11 @@ function loaded() {
|
|||
$(window).blur(function() {
|
||||
entityListContextMenu.close();
|
||||
});
|
||||
|
||||
function closeAllEntityListMenu() {
|
||||
document.getElementById("menuBackgroundOverlay").style.display = "none";
|
||||
document.getElementById("selection-menu").style.display = "none";
|
||||
document.getElementById("actions-menu").style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1618,7 +1618,8 @@ const GROUPS = [
|
|||
type: "vec3",
|
||||
vec3Type: "pyr",
|
||||
multiplier: DEGREES_TO_RADIANS,
|
||||
decimals: 4,
|
||||
decimals: 6,
|
||||
step: 1,
|
||||
subLabels: [ "x", "y", "z" ],
|
||||
unit: "deg/s",
|
||||
propertyID: "localAngularVelocity",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
const SPACE_LOCAL = "local";
|
||||
const SPACE_WORLD = "world";
|
||||
const HIGHLIGHT_LIST_NAME = "editHandleHighlightList";
|
||||
const MIN_DISTANCE_TO_REZ_FROM_AVATAR = 3;
|
||||
|
||||
Script.include([
|
||||
"../../libraries/controllers.js",
|
||||
|
@ -26,7 +27,6 @@ Script.include([
|
|||
"../../libraries/utils.js"
|
||||
]);
|
||||
|
||||
|
||||
function deepCopy(v) {
|
||||
return JSON.parse(JSON.stringify(v));
|
||||
}
|
||||
|
@ -103,7 +103,11 @@ SelectionManager = (function() {
|
|||
if (wantDebug) {
|
||||
print("setting selection to " + messageParsed.entityID);
|
||||
}
|
||||
that.setSelections([messageParsed.entityID], that);
|
||||
if (hmdMultiSelectMode) {
|
||||
that.addEntity(messageParsed.entityID, true, that);
|
||||
} else {
|
||||
that.setSelections([messageParsed.entityID], that);
|
||||
}
|
||||
}
|
||||
} else if (messageParsed.method === "clearSelection") {
|
||||
if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) {
|
||||
|
@ -314,6 +318,7 @@ SelectionManager = (function() {
|
|||
that.addChildrenEntities(originalEntityID, entitiesToDuplicate, entityHostTypes[i].entityHostType);
|
||||
}
|
||||
|
||||
var duplicateInterrupted = false;
|
||||
// duplicate entities from above and store their original to new entity mappings and children needing re-parenting
|
||||
for (var i = 0; i < entitiesToDuplicate.length; i++) {
|
||||
var originalEntityID = entitiesToDuplicate[i];
|
||||
|
@ -360,6 +365,8 @@ SelectionManager = (function() {
|
|||
duplicatedChildrenWithOldParents[newEntityID] = properties.parentID;
|
||||
}
|
||||
originalEntityToNewEntityID[originalEntityID] = newEntityID;
|
||||
} else {
|
||||
duplicateInterrupted = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,6 +385,11 @@ SelectionManager = (function() {
|
|||
}
|
||||
});
|
||||
|
||||
if (duplicateInterrupted) {
|
||||
audioFeedback.rejection();
|
||||
} else {
|
||||
audioFeedback.confirmation();
|
||||
}
|
||||
return duplicatedEntityIDs;
|
||||
};
|
||||
|
||||
|
@ -624,6 +636,141 @@ SelectionManager = (function() {
|
|||
}
|
||||
};
|
||||
|
||||
that.teleportToEntity = function() {
|
||||
if (that.hasSelection()) {
|
||||
var distanceFromTarget = MIN_DISTANCE_TO_REZ_FROM_AVATAR + Math.max(Math.max(that.worldDimensions.x, that.worldDimensions.y), that.worldDimensions.z);
|
||||
var teleportTargetPosition = Vec3.sum(that.worldPosition, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: distanceFromTarget }));
|
||||
MyAvatar.goToLocation(teleportTargetPosition, false);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
that.moveEntitiesSelectionToAvatar = function() {
|
||||
if (that.hasSelection() && that.hasUnlockedSelection()) {
|
||||
that.saveProperties();
|
||||
var distanceFromTarget = MIN_DISTANCE_TO_REZ_FROM_AVATAR + Math.max(Math.max(that.worldDimensions.x, that.worldDimensions.y), that.worldDimensions.z);
|
||||
var targetPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -distanceFromTarget }));
|
||||
// editing a parent will cause all the children to automatically follow along, so don't
|
||||
// edit any entity who has an ancestor in that.selections
|
||||
var toMove = that.selections.filter(function (selection) {
|
||||
if (that.selections.indexOf(that.savedProperties[selection].parentID) >= 0) {
|
||||
return false; // a parent is also being moved, so don't issue an edit for this entity
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
for (var i = 0; i < toMove.length; i++) {
|
||||
var id = toMove[i];
|
||||
var properties = that.savedProperties[id];
|
||||
var relativePosition = Vec3.subtract(properties.position, that.worldPosition);
|
||||
var newPosition = Vec3.sum(relativePosition, targetPosition);
|
||||
Entities.editEntity(id, { "position": newPosition });
|
||||
}
|
||||
that._update(false, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
};
|
||||
|
||||
that.selectParent = function() {
|
||||
if (that.hasSelection()) {
|
||||
var currentSelection = that.selections;
|
||||
that.selections = [];
|
||||
for (var i = 0; i < currentSelection.length; i++) {
|
||||
var properties = Entities.getEntityProperties(currentSelection[i], ['parentID']);
|
||||
if (properties.parentID !== Uuid.NULL) {
|
||||
that.selections.push(properties.parentID);
|
||||
}
|
||||
}
|
||||
that._update(true, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
that.selectTopParent = function() {
|
||||
if (that.hasSelection()) {
|
||||
var currentSelection = that.selections;
|
||||
that.selections = [];
|
||||
for (var i = 0; i < currentSelection.length; i++) {
|
||||
var topParentId = getTopParent(currentSelection[i]);
|
||||
if (topParentId !== Uuid.NULL) {
|
||||
that.selections.push(topParentId);
|
||||
}
|
||||
}
|
||||
that._update(true, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
function getTopParent(id) {
|
||||
var topParentId = Uuid.NULL;
|
||||
var properties = Entities.getEntityProperties(id, ['parentID']);
|
||||
if (properties.parentID === Uuid.NULL) {
|
||||
topParentId = id;
|
||||
} else {
|
||||
topParentId = getTopParent(properties.parentID);
|
||||
}
|
||||
return topParentId;
|
||||
}
|
||||
|
||||
that.addChildrenToSelection = function() {
|
||||
if (that.hasSelection()) {
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
var childrenIDs = Entities.getChildrenIDs(that.selections[i]);
|
||||
var collectNewChildren;
|
||||
var j;
|
||||
var k = 0;
|
||||
do {
|
||||
collectNewChildren = Entities.getChildrenIDs(childrenIDs[k]);
|
||||
if (collectNewChildren.length > 0) {
|
||||
for (j = 0; j < collectNewChildren.length; j++) {
|
||||
childrenIDs.push(collectNewChildren[j]);
|
||||
}
|
||||
}
|
||||
k++;
|
||||
} while (k < childrenIDs.length);
|
||||
if (childrenIDs.length > 0) {
|
||||
for (j = 0; j < childrenIDs.length; j++) {
|
||||
that.selections.push(childrenIDs[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
that._update(true, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
that.hasUnlockedSelection = function() {
|
||||
var selectionNotLocked = true;
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
var properties = Entities.getEntityProperties(that.selections[i], ['locked']);
|
||||
if (properties.locked) {
|
||||
selectionNotLocked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selectionNotLocked;
|
||||
};
|
||||
|
||||
that.selectFamily = function() {
|
||||
that.selectParent();
|
||||
that.addChildrenToSelection();
|
||||
};
|
||||
|
||||
that.selectTopFamily = function() {
|
||||
that.selectTopParent();
|
||||
that.addChildrenToSelection();
|
||||
};
|
||||
|
||||
return that;
|
||||
})();
|
||||
|
||||
|
@ -648,8 +795,10 @@ SelectionDisplay = (function() {
|
|||
const COLOR_HOVER = { red: 255, green: 220, blue: 82 };
|
||||
const COLOR_DUPLICATOR = { red: 162, green: 0, blue: 255 };
|
||||
const COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 };
|
||||
const COLOR_BOUNDING_EDGE = { red: 128, green: 128, blue: 128 };
|
||||
const COLOR_SCALE_CUBE = { red: 160, green: 160, blue: 160 };
|
||||
const COLOR_BOUNDING_EDGE = { red: 160, green: 160, blue: 160 };
|
||||
const COLOR_BOUNDING_EDGE_PARENT = { red: 194, green: 123, blue: 0 };
|
||||
const COLOR_BOUNDING_EDGE_CHILDREN = { red: 0, green: 168, blue: 214 };
|
||||
const COLOR_SCALE_CUBE = { red: 192, green: 192, blue: 192 };
|
||||
const COLOR_DEBUG_PICK_PLANE = { red: 255, green: 255, blue: 255 };
|
||||
const COLOR_DEBUG_PICK_PLANE_HIT = { red: 255, green: 165, blue: 0 };
|
||||
|
||||
|
@ -1779,6 +1928,18 @@ SelectionDisplay = (function() {
|
|||
var rotationZ = Quat.multiply(rotation, localRotationZ);
|
||||
worldRotationZ = rotationZ;
|
||||
|
||||
var handleBoundingBoxColor = COLOR_BOUNDING_EDGE;
|
||||
if (SelectionManager.selections.length === 1) {
|
||||
var parentState = getParentState(SelectionManager.selections[0]);
|
||||
if (parentState === "CHILDREN") {
|
||||
handleBoundingBoxColor = COLOR_BOUNDING_EDGE_CHILDREN;
|
||||
} else {
|
||||
if (parentState === "PARENT" || parentState === "PARENT_CHILDREN") {
|
||||
handleBoundingBoxColor = COLOR_BOUNDING_EDGE_PARENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var selectionBoxGeometry = {
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
|
@ -1900,6 +2061,7 @@ SelectionDisplay = (function() {
|
|||
Entities.editEntity(handleBoundingBox, {
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
color: handleBoundingBoxColor,
|
||||
dimensions: dimensions
|
||||
});
|
||||
|
||||
|
|
|
@ -55,18 +55,18 @@ TabBar {
|
|||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 28
|
||||
anchors.topMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 28
|
||||
anchors.leftMargin: 30
|
||||
}
|
||||
|
||||
Flow {
|
||||
id: createEntitiesFlow
|
||||
spacing: 35
|
||||
spacing: 20
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 55
|
||||
anchors.rightMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 55
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 70
|
||||
|
||||
|
@ -186,9 +186,9 @@ TabBar {
|
|||
color: hifi.buttons.black
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 55
|
||||
anchors.rightMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 55
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: createEntitiesFlow.bottom
|
||||
anchors.topMargin: 35
|
||||
onClicked: {
|
||||
|
@ -205,9 +205,9 @@ TabBar {
|
|||
color: hifi.buttons.black
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 55
|
||||
anchors.rightMargin: 30
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 55
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: assetServerButton.bottom
|
||||
anchors.topMargin: 20
|
||||
onClicked: {
|
||||
|
|
|
@ -55,7 +55,7 @@ Rectangle {
|
|||
|
||||
Text {
|
||||
id: text1
|
||||
text: qsTr("Material URL (Optional)")
|
||||
text: qsTr("Material URL <i>(Optional)</i>")
|
||||
color: "#ffffff"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ Rectangle {
|
|||
|
||||
Text {
|
||||
id: text1
|
||||
text: qsTr("Model URL")
|
||||
text: qsTr("Model URL <i>(.fbx, .fst, .glb, .gltf, .obj, .gz)</i>")
|
||||
color: "#ffffff"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
|
|
@ -65,6 +65,14 @@
|
|||
url(../fonts/hifi-glyphs.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Vircadia-Glyphs;
|
||||
src: url(../../../../resources/fonts/vircadia_glyphs.ttf),
|
||||
url(../../../../fonts/vircadia_glyphs.ttf),
|
||||
url(../../../../interface/resources/fonts/vircadia_glyphs.ttf),
|
||||
url(../fonts/vircadia_glyphs.ttf);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -407,6 +415,21 @@ input[type=button].glyph, button.hifi-edit-button.glyph {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=button].normal, button.hifi-edit-button.normal {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
text-transform: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=button].vglyph, button.hifi-edit-button.vglyph {
|
||||
font-family: Vircadia-Glyphs;
|
||||
font-size: 20px;
|
||||
text-transform: none;
|
||||
min-width: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=button].red, button.hifi-edit-button.red {
|
||||
color: #fff;
|
||||
background-color: #94132e;
|
||||
|
@ -417,6 +440,16 @@ input[type=button].blue, button.hifi-edit-button.blue {
|
|||
background-color: #1080b8;
|
||||
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
|
||||
}
|
||||
input[type=button].orange, button.hifi-edit-button.orange {
|
||||
color: #fff;
|
||||
background-color: #8f5100;
|
||||
background: linear-gradient(#d97b00 20%, #8f5100 100%);
|
||||
}
|
||||
input[type=button].green, button.hifi-edit-button.green {
|
||||
color: #fff;
|
||||
background-color: #078a00;
|
||||
background: linear-gradient(#00cc07 20%, #078a00 100%);
|
||||
}
|
||||
input[type=button].white, button.hifi-edit-button.white {
|
||||
color: #121212;
|
||||
background-color: #afafaf;
|
||||
|
@ -435,6 +468,14 @@ input[type=button].blue:enabled:hover, button.hifi-edit-button.blue:enabled:hove
|
|||
background: linear-gradient(#00b4ef, #00b4ef);
|
||||
border: none;
|
||||
}
|
||||
input[type=button].orange:enabled:hover, button.hifi-edit-button.orange:enabled:hover {
|
||||
background: linear-gradient(#d97b00, #d97b00);
|
||||
border: none;
|
||||
}
|
||||
input[type=button].green:enabled:hover, button.hifi-edit-button.green:enabled:hover {
|
||||
background: linear-gradient(#00cc07, #00cc07);
|
||||
border: none;
|
||||
}
|
||||
input[type=button].white:enabled:hover, button.hifi-edit-button.white:enabled:hover {
|
||||
background: linear-gradient(#fff, #fff);
|
||||
border: none;
|
||||
|
@ -449,6 +490,12 @@ input[type=button].red:active, button.hifi-edit-button.red:active {
|
|||
input[type=button].blue:active, button.hifi-edit-button.blue:active {
|
||||
background: linear-gradient(#1080b8, #1080b8);
|
||||
}
|
||||
input[type=button].orange:active, button.hifi-edit-button.orange:active {
|
||||
background: linear-gradient(#8f5100, #8f5100);
|
||||
}
|
||||
input[type=button].green:active, button.hifi-edit-button.green:active {
|
||||
background: linear-gradient(#078a00, #078a00);
|
||||
}
|
||||
input[type=button].white:active, button.hifi-edit-button.white:active {
|
||||
background: linear-gradient(#afafaf, #afafaf);
|
||||
}
|
||||
|
@ -1196,7 +1243,6 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
|||
background: #2e2e2e url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACKSURBVChTjdAxDsMgDAXQT4UYuQIzCwsSKxsSJ4YDoByDY7AwUOG2aZMQqX+xhd9gzIwxA3/k8a7LCCFgraX+Fk4UY4RSCoyxNfwgzjlyzhhjXOEvSimhtUbvB3hGUkp472m2wxUKIaD3TnOCd6jWim3bvlBrfdjJOUeolEJoZj/4PMH83bl/BXgCWSs2Z09IjgoAAAAASUVORK5CYII=) no-repeat bottom right;
|
||||
}
|
||||
|
||||
|
||||
div#grid-section, body#entity-list-body {
|
||||
padding-bottom: 0;
|
||||
margin: 16px;
|
||||
|
@ -1227,12 +1273,6 @@ div#grid-section, body#entity-list-body {
|
|||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
#delete {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
#entity-list {
|
||||
position: relative; /* New positioning context. */
|
||||
}
|
||||
|
@ -1408,6 +1448,11 @@ input[type=button]#export {
|
|||
font-size: 15px;
|
||||
}
|
||||
|
||||
#entity-table-scroll .vglyph {
|
||||
font-family: Vircadia-Glyphs;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#entity-table {
|
||||
margin-top: -28px;
|
||||
margin-bottom: -18px;
|
||||
|
@ -1420,7 +1465,7 @@ input[type=button]#export {
|
|||
background: none;
|
||||
}
|
||||
|
||||
#entity-table .glyph {
|
||||
#entity-table .glyph .vglyph {
|
||||
margin: 0 -2px 0 -2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -1453,11 +1498,11 @@ input[type=button]#export {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
#entity-table th .glyph {
|
||||
#entity-table th .glyph .vglyph {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
}
|
||||
#entity-table th .glyph + .sort-order {
|
||||
#entity-table th .glyph .vglyph + .sort-order {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
}
|
||||
|
@ -1484,7 +1529,7 @@ input[type=button]#export {
|
|||
#entity-table td {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#entity-table td.glyph {
|
||||
#entity-table td .glyph .vglyph {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -1820,3 +1865,71 @@ div.multiZoneSelToolbar {
|
|||
padding: 0px;
|
||||
}
|
||||
|
||||
#menuBackgroundOverlay{
|
||||
background-color:transparent;
|
||||
position:fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
display:none;
|
||||
}
|
||||
|
||||
div.entity-list-menu {
|
||||
position: fixed;
|
||||
display: none;
|
||||
width: 70%;
|
||||
height: 30px;
|
||||
top: 42px;
|
||||
left: 150px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-style: solid;
|
||||
border-color: #505050;
|
||||
border-width: 1px;
|
||||
background-color: #c0c0c0;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.menu-separator{
|
||||
width: 90%;
|
||||
height: 2px;
|
||||
background-color: #505050;
|
||||
}
|
||||
|
||||
button.menu-button {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 0;
|
||||
padding: 6px;
|
||||
text-align: left;
|
||||
background-color: #c0c0c0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button.menu-button:hover {
|
||||
background-color: #00B4EF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button.menu-button:active {
|
||||
background-color: #00B4EF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.menu-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.menu-item-caption {
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.menu-item-shortcut {
|
||||
float: right;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue