Merge remote-tracking branch 'upstream/master' into update

This commit is contained in:
HifiExperiments 2020-11-09 19:33:38 -08:00
commit 7bfbf3c99c
161 changed files with 2256 additions and 900 deletions

View file

@ -3,114 +3,60 @@ name: Master CI Build
on: on:
push: push:
branches: branches:
- master - gha-master-ci
# FIXME: Change target branch to "master" before merging into "master" branch.
env: env:
#APP_NAME: gpu-frame-player
APP_NAME: interface APP_NAME: interface
BUILD_TYPE: Release BUILD_TYPE: Release
BUCKET_NAME: hifi-gh-builds BUILD_NUMBER: ${{ github.run_number }}
CI_BUILD: Github 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 }} GIT_COMMIT: ${{ github.sha }}
HIFI_VCPKG_BOOTSTRAP: true # VCPKG did not build well on OSX disabling HIFI_VCPKG_BOOTSTRAP, which invokes a download to a working version of vcpkg
LAUNCHER_HMAC_SECRET: ${{ secrets.launcher_hmac_secret }} # HIFI_VCPKG_BOOTSTRAP: true
OCULUS_APP_ID: '${{ secrets.oculus_app_id }}'
RELEASE_TYPE: PRODUCTION RELEASE_TYPE: PRODUCTION
RELEASE_DYNAMODB_V2: ReleaseManager2-ReleaseQueue-prod RELEASE_NUMBER: ${{ github.run_number }}
STABLE_BUILD: 0 STABLE_BUILD: 0
UPLOAD_BUCKET: athena-public
# OSX-specific variables
# OSX specific variables
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
MACOSX_DEPLOYMENT_TARGET: '10.11' MACOSX_DEPLOYMENT_TARGET: '10.11'
# WIN32 specific variables # WIN-specific variables
PreferredToolArchitecture: X64 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: 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: build:
strategy: strategy:
matrix: matrix:
os: [windows-latest, macOS-latest] os: [windows-latest, macOS-latest, ubuntu-18.04]
build_type: [full, client] # build_type: [full, client]
#os: [windows-latest, macOS-latest, ubuntu-latest] build_type: [full]
# exclude: include:
# - os: ubuntu-latest - os: ubuntu-18.04
# build_type: client build_type: full
apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0
fail-fast: false
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
needs: generate_build_number
steps: steps:
- name: Download build number - name: Report Build Number
uses: actions/download-artifact@v1 shell: bash
with: run: |
name: BUILD_NUMBER echo "Build number: $BUILD_NUMBER"
- name: Restore build number - name: Configure build environment 1
id: buildnumber
uses: highfidelity/build-number@v3
with:
output_name: RELEASE_NUMBER
- name: Configure Build Environment 1
shell: bash shell: bash
id: buildenv1 id: buildenv1
run: | run: |
echo ::set-env name=UPLOAD_PREFIX::master echo ::set-env name=UPLOAD_PREFIX::master
echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7` 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 # 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=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 fi
# Mac build variables # Mac build variables
if [ "${{ matrix.os }}" = "macOS-latest" ]; then if [ "${{ matrix.os }}" = "macOS-latest" ]; then
@ -118,8 +64,8 @@ jobs:
echo ::set-env name=ZIP_COMMAND::zip echo ::set-env name=ZIP_COMMAND::zip
echo ::set-env name=ZIP_ARGS::-r echo ::set-env name=ZIP_ARGS::-r
echo ::set-env name=INSTALLER_EXT::dmg echo ::set-env name=INSTALLER_EXT::dmg
echo ::set-env name=SYMBOL_REGEX::dSYM 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::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-mac-symbols.zip" echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip"
fi fi
# Windows build variables # Windows build variables
if [ "${{ matrix.os }}" = "windows-latest" ]; then if [ "${{ matrix.os }}" = "windows-latest" ]; then
@ -127,40 +73,28 @@ jobs:
echo ::set-env name=ZIP_COMMAND::7z echo ::set-env name=ZIP_COMMAND::7z
echo ::set-env name=ZIP_ARGS::a echo ::set-env name=ZIP_ARGS::a
echo ::set-env name=INSTALLER_EXT::exe 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-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 fi
# Configuration is broken into two steps because you can't set an env var and also reference it in the same step # 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 shell: bash
run: | run: |
echo "${{ steps.buildenv1.outputs.symbols_archive }}" 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 # Build type variables
if [ "${{ matrix.build_type }}" = "full" ]; then if [ "${{ matrix.build_type }}" = "full" ]; then
echo ::set-env name=CLIENT_ONLY::FALSE 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 else
echo ::set-env name=CLIENT_ONLY::TRUE 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 fi
# Linux build variables - name: Clear working directory
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then if: startsWith(matrix.os, 'windows')
echo ::set-env name=PYTHON_EXEC::python3
echo ::set-env name=CMAKE_EXTRA::""
fi
# Mac build variables
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
fi
# Windows build variables
if [ "${{ matrix.os }}" = "windows-latest" ]; then
echo ::set-env name=CMAKE_EXTRA::"-A x64"
echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}}
echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx"
fi
- name: Clear Working Directory
if: matrix.os == 'windows-latest'
shell: bash shell: bash
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}
run: rm -rf ./* run: rm -rf ./*
@ -168,89 +102,119 @@ jobs:
with: with:
submodules: true submodules: true
fetch-depth: 1 fetch-depth: 1
- name: Create Build Directory - name: Install dependencies
run: cmake -E make_directory ${{runner.workspace}}/build if: startsWith(matrix.os, 'ubuntu')
- name: Decrypt Signing Key (Windows)
if: matrix.os == 'windows-latest'
working-directory: ${{runner.workspace}}/build
shell: bash shell: bash
run: gpg --batch --yes -o codesign.pfx --passphrase "${{secrets.gpg_symmetric_key}}" --decrypt $GITHUB_WORKSPACE/tools/ci-scripts/codesign.pfx.gpg run: |
- name: Import Signing Key (Windows) echo "Installing Python Modules:"
if: matrix.os == 'windows-latest' pip3 install distro || exit 1
working-directory: ${{runner.workspace}}/build echo "Updating apt repository index"
shell: powershell sudo apt update || exit 1
run: | echo "Installing apt packages"
$mypwd=ConvertTo-SecureString -String ${{ secrets.pfx_key }} -Force -AsPlainText sudo apt install -y ${{ matrix.apt-dependencies }} || exit 1
Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\CurrentUser\My -FilePath ${{runner.workspace}}\build\codesign.pfx
Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\LocalMachine\My -FilePath ${{runner.workspace}}\build\codesign.pfx
- name: Install Python modules - name: Install Python modules
if: matrix.os != 'ubuntu-latest' if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
shell: bash shell: bash
run: $PYTHON_EXEC -m pip install boto3 PyGithub 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 - name: Configure CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
shell: bash shell: bash
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY $CMAKE_EXTRA 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 - name: Build application
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
shell: bash shell: bash
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME run: cmake --build . --config $BUILD_TYPE --target $APP_NAME $CMAKE_BUILD_EXTRA
- name: Build Console - name: Build domain server
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
shell: bash shell: bash
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console run: cmake --build . --config $BUILD_TYPE --target domain-server $CMAKE_BUILD_EXTRA
- name: Build Domain Server (FullBuild) - name: Build assignment client
if: matrix.build_type == 'full'
shell: bash
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config $BUILD_TYPE --target domain-server
- name: Build Assignment Client (FullBuild)
if: matrix.build_type == 'full'
shell: bash
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config $BUILD_TYPE --target assignment-client
- name: Build Installer
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
shell: bash shell: bash
run: cmake --build . --config $BUILD_TYPE --target package run: cmake --build . --config $BUILD_TYPE --target assignment-client $CMAKE_BUILD_EXTRA
- name: Sign Installer (Windows) - name: Build console
if: matrix.os == 'windows-latest' working-directory: ${{runner.workspace}}/build
shell: powershell shell: bash
working-directory: C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64 run: cmake --build . --config $BUILD_TYPE --target packaged-server-console $CMAKE_BUILD_EXTRA
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: Build installer
- name: Upload Artifact working-directory: ${{runner.workspace}}/build
if: matrix.os != 'ubuntu-latest' 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 shell: bash
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
env: env:
AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }} AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }} AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }}
run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py
- name: Archive Symbols #- name: Archive symbols
if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') # if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
working-directory: ${{runner.workspace}} # working-directory: ${{runner.workspace}}
shell: bash # shell: bash
run: | # run: |
SYMBOLS_TEMP="symbols-temp" # SYMBOLS_TEMP="symbols-temp"
mkdir $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 \; # find "./build" \( -path '*/tools/gpu-frame-player/*' -or -path '*/interface/*' -or -path '*/plugins/*' \) -regex ".*\.$SYMBOL_REGEX" -exec cp -r {} $SYMBOLS_TEMP \;
cd $SYMBOLS_TEMP # cd $SYMBOLS_TEMP
$ZIP_COMMAND $ZIP_ARGS ../${{ steps.buildenv1.outputs.symbols_archive }} . # $ZIP_COMMAND $ZIP_ARGS ../${{ steps.buildenv1.outputs.symbols_archive }} .
- name: Upload Symbols #- name: Upload symbols
if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') # if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
working-directory: ${{runner.workspace}} # working-directory: ${{runner.workspace}}
shell: bash # shell: bash
run: | # 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" # 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 #- name: Debug list symbols
# if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') # if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
# working-directory: ${{runner.workspace}} # working-directory: ${{runner.workspace}}
# shell: bash # shell: bash
# run: | # run: |
# unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}" # unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}"
# - name: Debug Upload Symbols Artifact #- name: Upload debug list symbols
# if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest') # if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
# uses: actions/upload-artifact@v1 # uses: actions/upload-artifact@v1
# with: # with:
# name: symbols # name: symbols
# path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }} # path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}

View file

@ -112,11 +112,12 @@ Agent::Agent(ReceivedMessage& message) :
packetReceiver.registerListenerForTypes( packetReceiver.registerListenerForTypes(
{ PacketType::MixedAudio, PacketType::SilentAudioFrame }, { PacketType::MixedAudio, PacketType::SilentAudioFrame },
this, "handleAudioPacket"); PacketReceiver::makeUnsourcedListenerReference<Agent>(this, &Agent::handleAudioPacket));
packetReceiver.registerListenerForTypes( packetReceiver.registerListenerForTypes(
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket"); PacketReceiver::makeSourcedListenerReference<Agent>(this, &Agent::handleOctreePacket));
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); packetReceiver.registerListener(PacketType::SelectedAudioFormat,
PacketReceiver::makeUnsourcedListenerReference<Agent>(this, &Agent::handleSelectedAudioFormat));
// 100Hz timer for audio // 100Hz timer for audio
const int TARGET_INTERVAL_MSEC = 10; // 10ms const int TARGET_INTERVAL_MSEC = 10; // 10ms

View file

@ -118,8 +118,10 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
setUpStatusToMonitor(); setUpStatusToMonitor();
} }
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::CreateAssignment, this, "handleCreateAssignmentPacket"); packetReceiver.registerListener(PacketType::CreateAssignment,
packetReceiver.registerListener(PacketType::StopNode, this, "handleStopNodePacket"); PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleCreateAssignmentPacket));
packetReceiver.registerListener(PacketType::StopNode,
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleStopNodePacket));
} }
void AssignmentClient::stopAssignmentClient() { void AssignmentClient::stopAssignmentClient() {

View file

@ -72,7 +72,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort); auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); 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)); adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks));
// use QProcess to fork off a process for each of the child assignment clients // use QProcess to fork off a process for each of the child assignment clients

View file

@ -308,7 +308,8 @@ AssetServer::AssetServer(ReceivedMessage& message) :
// Queue all requests until the Asset Server is fully setup // Queue all requests until the Asset Server is fully setup
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); 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 #ifdef Q_OS_WIN
updateConsumedCores(); updateConsumedCores();
@ -464,10 +465,14 @@ void AssetServer::completeSetup() {
qCDebug(asset_server) << "Overriding temporary queuing packet handler."; qCDebug(asset_server) << "Overriding temporary queuing packet handler.";
// We're fully setup, override the request queueing handler and replay all requests // We're fully setup, override the request queueing handler and replay all requests
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet"); packetReceiver.registerListener(PacketType::AssetGet,
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo"); PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetGet));
packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload"); packetReceiver.registerListener(PacketType::AssetGetInfo,
packetReceiver.registerListener(PacketType::AssetMappingOperation, this, "handleAssetMappingOperation"); 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(); replayRequests();
} }

View file

@ -101,20 +101,23 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
PacketType::InjectorGainSet, PacketType::InjectorGainSet,
PacketType::AudioSoloRequest, PacketType::AudioSoloRequest,
PacketType::StopInjector }, PacketType::StopInjector },
this, "queueAudioPacket"); PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::queueAudioPacket)
);
// packets whose consequences are global should be processed on the main thread // packets whose consequences are global should be processed on the main thread
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::MuteEnvironment,
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleMuteEnvironmentPacket));
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest,
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleNodeMuteRequestPacket));
packetReceiver.registerListener(PacketType::KillAvatar,
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleKillAvatarPacket));
packetReceiver.registerListenerForTypes({ packetReceiver.registerListenerForTypes({
PacketType::ReplicatedMicrophoneAudioNoEcho, PacketType::ReplicatedMicrophoneAudioNoEcho,
PacketType::ReplicatedMicrophoneAudioWithEcho, PacketType::ReplicatedMicrophoneAudioWithEcho,
PacketType::ReplicatedInjectAudio, PacketType::ReplicatedInjectAudio,
PacketType::ReplicatedSilentAudioFrame PacketType::ReplicatedSilentAudioFrame },
}, PacketReceiver::makeUnsourcedListenerReference<AudioMixer>(this, &AudioMixer::queueReplicatedAudioPacket)
this, "queueReplicatedAudioPacket"
); );
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled); connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);

View file

@ -71,26 +71,38 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled); connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket"); packetReceiver.registerListener(PacketType::AvatarData,
packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting"); PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
packetReceiver.registerListener(PacketType::AvatarQuery, this, "handleAvatarQueryPacket"); packetReceiver.registerListener(PacketType::AdjustAvatarSorting,
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAdjustAvatarSorting));
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::AvatarQuery,
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAvatarQueryPacket));
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::AvatarIdentity,
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAvatarIdentityPacket));
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket"); packetReceiver.registerListener(PacketType::KillAvatar,
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket"); 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 }, packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket"); PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleOctreePacket));
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "queueIncomingPacket"); packetReceiver.registerListener(PacketType::ChallengeOwnership,
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
packetReceiver.registerListenerForTypes({ packetReceiver.registerListenerForTypes({
PacketType::ReplicatedAvatarIdentity, PacketType::ReplicatedAvatarIdentity,
PacketType::ReplicatedKillAvatar 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>(); auto nodeList = DependencyManager::get<NodeList>();
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch); connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch);

View file

@ -59,8 +59,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
PacketType::ChallengeOwnership, PacketType::ChallengeOwnership,
PacketType::ChallengeOwnershipRequest, PacketType::ChallengeOwnershipRequest,
PacketType::ChallengeOwnershipReply }, PacketType::ChallengeOwnershipReply },
this, PacketReceiver::makeSourcedListenerReference<EntityServer>(this, &EntityServer::handleEntityPacket));
"handleEntityPacket");
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification); connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
_dynamicDomainVerificationTimer.setSingleShot(true); _dynamicDomainVerificationTimer.setSingleShot(true);

View file

@ -25,9 +25,12 @@ MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(mess
{ {
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessages"); packetReceiver.registerListener(PacketType::MessagesData,
packetReceiver.registerListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe"); PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessages));
packetReceiver.registerListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe"); 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) { void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {

View file

@ -1122,8 +1122,10 @@ void OctreeServer::run() {
void OctreeServer::domainSettingsRequestComplete() { void OctreeServer::domainSettingsRequestComplete() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket"); packetReceiver.registerListener(PacketType::OctreeDataNack,
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket"); PacketReceiver::makeSourcedListenerReference<OctreeServer>(this, &OctreeServer::handleOctreeDataNackPacket));
packetReceiver.registerListener(getMyQueryMessageType(),
PacketReceiver::makeSourcedListenerReference<OctreeServer>(this, &OctreeServer::handleOctreeQueryPacket));
qDebug(octree_server) << "Received domain settings"; qDebug(octree_server) << "Received domain settings";

View file

@ -83,13 +83,18 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket"); PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleOctreePacket));
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); packetReceiver.registerListener(PacketType::SelectedAudioFormat,
PacketReceiver::makeUnsourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleSelectedAudioFormat));
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); packetReceiver.registerListener(PacketType::ReloadEntityServerScript,
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleReloadEntityServerScriptPacket));
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket"); packetReceiver.registerListener(PacketType::EntityScriptGetStatus,
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); 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; static const int LOG_INTERVAL = MSECS_PER_SECOND / 10;
auto timer = new QTimer(this); auto timer = new QTimer(this);

View file

@ -202,9 +202,9 @@
; The Inner invocation has written an uninstaller binary for us. ; The Inner invocation has written an uninstaller binary for us.
; We need to sign it if it's a production or PR build. ; We need to sign it if it's a production or PR build.
!if @PRODUCTION_BUILD@ == 1 !if @PRODUCTION_BUILD@ == 1
!if @BYPASS_SIGNING@ == 1 !if @BYPASS_SIGNING@ == TRUE
!warning "BYPASS_SIGNING set - installer will not be signed" !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 !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
!endif !endif

2
debian/rules vendored
View file

@ -6,7 +6,7 @@
override_dh_auto_configure: override_dh_auto_configure:
mkdir obj-$(DEB_TARGET_MULTIARCH) 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: override_dh_auto_build:
(cd obj-$(DEB_TARGET_MULTIARCH) && make -j4) (cd obj-$(DEB_TARGET_MULTIARCH) && make -j4)

View file

@ -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) { function getMetaverseUrl(callback) {
$.ajax('/api/metaverse_info', { $.ajax('/api/metaverse_info', {
success: function(data) { success: function(data) {

View file

@ -40,6 +40,8 @@ $(document).ready(function(){
// call our method to setup the place names table // call our method to setup the place names table
setupPlacesTable(); 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(); setupDomainNetworkingSettings();
// setupDomainLabelSetting(); // setupDomainLabelSetting();
@ -363,7 +365,7 @@ $(document).ready(function(){
confirmButtonText: "Create", confirmButtonText: "Create",
closeOnConfirm: false, closeOnConfirm: false,
html: true html: true
}, function(inputValue){ }, function (inputValue) {
if (inputValue === false) { if (inputValue === false) {
swal.close(); swal.close();
@ -373,7 +375,7 @@ $(document).ready(function(){
} }
} else { } else {
// we're going to change the alert to a new one with a spinner while we create this domain // 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); createNewDomainID(inputValue, justConnected);
} }
}); });
@ -385,7 +387,12 @@ $(document).ready(function(){
"label": label "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 // we successfully created a domain ID, set it on that field
var domainID = data.domain.domainId; var domainID = data.domain.domainId;
console.log("Setting domain id to ", data, domainID); console.log("Setting domain id to ", data, domainID);
@ -406,40 +413,50 @@ $(document).ready(function(){
text: successText, text: successText,
html: true, html: true,
confirmButtonText: 'Save' confirmButtonText: 'Save'
}, function(){ }, function () {
saveSettings(); 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) { if (justConnected) {
errorText += " just save your new access token?</br></br>You can always create a new domain ID later."; 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 { } else {
errorText += " cancel?" // they want to cancel
} if (justConnected) {
// since they just connected we need to save the access token here
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel saveSettings();
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();
}
} }
}); }
}); });
} }
@ -711,8 +728,8 @@ $(document).ready(function(){
name: 'places', name: 'places',
label: 'Places', label: 'Places',
html_id: Settings.PLACES_TABLE_ID, html_id: Settings.PLACES_TABLE_ID,
help: "The following places currently point to this domain.</br>To point places to this domain, " help: "To point places to this domain, "
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>My Places</a> " + " go to the <a href='" + METAVERSE_URL + "/user/places'>Places</a> "
+ "page in your Metaverse account.", + "page in your Metaverse account.",
read_only: true, read_only: true,
can_add_new_rows: false, can_add_new_rows: false,
@ -745,9 +762,10 @@ $(document).ready(function(){
var errorEl = createDomainLoadingError("There was an error retrieving your places."); var errorEl = createDomainLoadingError("There was an error retrieving your places.");
$("#" + Settings.PLACES_TABLE_ID).after(errorEl); $("#" + Settings.PLACES_TABLE_ID).after(errorEl);
var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name'); // DISABLE TEMP PLACE NAME BUTTON...
temporaryPlaceButton.hide(); // var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
$('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton); // temporaryPlaceButton.hide();
// $('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
if (accessTokenIsSet()) { if (accessTokenIsSet()) {
appendAddButtonToPlacesTable(); appendAddButtonToPlacesTable();
} }

View file

@ -19,7 +19,7 @@
</dl> </dl>
</div> </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> <h4 class="step-title"></h4>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
@ -51,7 +51,7 @@
<button type="button" class="btn btn-md btn-block btn-primary next-button">Next</button> <button type="button" class="btn btn-md btn-block btn-primary next-button">Next</button>
</dd> </dd>
</dl> </dl>
</div> </div> -->
<div class="wizard-step col-md-9 col-centered" style="display: none;"> <div class="wizard-step col-md-9 col-centered" style="display: none;">
<h4 class="step-title"></h4> <h4 class="step-title"></h4>
@ -204,7 +204,7 @@
</div> </div>
<br /> <br />
<div id="automatic-threading-options-row" class="row"> <div id="automatic-threading-options-row" class="row">
<p class="col-md-2"> <p class="col-md-12">
<label> <label>
<input id="enable-automatic-threading" name="threading-checkbox" type="checkbox" value="true"> Enable Automatic Threading <input id="enable-automatic-threading" name="threading-checkbox" type="checkbox" value="true"> Enable Automatic Threading
</label> </label>

View file

@ -50,6 +50,7 @@ $(document).ready(function(){
prepareAccessTokenPrompt(function(accessToken) { prepareAccessTokenPrompt(function(accessToken) {
Metaverse.accessToken = accessToken; Metaverse.accessToken = accessToken;
saveAccessToken(); saveAccessToken();
promptToCreateDomainID();
}); });
}); });
@ -171,6 +172,45 @@ function setupWizardSteps() {
$(currentStep).show(); $(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) { function updatePlaceNameLink(address) {
if (address) { if (address) {
var url = URLs.PLACE_URL + '/' + address; var url = URLs.PLACE_URL + '/' + address;
@ -341,7 +381,7 @@ function saveAccessToken() {
$(this).blur(); $(this).blur();
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved // 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) { function getSettingDescriptionForKey(groupKey, settingKey) {

View file

@ -17,7 +17,8 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include <random> #include <random>
#include <QDataStream> #include <QtCore/QDataStream>
#include <QtCore/QMetaMethod>
#include <AccountManager.h> #include <AccountManager.h>
#include <Assignment.h> #include <Assignment.h>

View file

@ -340,7 +340,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
void DomainServer::parseCommandLine(int argc, char* argv[]) { void DomainServer::parseCommandLine(int argc, char* argv[]) {
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity Domain Server"); parser.setApplicationDescription("Vircadia Domain Server");
const QCommandLineOption versionOption = parser.addVersionOption(); const QCommandLineOption versionOption = parser.addVersionOption();
const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption helpOption = parser.addHelpOption();
@ -778,32 +778,51 @@ void DomainServer::setupNodeListAndAssignments() {
// register as the packet receiver for the types we want // register as the packet receiver for the types we want
PacketReceiver& packetReceiver = nodeList->getPacketReceiver(); PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket"); packetReceiver.registerListener(PacketType::RequestAssignment,
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket"); PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processRequestAssignmentPacket));
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); packetReceiver.registerListener(PacketType::DomainListRequest,
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); PacketReceiver::makeSourcedListenerReference<DomainServer>(this, &DomainServer::processListRequestPacket));
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket"); packetReceiver.registerListener(PacketType::DomainServerPathQuery,
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacket"); 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 // 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::DomainSettingsRequest,
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket"); PacketReceiver::makeUnsourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processSettingsRequestPacket));
packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket"); 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 // register the gatekeeper for the packets it needs to receive
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket"); packetReceiver.registerListener(PacketType::DomainConnectRequest,
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket"); PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processConnectRequestPacket));
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket"); packetReceiver.registerListener(PacketType::ICEPing,
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket"); 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::ICEServerHeartbeatDenied,
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK"); 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::OctreeDataFileRequest,
packetReceiver.registerListener(PacketType::OctreeDataPersist, this, "processOctreeDataPersistMessage"); 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::OctreeFileReplacement,
packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl, this, "handleDomainContentReplacementFromURLRequest"); 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 // set a custom packetVersionMatch as the verify packet operator for the udt::Socket
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified); nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);

View file

@ -212,6 +212,8 @@ private:
/// guard read/write access from multiple threads to settings /// guard read/write access from multiple threads to settings
QReadWriteLock _settingsLock { QReadWriteLock::Recursive }; QReadWriteLock _settingsLock { QReadWriteLock::Recursive };
friend class DomainServer;
}; };
#endif // hifi_DomainServerSettingsManager_h #endif // hifi_DomainServerSettingsManager_h

View file

@ -239,7 +239,7 @@ target_openssl()
target_bullet() target_bullet()
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()
add_crashpad() add_crashpad()
target_breakpad() target_breakpad()

View file

@ -3,9 +3,9 @@
"channels": [ "channels": [
{ "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, { "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.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", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, { "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.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" },
@ -72,46 +72,20 @@
{ "from": { "makeAxis" : [ { "from": { "makeAxis" : [
["Keyboard.Left"], ["Keyboard.Left"],
["Keyboard.Right"] ["Keyboard.Right"]
] ]
}, },
"when": ["Application.CameraFirstPerson", "!Keyboard.Shift"], "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"],
"to": "Actions.Yaw" "to": "Actions.Yaw"
}, },
{ "from": { "makeAxis" : [ { "from": "Keyboard.Left",
["Keyboard.Left"], "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"],
["Keyboard.Right"] "to": "Actions.LATERAL_LEFT"
]
},
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"],
"to": "Actions.Yaw"
}, },
{ "from": { "makeAxis" : [ { "from": "Keyboard.Right",
["Keyboard.Left"], "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"],
["Keyboard.Right"] "to": "Actions.LATERAL_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": { "makeAxis" : [ { "from": { "makeAxis" : [
@ -119,53 +93,18 @@
["Keyboard.D"] ["Keyboard.D"]
] ]
}, },
"when": ["Application.CameraFirstPerson", "!Keyboard.Control"], "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"],
"to": "Actions.Yaw" "to": "Actions.Yaw"
}, },
{ "from": { "makeAxis" : [ { "from": "Keyboard.A",
["Keyboard.A"], "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"],
["Keyboard.D"] "to": "Actions.LATERAL_LEFT"
]
},
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"],
"to": "Actions.Yaw"
}, },
{ "from": { "makeAxis" : [ { "from": "Keyboard.D",
["Keyboard.A"], "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"],
["Keyboard.D"] "to": "Actions.LATERAL_RIGHT"
]
},
"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": { "makeAxis" : [ { "from": { "makeAxis" : [
@ -173,39 +112,12 @@
["Keyboard.TouchpadRight"] ["Keyboard.TouchpadRight"]
] ]
}, },
"when": "Application.CameraFirstPersonLookat", "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"],
"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",
"to": "Actions.Yaw" "to": "Actions.Yaw"
}, },
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": "Keyboard.RightMouseButton", "when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"],
"to": "Actions.DeltaYaw", "to": "Actions.DeltaYaw",
"filters": "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"] }, { "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", "to": "Actions.DeltaPitch",
"filters": "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"] }, { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
"when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"],
"to": "Actions.DeltaPitch", "to": "Actions.DeltaPitch",

Binary file not shown.

View file

@ -333,6 +333,8 @@ Rectangle {
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
height: childrenRect.height; height: childrenRect.height;
// FIXME: Reuse or remove wallet-related code.
visible: false;
Rectangle { Rectangle {
id: walletHeaderContainer; id: walletHeaderContainer;

View file

@ -27,6 +27,7 @@ Item {
width: parent.width width: parent.width
property string title: "Controls" property string title: "Controls"
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve"]
HifiConstants { id: hifi } HifiConstants { id: hifi }
@ -244,7 +245,7 @@ Item {
source: InputConfiguration.configurationLayout(box.textAt(box.currentIndex)); source: InputConfiguration.configurationLayout(box.textAt(box.currentIndex));
onLoaded: { onLoaded: {
if (loader.item.hasOwnProperty("pluginName")) { if (loader.item.hasOwnProperty("pluginName")) {
if (box.textAt(box.currentIndex) === "HTC Vive") { if (openVRDevices.indexOf(box.textAt(box.currentIndex)) !== -1) {
loader.item.pluginName = "OpenVR"; loader.item.pluginName = "OpenVR";
} else { } else {
loader.item.pluginName = box.textAt(box.currentIndex); loader.item.pluginName = box.textAt(box.currentIndex);
@ -298,7 +299,7 @@ Item {
loader.source = ""; loader.source = "";
var selectedDevice = box.textAt(box.currentIndex); var selectedDevice = box.textAt(box.currentIndex);
var source = ""; var source = "";
if (selectedDevice == "HTC Vive") { if (openVRDevices.indexOf(selectedDevice) !== -1) {
source = InputConfiguration.configurationLayout("OpenVR"); source = InputConfiguration.configurationLayout("OpenVR");
} else { } else {
source = InputConfiguration.configurationLayout(selectedDevice); source = InputConfiguration.configurationLayout(selectedDevice);

View file

@ -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>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>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>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 * <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement (walking) controls are
* enabled.</td></tr> * enabled.</td></tr>
* <tr><td><code>StrafeEnabled</code></td><td>number</td><td>number</td><td>Strafing is 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_LEFT_HAND_DOMINANT = "LeftHandDominant";
static const QString STATE_RIGHT_HAND_DOMINANT = "RightHandDominant"; static const QString STATE_RIGHT_HAND_DOMINANT = "RightHandDominant";
static const QString STATE_STRAFE_ENABLED = "StrafeEnabled"; static const QString STATE_STRAFE_ENABLED = "StrafeEnabled";
static const QString STATE_CAPTURE_MOUSE = "CaptureMouse";
// Statically provided display and input plugins // Statically provided display and input plugins
extern DisplayPluginList getDisplayPlugins(); extern DisplayPluginList getDisplayPlugins();
@ -919,7 +923,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<MessagesClient>(); DependencyManager::set<MessagesClient>();
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, 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_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_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 } }); STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } });
DependencyManager::set<UserInputMapper>(); DependencyManager::set<UserInputMapper>();
@ -1892,6 +1896,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float { _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0; 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 { _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
}); });
@ -2407,6 +2414,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateSystemTabletMode(); updateSystemTabletMode();
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); 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>()->setShouldPickHUDOperator([]() { return DependencyManager::get<HMDScriptingInterface>()->isHMDMode(); });
DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([this](const glm::vec3& intersection) { DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([this](const glm::vec3& intersection) {
@ -4704,6 +4712,11 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const {
void Application::mouseMoveEvent(QMouseEvent* event) { void Application::mouseMoveEvent(QMouseEvent* event) {
PROFILE_RANGE(app_input_mouse, __FUNCTION__); PROFILE_RANGE(app_input_mouse, __FUNCTION__);
if (_ignoreMouseMove) {
_ignoreMouseMove = false;
return;
}
maybeToggleMenuVisible(event); maybeToggleMenuVisible(event);
auto& compositor = getApplicationCompositor(); auto& compositor = getApplicationCompositor();
@ -4749,7 +4762,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
} }
if (_keyboardMouseDevice->isActive()) { if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->mouseMoveEvent(event); _keyboardMouseDevice->mouseMoveEvent(event, _captureMouse, _mouseCaptureTarget);
} }
} }
@ -5950,6 +5963,20 @@ void Application::cameraModeChanged() {
cameraMenuChanged(); 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) { void Application::changeViewAsNeeded(float boomLength) {
// Switch between first and third person views as needed // Switch between first and third person views as needed
// This is called when the boom length has changed // This is called when the boom length has changed
@ -6300,6 +6327,15 @@ void Application::update(float deltaTime) {
PROFILE_ASYNC_END(app, "Scene Loading", ""); 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(); auto myAvatar = getMyAvatar();
{ {
PerformanceTimer perfTimer("devices"); 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) { 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::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); 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_PITCH, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_PITCH));
myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_YAW)); 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)); myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
} }
} }

View file

@ -431,6 +431,7 @@ public slots:
void cycleCamera(); void cycleCamera();
void cameraModeChanged(); void cameraModeChanged();
void cameraMenuChanged(); void cameraMenuChanged();
void captureMouseChanged(bool captureMouse);
void toggleOverlays(); void toggleOverlays();
void setOverlaysVisible(bool visible); void setOverlaysVisible(bool visible);
Q_INVOKABLE void centerUI(); Q_INVOKABLE void centerUI();
@ -603,6 +604,7 @@ private:
void maybeToggleMenuVisible(QMouseEvent* event) const; void maybeToggleMenuVisible(QMouseEvent* event) const;
void toggleTabletUI(bool shouldOpen = false) const; void toggleTabletUI(bool shouldOpen = false) const;
bool shouldCaptureMouse() const;
void userKickConfirmation(const QUuid& nodeID); void userKickConfirmation(const QUuid& nodeID);
@ -756,7 +758,9 @@ private:
bool _settingsLoaded { false }; bool _settingsLoaded { false };
bool _fakedMouseEvent { false }; bool _captureMouse { false };
bool _ignoreMouseMove { false };
QPointF _mouseCaptureTarget { NAN, NAN };
bool _isMissingSequenceNumbers { false }; bool _isMissingSequenceNumbers { false };

View file

@ -36,6 +36,10 @@ class FancyCamera : public Camera {
* @property {ViewFrustum} frustum - The camera frustum. * @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 * @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the
* camera is in entity mode. * 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) Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)

View file

@ -288,8 +288,10 @@ Wallet::Wallet() {
auto& packetReceiver = nodeList->getPacketReceiver(); auto& packetReceiver = nodeList->getPacketReceiver();
_passphrase = new QString(""); _passphrase = new QString("");
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket"); packetReceiver.registerListener(PacketType::ChallengeOwnership,
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket"); PacketReceiver::makeSourcedListenerReference<Wallet>(this, &Wallet::handleChallengeOwnershipPacket));
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest,
PacketReceiver::makeSourcedListenerReference<Wallet>(this, &Wallet::handleChallengeOwnershipPacket));
connect(ledger.data(), &Ledger::accountResult, this, [](QJsonObject result) { connect(ledger.data(), &Ledger::accountResult, this, [](QJsonObject result) {
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();

View file

@ -81,7 +81,7 @@ int main(int argc, const char* argv[]) {
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater"); QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications"); QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server"); 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 allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir"); QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path"); QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");

View file

@ -25,7 +25,8 @@ OctreePacketProcessor::OctreePacketProcessor():
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
const PacketReceiver::PacketTypeList octreePackets = const PacketReceiver::PacketTypeList octreePackets =
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete }; { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete };
packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket"); packetReceiver.registerDirectListenerForTypes(octreePackets,
PacketReceiver::makeSourcedListenerReference<OctreePacketProcessor>(this, &OctreePacketProcessor::handleOctreePacket));
} }
OctreePacketProcessor::~OctreePacketProcessor() { } OctreePacketProcessor::~OctreePacketProcessor() { }

View file

@ -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. // 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>(); auto nodeList = DependencyManager::get<NodeList>();
PacketReceiver& packetReceiver = nodeList->getPacketReceiver(); PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacketOnClient"); packetReceiver.registerListener(PacketType::AvatarZonePresence,
PacketReceiver::makeUnsourcedListenerReference<ScreenshareScriptingInterface>(this, &ScreenshareScriptingInterface::processAvatarZonePresencePacketOnClient));
}; };
ScreenshareScriptingInterface::~ScreenshareScriptingInterface() { ScreenshareScriptingInterface::~ScreenshareScriptingInterface() {

View file

@ -9,7 +9,8 @@
// //
#include "DomainConnectionModel.h" #include "DomainConnectionModel.h"
#include <QLoggingCategory> #include <QtCore/QLoggingCategory>
#include <QtCore/QMetaMethod>
#include <NodeList.h> #include <NodeList.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>

View file

@ -358,6 +358,16 @@ void setupPreferences() {
preference->setItems(items); preference->setItems(items);
preferences->addPreference(preference); 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 getter = [myAvatar]()->int { return myAvatar->getControlScheme(); };
auto setter = [myAvatar](int index) { myAvatar->setControlScheme(index); }; auto setter = [myAvatar](int index) { myAvatar->setControlScheme(index); };

View file

@ -274,7 +274,7 @@ private: \
* of 2 if smaller than 128 pixels, or a multiple of 128 if greater than 128 pixels. * of 2 if smaller than 128 pixels, or a multiple of 128 if greater than 128 pixels.
* <em>Read-only.</em> * <em>Read-only.</em>
* @property {number} decimatedTextureCount - The number of textures that have been reduced in size because they were over the * @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> * <em>Read-only.</em>
* @property {number} gpuBuffers - The number of OpenGL buffer objects managed by the GPU back-end. * @property {number} gpuBuffers - The number of OpenGL buffer objects managed by the GPU back-end.
* <em>Read-only.</em> * <em>Read-only.</em>

View file

@ -81,7 +81,8 @@ ContextOverlayInterface::ContextOverlayInterface() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket"); packetReceiver.registerListener(PacketType::ChallengeOwnershipReply,
PacketReceiver::makeSourcedListenerReference<ContextOverlayInterface>(this, &ContextOverlayInterface::handleChallengeOwnershipReplyPacket));
_challengeOwnershipTimeoutTimer.setSingleShot(true); _challengeOwnershipTimeoutTimer.setSingleShot(true);
} }

View file

@ -369,13 +369,20 @@ AudioClient::AudioClient() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "processStreamStatsPacket"); packetReceiver.registerListener(PacketType::AudioStreamStats,
packetReceiver.registerListener(PacketType::AudioEnvironment, this, "handleAudioEnvironmentDataPacket"); PacketReceiver::makeSourcedListenerReference<AudioIOStats>(&_stats, &AudioIOStats::processStreamStatsPacket));
packetReceiver.registerListener(PacketType::SilentAudioFrame, this, "handleAudioDataPacket"); packetReceiver.registerListener(PacketType::AudioEnvironment,
packetReceiver.registerListener(PacketType::MixedAudio, this, "handleAudioDataPacket"); PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioEnvironmentDataPacket));
packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket"); packetReceiver.registerListener(PacketType::SilentAudioFrame,
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioDataPacket));
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); 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(); auto& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] { connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] {

View file

@ -123,10 +123,14 @@ AvatarHashMap::AvatarHashMap() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::BulkAvatarData,
packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar"); PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processAvatarDataPacket));
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar,
packetReceiver.registerListener(PacketType::BulkAvatarTraits, this, "processBulkAvatarTraits"); 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); connect(nodeList.data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);

View file

@ -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) { void ClientTraitsHandler::markTraitUpdated(AvatarTraits::TraitType updatedTrait) {

View file

@ -13,7 +13,7 @@ include_hifi_library_headers(ktx)
include_hifi_library_headers(render) include_hifi_library_headers(render)
include_hifi_library_headers(procedural) include_hifi_library_headers(procedural)
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()
GroupSources("src/display-plugins") GroupSources("src/display-plugins")

View file

@ -1,6 +1,7 @@
// //
// Created by Benjamin Arnold on 5/27/14. // Created by Benjamin Arnold on 5/27/14.
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -213,7 +214,6 @@ void CompositorHelper::setAllowMouseCapture(bool capture) {
_allowMouseCapture = capture; _allowMouseCapture = capture;
emit allowMouseCaptureChanged(); emit allowMouseCaptureChanged();
} }
_allowMouseCapture = capture;
} }
void CompositorHelper::handleLeaveEvent() { void CompositorHelper::handleLeaveEvent() {

View file

@ -1,6 +1,7 @@
// //
// Created by Bradley Austin Davis Arnold on 2015/06/13 // Created by Bradley Austin Davis Arnold on 2015/06/13
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html

View file

@ -26,7 +26,8 @@
EntityEditPacketSender::EntityEditPacketSender() { EntityEditPacketSender::EntityEditPacketSender() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); 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) { void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {

View file

@ -1214,7 +1214,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* ], * ],
* strokeWidths: [ 0.1, 0.1, 0.1 ], * strokeWidths: [ 0.1, 0.1, 0.1 ],
* color: { red: 255, green: 0, blue: 0 }, // Use just the red channel from the image. * 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, * isUVModeStretch: true,
* lifetime: 300 // Delete after 5 minutes. * lifetime: 300 // Delete after 5 minutes.
* }); * });

View file

@ -14,7 +14,8 @@
EntityScriptServerLogClient::EntityScriptServerLogClient() { EntityScriptServerLogClient::EntityScriptServerLogClient() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); 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::nodeActivated, this, &EntityScriptServerLogClient::nodeActivated);
QObject::connect(nodeList.data(), &NodeList::nodeKilled, this, &EntityScriptServerLogClient::nodeKilled); QObject::connect(nodeList.data(), &NodeList::nodeKilled, this, &EntityScriptServerLogClient::nodeKilled);

View file

@ -59,7 +59,8 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged); connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged);
auto& packetReceiver = nodeList->getPacketReceiver(); 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, void EntityScriptingInterface::queueEntityMessage(PacketType packetType,

View file

@ -709,6 +709,8 @@ public slots:
/**jsdoc /**jsdoc
* Finds all domain and avatar entities that intersect a sphere. * 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 * @function Entities.findEntities
* @param {Vec3} center - The point about which to search. * @param {Vec3} center - The point about which to search.
* @param {number} radius - The radius within which to search. * @param {number} radius - The radius within which to search.
@ -723,6 +725,8 @@ public slots:
/**jsdoc /**jsdoc
* Finds all domain and avatar entities whose axis-aligned boxes intersect a search axis-aligned box. * 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 * @function Entities.findEntitiesInBox
* @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values. * @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. * @param {Vec3} dimensions - The dimensions of the search AA box.
@ -734,6 +738,8 @@ public slots:
/**jsdoc /**jsdoc
* Finds all domain and avatar entities whose axis-aligned boxes intersect a search frustum. * 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 * @function Entities.findEntitiesInFrustum
* @param {ViewFrustum} frustum - The frustum to search in. The <code>position</code>, <code>orientation</code>, * @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> * <code>projection</code>, and <code>centerRadius</code> properties must be specified. The <code>fieldOfView</code>
@ -749,6 +755,8 @@ public slots:
/**jsdoc /**jsdoc
* Finds all domain and avatar entities of a particular type that intersect a sphere. * 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 * @function Entities.findEntitiesByType
* @param {Entities.EntityType} entityType - The type of entity to search for. * @param {Entities.EntityType} entityType - The type of entity to search for.
* @param {Vec3} center - The point about which to search. * @param {Vec3} center - The point about which to search.
@ -764,6 +772,8 @@ public slots:
/**jsdoc /**jsdoc
* Finds all domain and avatar entities with a particular name that intersect a sphere. * 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 * @function Entities.findEntitiesByName
* @param {string} entityName - The name of the entity to search for. * @param {string} entityName - The name of the entity to search for.
* @param {Vec3} center - The point about which to search. * @param {Vec3} center - The point about which to search.

View file

@ -1586,12 +1586,16 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
int targetIndex = weightedIndex; int targetIndex = weightedIndex;
hfmModel.blendshapeChannelNames.push_back("target_" + QString::number(weightedIndex)); hfmModel.blendshapeChannelNames.push_back("target_" + QString::number(weightedIndex));
if (!names.isEmpty() && names.contains(keys[weightedIndex])) { if (!names.isEmpty()) {
targetIndex = names.indexOf(keys[weightedIndex]); targetIndex = names.indexOf(keys[weightedIndex]);
if (targetIndex == -1) {
continue; // Ignore blendshape targets not present in glTF file.
}
indexFromMapping = values[weightedIndex].first; indexFromMapping = values[weightedIndex].first;
weight = values[weightedIndex].second; weight = values[weightedIndex].second;
hfmModel.blendshapeChannelNames[weightedIndex] = keys[weightedIndex]; hfmModel.blendshapeChannelNames[weightedIndex] = keys[weightedIndex];
} }
HFMBlendshape& blendshape = mesh.blendshapes[indexFromMapping]; HFMBlendshape& blendshape = mesh.blendshapes[indexFromMapping];
auto target = primitive.targets[targetIndex]; auto target = primitive.targets[targetIndex];

View file

@ -2,6 +2,6 @@ set(TARGET_NAME gl)
setup_hifi_library(Gui Widgets) setup_hifi_library(Gui Widgets)
link_hifi_libraries(shared) link_hifi_libraries(shared)
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()

View file

@ -74,6 +74,10 @@ static void* getGlProcessAddress(const char *namez) {
#else #else
typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int *value);
PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC QueryCurrentRendererIntegerMESA;
static void* getGlProcessAddress(const char *namez) { static void* getGlProcessAddress(const char *namez) {
return (void*)glXGetProcAddressARB((const GLubyte*)namez); return (void*)glXGetProcAddressARB((const GLubyte*)namez);
} }
@ -92,6 +96,10 @@ void gl::initModuleGl() {
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB"); wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB");
#endif #endif
#if defined(Q_OS_LINUX)
QueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)getGlProcessAddress("glXQueryCurrentRendererIntegerMESA");
#endif
#if defined(USE_GLES) #if defined(USE_GLES)
gladLoadGLES2Loader(getGlProcessAddress); gladLoadGLES2Loader(getGlProcessAddress);
#else #else
@ -124,3 +132,14 @@ void gl::setSwapInterval(int interval) {
Q_UNUSED(interval); Q_UNUSED(interval);
#endif #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;
}

View file

@ -52,6 +52,7 @@ namespace gl {
void initModuleGl(); void initModuleGl();
int getSwapInterval(); int getSwapInterval();
void setSwapInterval(int swapInterval); void setSwapInterval(int swapInterval);
bool queryCurrentRendererIntegerMESA(int attr, unsigned int *value);
} }
#endif // hifi_gpu_GPUConfig_h #endif // hifi_gpu_GPUConfig_h

View file

@ -3,6 +3,6 @@ setup_hifi_library(Concurrent)
link_hifi_libraries(shared gl gpu shaders) link_hifi_libraries(shared gl gpu shaders)
GroupSources("src") GroupSources("src")
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()

View file

@ -10,11 +10,13 @@
// //
#include "GLBackend.h" #include "GLBackend.h"
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <list> #include <list>
#include <functional> #include <functional>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include "gl/Config.h"
#if defined(NSIGHT_FOUND) #if defined(NSIGHT_FOUND)
#include "nvToolsExt.h" #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 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_TEXTURE_IMAGE_UNITS{ 0 };
GLint GLBackend::MAX_UNIFORM_BUFFER_BINDINGS{ 0 }; GLint GLBackend::MAX_UNIFORM_BUFFER_BINDINGS{ 0 };
GLint GLBackend::MAX_COMBINED_UNIFORM_BLOCKS{ 0 }; GLint GLBackend::MAX_COMBINED_UNIFORM_BLOCKS{ 0 };
GLint GLBackend::MAX_COMBINED_TEXTURE_IMAGE_UNITS{ 0 }; GLint GLBackend::MAX_COMBINED_TEXTURE_IMAGE_UNITS{ 0 };
GLint GLBackend::MAX_UNIFORM_BLOCK_SIZE{ 0 }; GLint GLBackend::MAX_UNIFORM_BLOCK_SIZE{ 0 };
GLint GLBackend::UNIFORM_BUFFER_OFFSET_ALIGNMENT{ 1 }; 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() { void GLBackend::init() {
static std::once_flag once; static std::once_flag once;
@ -132,13 +148,59 @@ void GLBackend::init() {
GL_GET_INTEGER(MAX_UNIFORM_BLOCK_SIZE); GL_GET_INTEGER(MAX_UNIFORM_BLOCK_SIZE);
GL_GET_INTEGER(UNIFORM_BUFFER_OFFSET_ALIGNMENT); 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); LOG_GL_CONTEXT_INFO(gpugllogging, contextInfo);
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
// From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers. // From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers.
qCDebug(gpugllogging) << "GPU:"; qCDebug(gpugllogging) << "GPU:";
qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tcard:" << gpu->getName();
qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); 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) << "Limits:";
qCDebug(gpugllogging) << "\tmax textures:" << MAX_TEXTURE_IMAGE_UNITS; qCDebug(gpugllogging) << "\tmax textures:" << MAX_TEXTURE_IMAGE_UNITS;
qCDebug(gpugllogging) << "\tmax texture binding:" << MAX_COMBINED_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) { GLBackend::GLBackend(bool syncCache) {
_pipeline._cameraCorrectionBuffer._buffer->flush(); _pipeline._cameraCorrectionBuffer._buffer->flush();
initShaderBinaryCache(); initShaderBinaryCache();

View file

@ -67,6 +67,13 @@ protected:
GLBackend(); GLBackend();
public: public:
enum VideoCardType {
ATI,
NVIDIA,
MESA,
Unknown
};
#if defined(USE_GLES) #if defined(USE_GLES)
// https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGet.xhtml // https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGet.xhtml
static const GLint MIN_REQUIRED_TEXTURE_IMAGE_UNITS = 16; static const GLint MIN_REQUIRED_TEXTURE_IMAGE_UNITS = 16;
@ -89,6 +96,25 @@ public:
static GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS; static GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS;
static GLint MAX_UNIFORM_BLOCK_SIZE; static GLint MAX_UNIFORM_BLOCK_SIZE;
static GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT; 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(); virtual ~GLBackend();

View file

@ -19,6 +19,8 @@
#define MAX_RESOURCE_TEXTURES_PER_FRAME 2 #define MAX_RESOURCE_TEXTURES_PER_FRAME 2
#define NO_BUFFER_WORK_SLEEP_TIME_MS 2 #define NO_BUFFER_WORK_SLEEP_TIME_MS 2
#define THREADED_TEXTURE_BUFFERING 1 #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); 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() { void GLTextureTransferEngineDefault::updateMemoryPressure() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__); PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
bool useAvailableGlMemory = false;
size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
if (0 == allowedMemoryAllocation) { 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) // Clear any defunct textures (weak pointers that no longer have a valid texture)
@ -205,7 +223,7 @@ void GLTextureTransferEngineDefault::updateMemoryPressure() {
idealMemoryAllocation += texture->evalTotalSize(); idealMemoryAllocation += texture->evalTotalSize();
// Track how much we're actually using // Track how much we're actually using
totalVariableMemoryAllocation += gltexture->size(); totalVariableMemoryAllocation += gltexture->size();
if (vartexture->canDemote()) { if (!gltexture->_gpuObject.getImportant() && vartexture->canDemote()) {
canDemote |= true; canDemote |= true;
} }
if (vartexture->canPromote()) { if (vartexture->canPromote()) {
@ -218,7 +236,22 @@ void GLTextureTransferEngineDefault::updateMemoryPressure() {
Backend::textureResourceIdealGPUMemSize.set(idealMemoryAllocation); Backend::textureResourceIdealGPUMemSize.set(idealMemoryAllocation);
size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; 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 we're oversubscribed we need to demote textures IMMEDIATELY
if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) {
@ -470,7 +503,7 @@ void GLTextureTransferEngineDefault::processDemotes(size_t reliefRequired, const
for (const auto& texture : strongTextures) { for (const auto& texture : strongTextures) {
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture); GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture); GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
if (vargltexture->canDemote()) { if (!gltexture->_gpuObject.getImportant() && vargltexture->canDemote()) {
demoteQueue.push({ texture, (float)gltexture->size() }); demoteQueue.push({ texture, (float)gltexture->size() });
} }
} }

View file

@ -6,6 +6,6 @@ if (UNIX)
endif(UNIX) endif(UNIX)
GroupSources("src") GroupSources("src")
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()

View file

@ -571,6 +571,9 @@ public:
void setExternalRecycler(const ExternalRecycler& recycler); void setExternalRecycler(const ExternalRecycler& recycler);
ExternalRecycler getExternalRecycler() const; ExternalRecycler getExternalRecycler() const;
bool getImportant() const { return _important; }
void setImportant(bool important) { _important = important; }
const GPUObjectPointer gpuObject {}; const GPUObjectPointer gpuObject {};
ExternalUpdates getUpdates() const; ExternalUpdates getUpdates() const;
@ -632,6 +635,7 @@ protected:
bool _autoGenerateMips = false; bool _autoGenerateMips = false;
bool _isIrradianceValid = false; bool _isIrradianceValid = false;
bool _defined = 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); 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);

View file

@ -4,6 +4,7 @@
// //
// Created by Sam Gateau on 4/27/15. // Created by Sam Gateau on 4/27/15.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -36,13 +37,12 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputC
_inputDevice->_axisStateMap[MOUSE_AXIS_X].value = _lastCursor.x(); _inputDevice->_axisStateMap[MOUSE_AXIS_X].value = _lastCursor.x();
_inputDevice->_axisStateMap[MOUSE_AXIS_Y].value = _lastCursor.y(); _inputDevice->_axisStateMap[MOUSE_AXIS_Y].value = _lastCursor.y();
QPoint currentMove = _lastCursor - _previousCursor; updateDeltaAxisValue(MOUSE_AXIS_X_POS, _accumulatedMove.x() > 0 ? _accumulatedMove.x() : 0.0f);
updateDeltaAxisValue(MOUSE_AXIS_X_POS, currentMove.x() > 0 ? currentMove.x() : 0.0f); updateDeltaAxisValue(MOUSE_AXIS_X_NEG, _accumulatedMove.x() < 0 ? -_accumulatedMove.x() : 0.0f);
updateDeltaAxisValue(MOUSE_AXIS_X_NEG, currentMove.x() < 0 ? -currentMove.x() : 0.0f);
// Y mouse is inverted positive is pointing up the screen // 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_POS, _accumulatedMove.y() < 0 ? -_accumulatedMove.y() : 0.0f);
updateDeltaAxisValue(MOUSE_AXIS_Y_NEG, currentMove.y() > 0 ? currentMove.y() : 0.0f); updateDeltaAxisValue(MOUSE_AXIS_Y_NEG, _accumulatedMove.y() > 0 ? _accumulatedMove.y() : 0.0f);
_previousCursor = _lastCursor; _accumulatedMove = QPoint(0.0f, 0.0f);
}); });
// For touch event, we need to check that the last event is not too long ago // 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()); _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(); 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 // 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 // outside of the application window, because we don't get MouseEvents when the cursor is outside
// of the application window. // of the application window.

View file

@ -4,6 +4,7 @@
// //
// Created by Sam Gateau on 4/27/15. // Created by Sam Gateau on 4/27/15.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -79,7 +80,7 @@ public:
void keyPressEvent(QKeyEvent* event); void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event);
void mouseMoveEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event, bool capture, QPointF captureTarget);
void mousePressEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event);
void eraseMouseClicked(); void eraseMouseClicked();
@ -123,7 +124,7 @@ public:
protected: protected:
QPoint _lastCursor; QPoint _lastCursor;
QPoint _previousCursor; QPoint _accumulatedMove;
QPoint _mousePressPos; QPoint _mousePressPos;
quint64 _mousePressTime; quint64 _mousePressTime;
qreal _lastTotalScaleFactor; qreal _lastTotalScaleFactor;

View file

@ -43,10 +43,14 @@ AssetClient::AssetClient() {
auto nodeList = DependencyManager::get<LimitedNodeList>(); auto nodeList = DependencyManager::get<LimitedNodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssetMappingOperationReply, this, "handleAssetMappingOperationReply"); packetReceiver.registerListener(PacketType::AssetMappingOperationReply,
packetReceiver.registerListener(PacketType::AssetGetInfoReply, this, "handleAssetGetInfoReply"); PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetMappingOperationReply));
packetReceiver.registerListener(PacketType::AssetGetReply, this, "handleAssetGetReply", true); packetReceiver.registerListener(PacketType::AssetGetInfoReply,
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply"); 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::nodeKilled, this, &AssetClient::handleNodeKilled);
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset, connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,

View file

@ -76,9 +76,9 @@ const quint16 DOMAIN_SERVER_EXPORTER_PORT =
const quint16 DOMAIN_SERVER_METADATA_EXPORTER_PORT = const quint16 DOMAIN_SERVER_METADATA_EXPORTER_PORT =
QProcessEnvironment::systemEnvironment() QProcessEnvironment::systemEnvironment()
.contains("DOMAIN_SERVER_METADATA_EXPORTER_PORT") .contains("VIRCADIA_DOMAIN_SERVER_METADATA_EXPORTER_PORT")
? QProcessEnvironment::systemEnvironment() ? QProcessEnvironment::systemEnvironment()
.value("DOMAIN_SERVER_METADATA_EXPORTER_PORT") .value("VIRCADIA_DOMAIN_SERVER_METADATA_EXPORTER_PORT")
.toUInt() .toUInt()
: 9704; : 9704;

View file

@ -34,7 +34,8 @@ EntityScriptClient::EntityScriptClient() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); 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::nodeKilled, this, &EntityScriptClient::handleNodeKilled);
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset, connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,

View file

@ -13,6 +13,7 @@
#include <QtCore/QFile> #include <QtCore/QFile>
#include <QtCore/QFileSelector> #include <QtCore/QFileSelector>
#include <QtCore/QDateTime>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <StatTracker.h> #include <StatTracker.h>
@ -54,6 +55,7 @@ void FileResourceRequest::doSend() {
} else { } else {
QFile file(filename); QFile file(filename);
if (file.exists()) { if (file.exists()) {
setProperty("last-modified", toHttpDateString(QFileInfo(file).lastModified().toMSecsSinceEpoch()));
if (file.open(QFile::ReadOnly)) { if (file.open(QFile::ReadOnly)) {
if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) { if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) {

View file

@ -28,7 +28,8 @@ MessagesClient::MessagesClient() {
}); });
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver(); 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); connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);
} }

View file

@ -139,20 +139,34 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
startSTUNPublicSocketUpdate(); startSTUNPublicSocketUpdate();
auto& packetReceiver = getPacketReceiver(); auto& packetReceiver = getPacketReceiver();
packetReceiver.registerListener(PacketType::DomainList, this, "processDomainServerList"); packetReceiver.registerListener(PacketType::DomainList,
packetReceiver.registerListener(PacketType::Ping, this, "processPingPacket"); PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerList));
packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket"); packetReceiver.registerListener(PacketType::Ping,
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingPacket));
packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode"); packetReceiver.registerListener(PacketType::PingReply,
packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket"); PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingReplyPacket));
packetReceiver.registerListener(PacketType::DomainConnectionDenied, &_domainHandler, "processDomainServerConnectionDeniedPacket"); packetReceiver.registerListener(PacketType::ICEPing,
packetReceiver.registerListener(PacketType::DomainSettings, &_domainHandler, "processSettingsPacketList"); PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processICEPingPacket));
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket"); packetReceiver.registerListener(PacketType::DomainServerAddedNode,
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket"); PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerAddedNode));
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket"); packetReceiver.registerListener(PacketType::DomainServerConnectionToken,
packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse"); PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerConnectionTokenPacket));
packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode"); packetReceiver.registerListener(PacketType::DomainConnectionDenied,
packetReceiver.registerListener(PacketType::UsernameFromIDReply, this, "processUsernameFromIDReply"); 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) { qint64 NodeList::sendStats(QJsonObject statsObject, HifiSockAddr destination) {

View file

@ -12,7 +12,8 @@
#include "PacketReceiver.h" #include "PacketReceiver.h"
#include <QMutexLocker> #include <QtCore/QMetaObject>
#include <QtCore/QMutexLocker>
#include "DependencyManager.h" #include "DependencyManager.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
@ -25,85 +26,57 @@ PacketReceiver::PacketReceiver(QObject* parent) : QObject(parent) {
qRegisterMetaType<QSharedPointer<ReceivedMessage>>(); 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(!types.empty(), "PacketReceiver::registerListenerForTypes", "No types to register");
Q_ASSERT_X(listener, "PacketReceiver::registerListenerForTypes", "No object to register"); Q_ASSERT_X(listener, "PacketReceiver::registerListenerForTypes", "No listener to register");
Q_ASSERT_X(slot, "PacketReceiver::registerListenerForTypes", "No slot to register");
// Partition types based on whether they are sourced or not (non sourced in front) std::for_each(std::begin(types), std::end(types), [this, &listener](PacketType type) {
auto middle = std::partition(std::begin(types), std::end(types), [](PacketType type) { registerVerifiedListener(type, listener);
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);
}); });
return true; return true;
} }
void PacketReceiver::registerDirectListener(PacketType type, QObject* listener, const char* slot) { void PacketReceiver::registerDirectListener(PacketType type, const ListenerReferencePointer& listener) {
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListener", "No object to register"); Q_ASSERT_X(listener, "PacketReceiver::registerDirectListener", "No listener to register");
Q_ASSERT_X(slot, "PacketReceiver::registerDirectListener", "No slot to register");
bool success = registerListener(type, listener, slot); bool success = registerListener(type, listener);
if (success) { if (success) {
QMutexLocker locker(&_directConnectSetMutex); QMutexLocker locker(&_directConnectSetMutex);
// if we successfully registered, add this object to the set of objects that are directly connected // 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, void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener) {
QObject* listener, const char* slot) { Q_ASSERT_X(listener, "PacketReceiver::registerDirectListenerForTypes", "No listener to register");
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListenerForTypes", "No object to register");
Q_ASSERT_X(slot, "PacketReceiver::registerDirectListenerForTypes", "No slot to register");
// just call register listener for types to start // 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) { if (success) {
QMutexLocker locker(&_directConnectSetMutex); QMutexLocker locker(&_directConnectSetMutex);
// if we successfully registered, add this object to the set of objects that are directly connected // 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 PacketReceiver::registerListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending) {
bool deliverPending) { Q_ASSERT_X(listener, "PacketReceiver::registerListener", "No listener to register");
Q_ASSERT_X(listener, "PacketReceiver::registerListener", "No object to register");
Q_ASSERT_X(slot, "PacketReceiver::registerListener", "No slot 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; qCDebug(networking) << "Registering a packet listener for packet list type" << type;
registerVerifiedListener(type, listener, matchingMethod, deliverPending); registerVerifiedListener(type, listener, deliverPending);
return true; return true;
} else { } else {
qCWarning(networking) << "FAILED to Register a packet listener for packet list type" << type; 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 { bool PacketReceiver::matchingMethodForListener(PacketType type, const ListenerReferencePointer& listener) const {
Q_ASSERT_X(object, "PacketReceiver::matchingMethodForListener", "No object to call"); Q_ASSERT_X(listener, "PacketReceiver::matchingMethodForListener", "No listener to call");
Q_ASSERT_X(slot, "PacketReceiver::matchingMethodForListener", "No slot to call");
// normalize the slot with the expected parameters bool isSourced = listener->isSourced();
static const QString SIGNATURE_TEMPLATE("%1(%2)"); bool isNonSourcedPacket = PacketTypeEnum::getNonSourcedPackets().contains(type);
static const QString NON_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>";
QSet<QString> possibleSignatures { assert(!isSourced || !isNonSourcedPacket);
SIGNATURE_TEMPLATE.arg(slot, NON_SOURCED_MESSAGE_LISTENER_PARAMETERS) if (isSourced && isNonSourcedPacket) {
}; qCDebug(networking) << "PacketReceiver::registerListener cannot support a sourced listener for type" << type;
return false;
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);
} }
int methodIndex = -1; return true;
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();
}
} }
void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object, const QMetaMethod& slot, bool deliverPending) { void PacketReceiver::registerVerifiedListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending) {
Q_ASSERT_X(object, "PacketReceiver::registerVerifiedListener", "No object to register"); Q_ASSERT_X(listener, "PacketReceiver::registerVerifiedListener", "No listener to register");
QMutexLocker locker(&_packetListenerLock); QMutexLocker locker(&_packetListenerLock);
if (_messageListenerMap.contains(type)) { if (_messageListenerMap.contains(type)) {
@ -175,7 +109,7 @@ void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object,
} }
// add the mapping // add the mapping
_messageListenerMap[type] = { QPointer<QObject>(object), slot, deliverPending }; _messageListenerMap[type] = { listener, deliverPending };
} }
void PacketReceiver::unregisterListener(QObject* listener) { void PacketReceiver::unregisterListener(QObject* listener) {
@ -188,7 +122,7 @@ void PacketReceiver::unregisterListener(QObject* listener) {
auto it = _messageListenerMap.begin(); auto it = _messageListenerMap.begin();
while (it != _messageListenerMap.end()) { while (it != _messageListenerMap.end()) {
if (it.value().object == listener) { if (it.value().listener->getObject() == listener) {
it = _messageListenerMap.erase(it); it = _messageListenerMap.erase(it);
} else { } else {
++it; ++it;
@ -261,7 +195,7 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
QMutexLocker packetListenerLocker(&_packetListenerLock); QMutexLocker packetListenerLocker(&_packetListenerLock);
auto it = _messageListenerMap.find(receivedMessage->getType()); auto it = _messageListenerMap.find(receivedMessage->getType());
if (it != _messageListenerMap.end() && it->method.isValid()) { if (it != _messageListenerMap.end() && !it->listener.isNull()) {
auto listener = it.value(); auto listener = it.value();
@ -271,36 +205,19 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
bool success = false; bool success = false;
Qt::ConnectionType connectionType; bool isDirectConnect = false;
// check if this is a directly connected listener // check if this is a directly connected listener
{ {
QMutexLocker directConnectLocker(&_directConnectSetMutex); 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 // one final check on the QPointer before we go to invoke
if (listener.object) { if (listener.listener->getObject()) {
if (metaMethod.parameterTypes().contains(SHARED_NODE_NORMALIZED)) { if (isDirectConnect) {
success = metaMethod.invoke(listener.object, success = listener.listener->invokeDirectly(receivedMessage, matchingNode);
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));
} else { } else {
success = metaMethod.invoke(listener.object, success = listener.listener->invokeWithQt(receivedMessage, matchingNode);
connectionType,
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage));
} }
} else { } else {
qCDebug(networking).nospace() << "Listener for packet " << receivedMessage->getType() 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 // if it exists, remove the listener from _directlyConnectedObjects
{ {
QMutexLocker directConnectLocker(&_directConnectSetMutex); QMutexLocker directConnectLocker(&_directConnectSetMutex);
_directlyConnectedObjects.remove(listener.object); _directlyConnectedObjects.remove(listener.listener->getObject());
} }
} }
if (!success) { if (!success) {
qCDebug(networking).nospace() << "Error delivering packet " << receivedMessage->getType() << " to listener " qCDebug(networking).nospace() << "Error delivering packet " << receivedMessage->getType() << " to listener "
<< listener.object << "::" << qPrintable(listener.method.methodSignature()); << listener.listener->getObject();
} }
} else if (it == _messageListenerMap.end()) { } else if (it == _messageListenerMap.end()) {
qCWarning(networking) << "No listener found for packet type" << receivedMessage->getType(); qCWarning(networking) << "No listener found for packet type" << receivedMessage->getType();
// insert a dummy listener so we don't print this again // insert a dummy listener so we don't print this again
_messageListenerMap.insert(receivedMessage->getType(), { nullptr, QMetaMethod(), false }); _messageListenerMap.insert(receivedMessage->getType(), { ListenerReferencePointer(), false });
} }
} }

View file

@ -16,12 +16,12 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <QtCore/QMap>
#include <QtCore/QMetaMethod>
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QPointer> #include <QtCore/QPointer>
#include <QtCore/QSet> #include <QtCore/QSet>
#include <QtCore/QSharedPointer>
#include <QtCore/QEnableSharedFromThis>
#include "NLPacket.h" #include "NLPacket.h"
#include "NLPacketList.h" #include "NLPacketList.h"
@ -29,6 +29,7 @@
#include "udt/PacketHeaders.h" #include "udt/PacketHeaders.h"
class EntityEditPacketSender; class EntityEditPacketSender;
class Node;
class OctreePacketProcessor; class OctreePacketProcessor;
namespace std { namespace std {
@ -42,6 +43,22 @@ namespace std {
class PacketReceiver : public QObject { class PacketReceiver : public QObject {
Q_OBJECT 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: public:
using PacketTypeList = std::vector<PacketType>; 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 // 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 // been received. If deliverPending is true, ReceivedMessage will be delivered as soon as the first packet
// for the message is received. // for the message is received.
bool registerListener(PacketType type, QObject* listener, const char* slot, bool deliverPending = false); bool registerListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending = false);
bool registerListenerForTypes(PacketTypeList types, QObject* listener, const char* slot); bool registerListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener);
void unregisterListener(QObject* listener); void unregisterListener(QObject* listener);
void handleVerifiedPacket(std::unique_ptr<udt::Packet> packet); void handleVerifiedPacket(std::unique_ptr<udt::Packet> packet);
@ -64,9 +81,34 @@ public:
void handleMessageFailure(HifiSockAddr from, udt::Packet::MessageNumber messageNumber); void handleMessageFailure(HifiSockAddr from, udt::Packet::MessageNumber messageNumber);
private: 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 { struct Listener {
QPointer<QObject> object; ListenerReferencePointer listener;
QMetaMethod method;
bool deliverPending; bool deliverPending;
}; };
@ -74,11 +116,11 @@ private:
// these are brutal hacks for now - ideally GenericThread / ReceivedPacketProcessor // 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 // 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 registerDirectListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener);
void registerDirectListener(PacketType type, QObject* listener, const char* slot); void registerDirectListener(PacketType type, const ListenerReferencePointer& listener);
QMetaMethod matchingMethodForListener(PacketType type, QObject* object, const char* slot) const; bool matchingMethodForListener(PacketType type, const ListenerReferencePointer& listener) const;
void registerVerifiedListener(PacketType type, QObject* listener, const QMetaMethod& slot, bool deliverPending = false); void registerVerifiedListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending = false);
QMutex _packetListenerLock; QMutex _packetListenerLock;
QHash<PacketType, Listener> _messageListenerMap; QHash<PacketType, Listener> _messageListenerMap;
@ -93,4 +135,42 @@ private:
friend class OctreePacketProcessor; 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 #endif // hifi_PacketReceiver_h

View file

@ -16,8 +16,9 @@
#include <cmath> #include <cmath>
#include <assert.h> #include <assert.h>
#include <QThread> #include <QtCore/QMetaMethod>
#include <QTimer> #include <QtCore/QThread>
#include <QtCore/QTimer>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <shared/QtHelpers.h> #include <shared/QtHelpers.h>

View file

@ -15,8 +15,14 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include <StatTracker.h> #include <StatTracker.h>
#include <QtCore/QDateTime>
#include <QtCore/QThread> #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() { void ResourceRequest::send() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {

View file

@ -90,6 +90,7 @@ public:
void setCacheEnabled(bool value) { _cacheEnabled = value; } void setCacheEnabled(bool value) { _cacheEnabled = value; }
void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } void setByteRange(ByteRange byteRange) { _byteRange = byteRange; }
static QString toHttpDateString(uint64_t msecsSinceEpoch);
public slots: public slots:
void send(); void send();

View file

@ -64,7 +64,8 @@ void OctreePersistThread::start() {
cleanupOldReplacementBackups(); cleanupOldReplacementBackups();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); 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>(); auto nodeList = DependencyManager::get<NodeList>();
const DomainHandler& domainHandler = nodeList->getDomainHandler(); const DomainHandler& domainHandler = nodeList->getDomainHandler();

View file

@ -3,5 +3,5 @@ setup_hifi_library(Multimedia Network Qml Quick WebChannel WebSockets ${PLATFORM
link_hifi_libraries(shared networking gl) link_hifi_libraries(shared networking gl)
# Required for some low level GL interaction in the OffscreenQMLSurface # Required for some low level GL interaction in the OffscreenQMLSurface
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()

View file

@ -260,6 +260,7 @@ void Font::read(QIODevice& in) {
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR)); gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
_texture->setStoredMipFormat(formatMip); _texture->setStoredMipFormat(formatMip);
_texture->assignStoredMip(0, image.sizeInBytes(), image.constBits()); _texture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
_texture->setImportant(true);
} }
void Font::setupGPU() { void Font::setupGPU() {

View file

@ -85,11 +85,25 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
Lock lock(_containerLock); Lock lock(_containerLock);
if (_scriptCache.contains(url) && !forceDownload) { if (_scriptCache.contains(url) && !forceDownload) {
auto scriptContent = _scriptCache[url]; auto entry = _scriptCache[url];
lock.unlock(); if (url.isLocalFile() || url.scheme().isEmpty()) {
qCDebug(scriptengine) << "Found script in cache:" << url.fileName(); auto modifiedTime = QFileInfo(url.toLocalFile()).lastModified();
contentAvailable(url.toString(), scriptContent, true, true, STATUS_CACHED); QString localTime = ResourceRequest::toHttpDateString(modifiedTime.toMSecsSinceEpoch());
} else { 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]; auto& scriptRequest = _activeScriptRequests[url];
bool alreadyWaiting = scriptRequest.scriptUsers.size() > 0; bool alreadyWaiting = scriptRequest.scriptUsers.size() > 0;
scriptRequest.scriptUsers.push_back(contentAvailable); scriptRequest.scriptUsers.push_back(contentAvailable);
@ -140,7 +154,10 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
_activeScriptRequests.remove(url); _activeScriptRequests.remove(url);
_scriptCache[url] = scriptContent = req->getData(); _scriptCache[url] = {
{ "data", scriptContent = req->getData() },
{ "last-modified", req->property("last-modified") },
};
} else { } else {
auto result = req->getResult(); auto result = req->getResult();
bool irrecoverable = bool irrecoverable =
@ -177,7 +194,7 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
allCallbacks = scriptRequest.scriptUsers; allCallbacks = scriptRequest.scriptUsers;
if (_scriptCache.contains(url)) { if (_scriptCache.contains(url)) {
scriptContent = _scriptCache[url]; scriptContent = _scriptCache[url]["data"].toString();
} }
_activeScriptRequests.remove(url); _activeScriptRequests.remove(url);
qCWarning(scriptengine) << "Error loading script from URL (" << status <<")"; qCWarning(scriptengine) << "Error loading script from URL (" << status <<")";

View file

@ -60,7 +60,7 @@ private:
Mutex _containerLock; Mutex _containerLock;
QMap<QUrl, ScriptRequest> _activeScriptRequests; QMap<QUrl, ScriptRequest> _activeScriptRequests;
QHash<QUrl, QString> _scriptCache; QHash<QUrl, QVariantMap> _scriptCache;
QMultiMap<QUrl, ScriptUser*> _scriptUsers; QMultiMap<QUrl, ScriptUser*> _scriptUsers;
}; };

View file

@ -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. // 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 // Note that the first entry in the backtrace should be "print" and is somewhat useless to us
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get(); AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
if (loggerInterface->showSourceDebugging()) { if (loggerInterface && loggerInterface->showSourceDebugging()) {
QScriptContext* userContext = context; QScriptContext* userContext = context;
while (userContext && QScriptContextInfo(userContext).functionType() == QScriptContextInfo::NativeFunction) { while (userContext && QScriptContextInfo(userContext).functionType() == QScriptContextInfo::NativeFunction) {
userContext = userContext->parentContext(); userContext = userContext->parentContext();

View file

@ -17,6 +17,10 @@
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>
#endif #endif
#ifdef Q_OS_UNIX
#include <stdio.h>
#include <unistd.h>
#endif
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
@ -32,6 +36,40 @@ LogHandler& LogHandler::getInstance() {
return staticInstance; 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) { const char* stringForLogType(LogMsgType msgType) {
switch (msgType) { switch (msgType) {
case LogInfo: 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 // the following will produce 11/18 13:55:36
const QString DATE_STRING_FORMAT = "MM/dd hh:mm:ss"; 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 + " ")); 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 #ifdef Q_OS_WIN
// On windows, this will output log lines into the Visual Studio "output" tab // On windows, this will output log lines into the Visual Studio "output" tab
OutputDebugStringA(qPrintable(logMessage)); OutputDebugStringA(qPrintable(logMessage));

View file

@ -57,7 +57,7 @@ public:
void setupRepeatedMessageFlusher(); void setupRepeatedMessageFlusher();
private: private:
LogHandler() = default; LogHandler();
~LogHandler() = default; ~LogHandler() = default;
void flushRepeatedMessages(); void flushRepeatedMessages();
@ -66,6 +66,7 @@ private:
bool _shouldOutputProcessID { false }; bool _shouldOutputProcessID { false };
bool _shouldOutputThreadID { false }; bool _shouldOutputThreadID { false };
bool _shouldDisplayMilliseconds { false }; bool _shouldDisplayMilliseconds { false };
bool _useColor { false };
int _currentMessageID { 0 }; int _currentMessageID { 0 };
struct RepeatedMessageRecord { struct RepeatedMessageRecord {

View file

@ -3,6 +3,7 @@
// interface/src // interface/src
// //
// Copyright 2013 High Fidelity, Inc. // Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -43,6 +44,8 @@ class Camera : public QObject {
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
Q_PROPERTY(QString mode READ getModeString WRITE setModeString NOTIFY modeUpdated) Q_PROPERTY(QString mode READ getModeString WRITE setModeString NOTIFY modeUpdated)
Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT) 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: public:
Camera(); Camera();
@ -110,6 +113,37 @@ public slots:
*/ */
void setOrientation(const glm::quat& orientation); 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 /**jsdoc
* Computes a {@link PickRay} based on the current camera configuration and the specified <code>x, y</code> position on the * 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 * 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); 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: private:
void recompose(); void recompose();
void decompose(); void decompose();
@ -194,6 +242,9 @@ private:
glm::quat _orientation; glm::quat _orientation;
bool _isKeepLookingAt{ false }; bool _isKeepLookingAt{ false };
glm::vec3 _lookingAt; glm::vec3 _lookingAt;
bool _captureMouse { false };
float _sensitivity { 1.0f };
}; };
#endif // hifi_Camera_h #endif // hifi_Camera_h

View file

@ -4,5 +4,5 @@ link_hifi_libraries(shared networking qml gl audio audio-client plugins pointers
include_hifi_library_headers(controllers) include_hifi_library_headers(controllers)
# Required for some low level GL interaction in the OffscreenQMLSurface # Required for some low level GL interaction in the OffscreenQMLSurface
set(OpenGL_GL_PREFERENCE "GLVND") set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl() target_opengl()

View file

@ -172,31 +172,31 @@ public:
QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 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 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 = 0); 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 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 = 0); 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 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 = 0); 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 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 = 0); 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 // 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 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 = 0); 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 // 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 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 = 0); 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 // 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 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 = 0); 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 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 = 0); 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 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()); 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 // Compatibility with QInputDialog::getText
static QString getText(void* ignored, const QString & title, const QString & label, static QString getText(void* ignored, const QString & title, const QString & label,
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, 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); return getText(OffscreenUi::ICON_NONE, title, label, text, ok);
} }
// Compatibility with QInputDialog::getItem // Compatibility with QInputDialog::getItem
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, 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) { Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok); return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok);
} }
@ -222,12 +222,12 @@ public:
// Compatibility with QInputDialog::getText // Compatibility with QInputDialog::getText
static ModalDialogListener* getTextAsync(void* ignored, const QString & title, const QString & label, static ModalDialogListener* getTextAsync(void* ignored, const QString & title, const QString & label,
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, 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); return getTextAsync(OffscreenUi::ICON_NONE, title, label, text);
} }
// Compatibility with QInputDialog::getItem // Compatibility with QInputDialog::getItem
static ModalDialogListener* getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items, 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) { Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable); return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable);
} }

View file

@ -5,10 +5,12 @@
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com // Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
// Copyright 2019 Kasen IO // Copyright 2019 Kasen IO
// //
// Authored by: Humbletim (humbletim@gmail.com)
//
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
// Supporting file containing all QtScript specific integration. // Supporting file containing all QtScript specific integration.
#ifndef EXAMPLE_SCRIPT_PLUGIN_H #ifndef EXAMPLE_SCRIPT_PLUGIN_H
#define EXAMPLE_SCRIPT_PLUGIN_H #define EXAMPLE_SCRIPT_PLUGIN_H

View file

@ -5,10 +5,12 @@
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com // Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
// Copyright 2019 Kasen IO // Copyright 2019 Kasen IO
// //
// Authored by: Humbletim (humbletim@gmail.com)
//
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
// 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" #include "ExampleScriptPlugin.h"

View file

@ -43,7 +43,7 @@
lazy-validation> lazy-validation>
<v-row> <v-row>
<v-col cols="12" sm="6" md="4"> <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>
<v-col cols="12" sm="6" md="4"> <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> <v-text-field v-model="owner" :rules="ownerRules" label="Owner *" hint="Owner name to show in listing" required persistent-hint></v-text-field>

View file

@ -2,9 +2,7 @@
// //
// Created by Darlingnotin in 2019. // Created by Darlingnotin in 2019.
// Copyright 2019 Darlingnotin // Copyright 2019 Darlingnotin
// // Copyright 2020 Vircadia contributors.
// App maintained in: https://github.com/kasenvr/Decentralized_GoTo_Experimental
// App copied to: https://github.com/kasenvr/project-athena
// //
// Distributed under the ISC license. // Distributed under the ISC license.
// See the accompanying file LICENSE or https://opensource.org/licenses/ISC // See the accompanying file LICENSE or https://opensource.org/licenses/ISC

View file

@ -35,7 +35,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/audioMuteOverlay.js", "system/audioMuteOverlay.js",
"system/inspect.js", "system/inspect.js",
"system/keyboardShortcuts/keyboardShortcuts.js", "system/keyboardShortcuts/keyboardShortcuts.js",
"system/checkForUpdates.js" "system/checkForUpdates.js",
"system/onFirstRun.js"
]; ];
var DEFAULT_SCRIPTS_SEPARATE = [ var DEFAULT_SCRIPTS_SEPARATE = [
"system/controllers/controllerScripts.js", "system/controllers/controllerScripts.js",

View file

@ -16,7 +16,7 @@ var FRAME_RATE = 30; // 30 default
function getFrame(callback) { function getFrame(callback) {
// A model exported from blender with a texture named 'Picture' on one face. // 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); var model = ModelCache.prefetch(FRAME_URL);
if (model.state === Resource.State.FINISHED) { if (model.state === Resource.State.FINISHED) {

View 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;
})();

View file

@ -34,7 +34,8 @@ Script.include([
"../libraries/entityIconOverlayManager.js", "../libraries/entityIconOverlayManager.js",
"../libraries/gridTool.js", "../libraries/gridTool.js",
"entityList/entityList.js", "entityList/entityList.js",
"entitySelectionTool/entitySelectionTool.js" "entitySelectionTool/entitySelectionTool.js",
"audioFeedback/audioFeedback.js"
]); ]);
var CreateWindow = Script.require('./modules/createWindow.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 cameraManager = new CameraManager();
var grid = new Grid(); var grid = new Grid();
@ -824,7 +827,7 @@ var toolBar = (function () {
HMD.displayModeChanged.connect(function() { HMD.displayModeChanged.connect(function() {
if (isActive) { if (isActive) {
tablet.gotoHomeScreen(); tablet.gotoHomeScreen();
} }
that.setActive(false); that.setActive(false);
}); });
@ -1131,7 +1134,11 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) {
var entity = entityIconOverlayManager.findEntity(data.overlayID); var entity = entityIconOverlayManager.findEntity(data.overlayID);
if (entity !== null) { 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() { function unparentSelectedEntities() {
if (SelectionManager.hasSelection()) { if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
var selectedEntities = selectionManager.selections; var selectedEntities = selectionManager.selections;
var parentCheck = false; var parentCheck = false;
if (selectedEntities.length < 1) { if (selectedEntities.length < 1) {
audioFeedback.rejection();
Window.notifyEditError("You must have an entity selected in order to unparent it."); Window.notifyEditError("You must have an entity selected in order to unparent it.");
return; return;
} }
@ -1706,12 +1714,17 @@ function unparentSelectedEntities() {
return true; return true;
}); });
if (parentCheck) { if (parentCheck) {
audioFeedback.confirmation();
if (selectedEntities.length > 1) { if (selectedEntities.length > 1) {
Window.notify("Entities unparented"); Window.notify("Entities unparented");
} else { } else {
Window.notify("Entity unparented"); Window.notify("Entity unparented");
} }
//Refresh
entityListTool.sendUpdate();
selectionManager._update(false, this);
} else { } else {
audioFeedback.rejection();
if (selectedEntities.length > 1) { if (selectedEntities.length > 1) {
Window.notify("Selected Entities have no parents"); Window.notify("Selected Entities have no parents");
} else { } else {
@ -1719,13 +1732,15 @@ function unparentSelectedEntities() {
} }
} }
} else { } 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() { function parentSelectedEntities() {
if (SelectionManager.hasSelection()) { if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
var selectedEntities = selectionManager.selections; var selectedEntities = selectionManager.selections;
if (selectedEntities.length <= 1) { if (selectedEntities.length <= 1) {
audioFeedback.rejection();
Window.notifyEditError("You must have multiple entities selected in order to parent them"); Window.notifyEditError("You must have multiple entities selected in order to parent them");
return; return;
} }
@ -1742,16 +1757,22 @@ function parentSelectedEntities() {
}); });
if (parentCheck) { if (parentCheck) {
audioFeedback.confirmation();
Window.notify("Entities parented"); Window.notify("Entities parented");
//Refresh
entityListTool.sendUpdate();
selectionManager._update(false, this);
} else { } else {
audioFeedback.rejection();
Window.notify("Entities are already parented to last"); Window.notify("Entities are already parented to last");
} }
} else { } 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() { function deleteSelectedEntities() {
if (SelectionManager.hasSelection()) { if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
var deletedIDs = []; var deletedIDs = [];
SelectionManager.saveProperties(); SelectionManager.saveProperties();
@ -1782,6 +1803,9 @@ function deleteSelectedEntities() {
pushCommandForSelections([], savedProperties); pushCommandForSelections([], savedProperties);
entityListTool.deleteEntities(deletedIDs); 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) { function updateSelections(selectionUpdated, caller) {
if (HMD.active && visible) {
webView.setLandscape(true);
} else {
if (!visible) {
hmdMultiSelectMode = false;
webView.setLandscape(false);
}
}
if (blockPropertyUpdates) { if (blockPropertyUpdates) {
return; return;
} }
@ -2939,4 +2972,22 @@ function zoneSortOrder(a, b) {
return 0; 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 }()); // END LOCAL_SCOPE

View file

@ -3,6 +3,7 @@
// entityList.js // entityList.js
// //
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -14,6 +15,7 @@
var PROFILING_ENABLED = false; var PROFILING_ENABLED = false;
var profileIndent = ''; var profileIndent = '';
const PROFILE_NOOP = function(_name, fn, args) { const PROFILE_NOOP = function(_name, fn, args) {
fn.apply(this, args); fn.apply(this, args);
}; };
@ -73,7 +75,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
that.setVisible = function(newVisible) { that.setVisible = function(newVisible) {
visible = newVisible; visible = newVisible;
webView.setVisible(shouldUseEditTabletApp() && visible); webView.setVisible(shouldUseEditTabletApp() && visible);
entityListWindow.setVisible(!shouldUseEditTabletApp() && visible); entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);
}; };
that.isVisible = function() { that.isVisible = function() {
@ -163,6 +165,15 @@ EntityListTool = function(shouldUseEditTabletApp) {
} }
that.sendUpdate = function() { 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() { PROFILE('Script-sendUpdate', function() {
var entities = []; var entities = [];
@ -179,7 +190,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
PROFILE("getMultipleProperties", function () { PROFILE("getMultipleProperties", function () {
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked', var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked',
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID', '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++) { for (var i = 0; i < multipleProperties.length; i++) {
var properties = multipleProperties[i]; var properties = multipleProperties[i];
@ -192,6 +203,17 @@ EntityListTool = function(shouldUseEditTabletApp) {
} else if (properties.type === "Image") { } else if (properties.type === "Image") {
url = properties.imageURL; 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({ entities.push({
id: ids[i], id: ids[i],
name: properties.name, name: properties.name,
@ -211,7 +233,10 @@ EntityListTool = function(shouldUseEditTabletApp) {
isBaked: entityIsBaked(properties), isBaked: entityIsBaked(properties),
drawCalls: (properties.renderInfo !== undefined ? drawCalls: (properties.renderInfo !== undefined ?
valueIfDefined(properties.renderInfo.drawCalls) : ""), 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) { function onFileSaveChanged(filename) {
Window.saveFileChanged.disconnect(onFileSaveChanged); Window.saveFileChanged.disconnect(onFileSaveChanged);
if (filename !== "") { if (filename !== "") {
@ -302,6 +343,34 @@ EntityListTool = function(shouldUseEditTabletApp) {
SelectionDisplay.toggleSpaceMode(); SelectionDisplay.toggleSpaceMode();
} else if (data.type === 'keyUpEvent') { } else if (data.type === 'keyUpEvent') {
keyUpEventFromUIWindow(data.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();
} }
}; };

View file

@ -3,6 +3,7 @@
// //
// Created by Ryan Huffman on 19 Nov 2014 // Created by Ryan Huffman on 19 Nov 2014
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -30,7 +31,9 @@
<input type="button" id="visible" class="glyph" value="&#xe007;" /> <input type="button" id="visible" class="glyph" value="&#xe007;" />
</div> </div>
<button id="toggle-space-mode" class="hifi-edit-button space-mode-local">Local</button> <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>
<div id="entity-list"> <div id="entity-list">
<div id="filter-area"> <div id="filter-area">
@ -88,5 +91,142 @@
</div> </div>
</div> </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> </body>
</html> </html>

View file

@ -2,6 +2,7 @@
// //
// Created by Ryan Huffman on 19 Nov 2014 // Created by Ryan Huffman on 19 Nov 2014
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -46,6 +47,14 @@ const COLUMNS = {
alwaysShown: true, alwaysShown: true,
defaultSortOrder: ASCENDING_SORT, defaultSortOrder: ASCENDING_SORT,
}, },
parentState: {
columnHeader: "A",
vglyph: true,
dropdownLabel: "Hierarchy",
propertyID: "parentState",
initialWidth: 0.08,
defaultSortOrder: DESCENDING_SORT,
},
name: { name: {
columnHeader: "Name", columnHeader: "Name",
propertyID: "name", propertyID: "name",
@ -133,6 +142,20 @@ const COLUMNS = {
initialWidth: 0.06, initialWidth: 0.06,
defaultSortOrder: DESCENDING_SORT, 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 = [ const FILTER_TYPES = [
@ -164,6 +187,7 @@ let selectedEntities = [];
let entityList = null; // The ListView let entityList = null; // The ListView
let hmdMultiSelectMode = false;
/** /**
* @type EntityListContextMenu * @type EntityListContextMenu
*/ */
@ -198,7 +222,31 @@ let elEntityTable,
elRefresh, elRefresh,
elToggleLocked, elToggleLocked,
elToggleVisible, elToggleVisible,
elActionsMenu,
elSelectionMenu,
elMenuBackgroundOverlay,
elHmdMultiSelect,
elHmdCopy,
elHmdCut,
elHmdPaste,
elHmdDuplicate,
elUndo,
elRedo,
elParent,
elUnparent,
elDelete, elDelete,
elMoveEntitySelectionToAvatar,
elSelectAll,
elSelectInverse,
elSelectNone,
elSelectAllInBox,
elSelectAllTouchingBox,
elSelectParent,
elSelectTopParent,
elAddChildrenToSelection,
elSelectFamily,
elSelectTopFamily,
elTeleportToEntity,
elFilterTypeMultiselectBox, elFilterTypeMultiselectBox,
elFilterTypeText, elFilterTypeText,
elFilterTypeOptions, elFilterTypeOptions,
@ -233,7 +281,7 @@ const PROFILE = !ENABLE_PROFILING ? PROFILE_NOOP : function(name, fn, args) {
console.log("PROFILE-Web " + profileIndent + "(" + name + ") End " + delta + "ms"); console.log("PROFILE-Web " + profileIndent + "(" + name + ") End " + delta + "ms");
}; };
function loaded() { function loaded() {
openEventBridge(function() { openEventBridge(function() {
elEntityTable = document.getElementById("entity-table"); elEntityTable = document.getElementById("entity-table");
elEntityTableHeader = document.getElementById("entity-table-header"); elEntityTableHeader = document.getElementById("entity-table-header");
@ -241,8 +289,32 @@ function loaded() {
elEntityTableScroll = document.getElementById("entity-table-scroll"); elEntityTableScroll = document.getElementById("entity-table-scroll");
elRefresh = document.getElementById("refresh"); elRefresh = document.getElementById("refresh");
elToggleLocked = document.getElementById("locked"); 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"); 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"); elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box");
elFilterTypeText = document.getElementById("filter-type-text"); elFilterTypeText = document.getElementById("filter-type-text");
elFilterTypeOptions = document.getElementById("filter-type-options"); elFilterTypeOptions = document.getElementById("filter-type-options");
@ -270,8 +342,156 @@ function loaded() {
elExport.onclick = function() { elExport.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'export'})); 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() { elDelete.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); 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() { elToggleSpaceMode.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleSpaceMode' })); EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleSpaceMode' }));
@ -330,9 +550,12 @@ function loaded() {
elTh.setAttribute("id", thID); elTh.setAttribute("id", thID);
elTh.setAttribute("columnIndex", columnIndex); elTh.setAttribute("columnIndex", columnIndex);
elTh.setAttribute("columnID", columnID); elTh.setAttribute("columnID", columnID);
if (columnData.glyph) { if (columnData.glyph || columnData.vglyph) {
let elGlyph = document.createElement("span"); let elGlyph = document.createElement("span");
elGlyph.className = "glyph"; elGlyph.className = "glyph";
if (columnData.vglyph) {
elGlyph.className = "vglyph";
}
elGlyph.innerHTML = columnData.columnHeader; elGlyph.innerHTML = columnData.columnHeader;
elTh.appendChild(elGlyph); elTh.appendChild(elGlyph);
} else { } else {
@ -538,7 +761,7 @@ function loaded() {
let selection = [entityID]; let selection = [entityID];
let controlKey = window.navigator.platform.startsWith("Mac") ? clickEvent.metaKey : clickEvent.ctrlKey; let controlKey = window.navigator.platform.startsWith("Mac") ? clickEvent.metaKey : clickEvent.ctrlKey;
if (controlKey) { if (controlKey || hmdMultiSelectMode) {
let selectedIndex = selectedEntities.indexOf(entityID); let selectedIndex = selectedEntities.indexOf(entityID);
if (selectedIndex >= 0) { if (selectedIndex >= 0) {
selection = []; selection = [];
@ -632,10 +855,13 @@ function loaded() {
isBaked: entity.isBaked, isBaked: entity.isBaked,
drawCalls: displayIfNonZero(entity.drawCalls), drawCalls: displayIfNonZero(entity.drawCalls),
hasScript: entity.hasScript, 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 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 selected: false // if this entity is selected for edit regardless of having a visible row
}; };
entities.push(entityData); entities.push(entityData);
entitiesByID[entityData.id] = entityData; entitiesByID[entityData.id] = entityData;
}); });
@ -817,7 +1043,7 @@ function loaded() {
function updateSelectedEntities(selectedIDs, autoScroll) { function updateSelectedEntities(selectedIDs, autoScroll) {
let notFound = false; let notFound = false;
// reset all currently selected entities and their rows first // reset all currently selected entities and their rows first
selectedEntities.forEach(function(id) { selectedEntities.forEach(function(id) {
let entity = entitiesByID[id]; let entity = entitiesByID[id];
@ -828,7 +1054,7 @@ function loaded() {
} }
} }
}); });
// then reset selected entities list with newly selected entities and set them selected // then reset selected entities list with newly selected entities and set them selected
selectedEntities = []; selectedEntities = [];
selectedIDs.forEach(function(id) { selectedIDs.forEach(function(id) {
@ -891,6 +1117,8 @@ function loaded() {
let elCell = elRow.childNodes[i]; let elCell = elRow.childNodes[i];
if (column.data.glyph) { if (column.data.glyph) {
elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null; elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null;
} else if (column.data.vglyph) {
elCell.innerHTML = itemData[column.data.propertyID];
} else { } else {
let value = itemData[column.data.propertyID]; let value = itemData[column.data.propertyID];
if (column.data.format) { if (column.data.format) {
@ -978,6 +1206,9 @@ function loaded() {
let column = columnsByID[columnID]; let column = columnsByID[columnID];
let visible = column.elTh.style.visibility !== "hidden"; let visible = column.elTh.style.visibility !== "hidden";
let className = column.data.glyph ? "glyph" : ""; let className = column.data.glyph ? "glyph" : "";
if (column.data.vglyph) {
className = "vglyph";
}
className += visible ? "" : " hidden"; className += visible ? "" : " hidden";
return className; return className;
} }
@ -1327,7 +1558,7 @@ function loaded() {
break; break;
} }
if (controlKey && keyCodeString === "A") { if (controlKey && !shiftKey && !altKey && keyCodeString === "A") {
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id); let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => { let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
return selectedEntities.includes(visibleEntityID); return selectedEntities.includes(visibleEntityID);
@ -1350,6 +1581,32 @@ function loaded() {
return; 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({ EventBridge.emitWebEvent(JSON.stringify({
type: 'keyUpEvent', type: 'keyUpEvent',
@ -1364,10 +1621,12 @@ function loaded() {
} }
})); }));
}, false); }, false);
if (window.EventBridge !== undefined) { if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) { EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data); data = JSON.parse(data);
if (data.type === "clearEntityList") { if (data.type === "clearEntityList") {
clearEntities(); clearEntities();
} else if (data.type === "selectionUpdate") { } else if (data.type === "selectionUpdate") {
@ -1395,6 +1654,12 @@ function loaded() {
removeEntities(data.ids); removeEntities(data.ids);
} else if (data.type === "setSpaceMode") { } else if (data.type === "setSpaceMode") {
setSpaceMode(data.spaceMode); 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() { $(window).blur(function() {
entityListContextMenu.close(); entityListContextMenu.close();
}); });
function closeAllEntityListMenu() {
document.getElementById("menuBackgroundOverlay").style.display = "none";
document.getElementById("selection-menu").style.display = "none";
document.getElementById("actions-menu").style.display = "none";
}
} }

View file

@ -1618,7 +1618,8 @@ const GROUPS = [
type: "vec3", type: "vec3",
vec3Type: "pyr", vec3Type: "pyr",
multiplier: DEGREES_TO_RADIANS, multiplier: DEGREES_TO_RADIANS,
decimals: 4, decimals: 6,
step: 1,
subLabels: [ "x", "y", "z" ], subLabels: [ "x", "y", "z" ],
unit: "deg/s", unit: "deg/s",
propertyID: "localAngularVelocity", propertyID: "localAngularVelocity",

View file

@ -19,6 +19,7 @@
const SPACE_LOCAL = "local"; const SPACE_LOCAL = "local";
const SPACE_WORLD = "world"; const SPACE_WORLD = "world";
const HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; const HIGHLIGHT_LIST_NAME = "editHandleHighlightList";
const MIN_DISTANCE_TO_REZ_FROM_AVATAR = 3;
Script.include([ Script.include([
"../../libraries/controllers.js", "../../libraries/controllers.js",
@ -26,7 +27,6 @@ Script.include([
"../../libraries/utils.js" "../../libraries/utils.js"
]); ]);
function deepCopy(v) { function deepCopy(v) {
return JSON.parse(JSON.stringify(v)); return JSON.parse(JSON.stringify(v));
} }
@ -103,7 +103,11 @@ SelectionManager = (function() {
if (wantDebug) { if (wantDebug) {
print("setting selection to " + messageParsed.entityID); 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") { } else if (messageParsed.method === "clearSelection") {
if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) {
@ -314,6 +318,7 @@ SelectionManager = (function() {
that.addChildrenEntities(originalEntityID, entitiesToDuplicate, entityHostTypes[i].entityHostType); 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 // 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++) { for (var i = 0; i < entitiesToDuplicate.length; i++) {
var originalEntityID = entitiesToDuplicate[i]; var originalEntityID = entitiesToDuplicate[i];
@ -360,6 +365,8 @@ SelectionManager = (function() {
duplicatedChildrenWithOldParents[newEntityID] = properties.parentID; duplicatedChildrenWithOldParents[newEntityID] = properties.parentID;
} }
originalEntityToNewEntityID[originalEntityID] = newEntityID; originalEntityToNewEntityID[originalEntityID] = newEntityID;
} else {
duplicateInterrupted = true;
} }
} }
@ -378,6 +385,11 @@ SelectionManager = (function() {
} }
}); });
if (duplicateInterrupted) {
audioFeedback.rejection();
} else {
audioFeedback.confirmation();
}
return duplicatedEntityIDs; 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; return that;
})(); })();
@ -648,8 +795,10 @@ SelectionDisplay = (function() {
const COLOR_HOVER = { red: 255, green: 220, blue: 82 }; const COLOR_HOVER = { red: 255, green: 220, blue: 82 };
const COLOR_DUPLICATOR = { red: 162, green: 0, blue: 255 }; const COLOR_DUPLICATOR = { red: 162, green: 0, blue: 255 };
const COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 }; const COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 };
const COLOR_BOUNDING_EDGE = { red: 128, green: 128, blue: 128 }; const COLOR_BOUNDING_EDGE = { red: 160, green: 160, blue: 160 };
const COLOR_SCALE_CUBE = { 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 = { red: 255, green: 255, blue: 255 };
const COLOR_DEBUG_PICK_PLANE_HIT = { red: 255, green: 165, blue: 0 }; const COLOR_DEBUG_PICK_PLANE_HIT = { red: 255, green: 165, blue: 0 };
@ -1779,6 +1928,18 @@ SelectionDisplay = (function() {
var rotationZ = Quat.multiply(rotation, localRotationZ); var rotationZ = Quat.multiply(rotation, localRotationZ);
worldRotationZ = rotationZ; 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 = { var selectionBoxGeometry = {
position: position, position: position,
rotation: rotation, rotation: rotation,
@ -1900,6 +2061,7 @@ SelectionDisplay = (function() {
Entities.editEntity(handleBoundingBox, { Entities.editEntity(handleBoundingBox, {
position: position, position: position,
rotation: rotation, rotation: rotation,
color: handleBoundingBoxColor,
dimensions: dimensions dimensions: dimensions
}); });

View file

@ -55,18 +55,18 @@ TabBar {
font.pixelSize: 14 font.pixelSize: 14
font.bold: true font.bold: true
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 28 anchors.topMargin: 30
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 28 anchors.leftMargin: 30
} }
Flow { Flow {
id: createEntitiesFlow id: createEntitiesFlow
spacing: 35 spacing: 20
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 55 anchors.rightMargin: 30
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 55 anchors.leftMargin: 30
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 70 anchors.topMargin: 70
@ -186,9 +186,9 @@ TabBar {
color: hifi.buttons.black color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 55 anchors.rightMargin: 30
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 55 anchors.leftMargin: 30
anchors.top: createEntitiesFlow.bottom anchors.top: createEntitiesFlow.bottom
anchors.topMargin: 35 anchors.topMargin: 35
onClicked: { onClicked: {
@ -205,9 +205,9 @@ TabBar {
color: hifi.buttons.black color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 55 anchors.rightMargin: 30
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 55 anchors.leftMargin: 30
anchors.top: assetServerButton.bottom anchors.top: assetServerButton.bottom
anchors.topMargin: 20 anchors.topMargin: 20
onClicked: { onClicked: {

View file

@ -55,7 +55,7 @@ Rectangle {
Text { Text {
id: text1 id: text1
text: qsTr("Material URL (Optional)") text: qsTr("Material URL <i>(Optional)</i>")
color: "#ffffff" color: "#ffffff"
font.pixelSize: 12 font.pixelSize: 12
} }

View file

@ -55,7 +55,7 @@ Rectangle {
Text { Text {
id: text1 id: text1
text: qsTr("Model URL") text: qsTr("Model URL <i>(.fbx, .fst, .glb, .gltf, .obj, .gz)</i>")
color: "#ffffff" color: "#ffffff"
font.pixelSize: 12 font.pixelSize: 12
} }

View file

@ -65,6 +65,14 @@
url(../fonts/hifi-glyphs.ttf); 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; margin: 0;
padding: 0; padding: 0;
@ -407,6 +415,21 @@ input[type=button].glyph, button.hifi-edit-button.glyph {
padding: 0; 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 { input[type=button].red, button.hifi-edit-button.red {
color: #fff; color: #fff;
background-color: #94132e; background-color: #94132e;
@ -417,6 +440,16 @@ input[type=button].blue, button.hifi-edit-button.blue {
background-color: #1080b8; background-color: #1080b8;
background: linear-gradient(#00b4ef 20%, #1080b8 100%); 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 { input[type=button].white, button.hifi-edit-button.white {
color: #121212; color: #121212;
background-color: #afafaf; 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); background: linear-gradient(#00b4ef, #00b4ef);
border: none; 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 { input[type=button].white:enabled:hover, button.hifi-edit-button.white:enabled:hover {
background: linear-gradient(#fff, #fff); background: linear-gradient(#fff, #fff);
border: none; 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 { input[type=button].blue:active, button.hifi-edit-button.blue:active {
background: linear-gradient(#1080b8, #1080b8); 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 { input[type=button].white:active, button.hifi-edit-button.white:active {
background: linear-gradient(#afafaf, #afafaf); 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; 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 { div#grid-section, body#entity-list-body {
padding-bottom: 0; padding-bottom: 0;
margin: 16px; margin: 16px;
@ -1227,12 +1273,6 @@ div#grid-section, body#entity-list-body {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
#delete {
float: right;
margin-right: 0;
background-color: #ff0000;
}
#entity-list { #entity-list {
position: relative; /* New positioning context. */ position: relative; /* New positioning context. */
} }
@ -1408,6 +1448,11 @@ input[type=button]#export {
font-size: 15px; font-size: 15px;
} }
#entity-table-scroll .vglyph {
font-family: Vircadia-Glyphs;
font-size: 15px;
}
#entity-table { #entity-table {
margin-top: -28px; margin-top: -28px;
margin-bottom: -18px; margin-bottom: -18px;
@ -1420,7 +1465,7 @@ input[type=button]#export {
background: none; background: none;
} }
#entity-table .glyph { #entity-table .glyph .vglyph {
margin: 0 -2px 0 -2px; margin: 0 -2px 0 -2px;
vertical-align: middle; vertical-align: middle;
} }
@ -1453,11 +1498,11 @@ input[type=button]#export {
outline: none; outline: none;
} }
#entity-table th .glyph { #entity-table th .glyph .vglyph {
position: relative; position: relative;
left: 4px; left: 4px;
} }
#entity-table th .glyph + .sort-order { #entity-table th .glyph .vglyph + .sort-order {
position: relative; position: relative;
left: 4px; left: 4px;
} }
@ -1484,7 +1529,7 @@ input[type=button]#export {
#entity-table td { #entity-table td {
box-sizing: border-box; box-sizing: border-box;
} }
#entity-table td.glyph { #entity-table td .glyph .vglyph {
text-align: center; text-align: center;
padding: 0; padding: 0;
} }
@ -1820,3 +1865,71 @@ div.multiZoneSelToolbar {
padding: 0px; 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