mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 13:53:26 +02:00
Merge branch 'master' into cullFace
This commit is contained in:
commit
5fc55d1763
97 changed files with 6319 additions and 891 deletions
.github/workflows
CMakeLists.txtandroid
assignment-client
cmake/macros
domain-server/src
hifi_vcpkg.pyinterface
launchers/qt
libraries
animation/src
audio-client/src
audio/src
entities/src
EntityItemProperties.cppEntityItemProperties.hEntityPropertyFlags.hZoneEntityItem.cppZoneEntityItem.h
input-plugins/src/input-plugins
material-networking/src/material-networking
model-networking/src/model-networking
networking/src
pointers/src
procedural/src/procedural
script-engine/src
ui/src
screenshare
scripts
developer
system/create
tools
animedit
.gitignoreLICENCEREADME.mdanimedit.promain.cppqml.qrc
qml
TreeDelegate.qml
treeitem.cpptreeitem.htreemodel.cpptreemodel.hfields
BooleanField.qmlIdField.qmlJSONField.qmlNumberArrayField.qmlNumberField.qmlStringField.qmlTypeField.qml
main.qmlnodes
ci-scripts
gpu-frame-player
jsdoc/plugins
197
.github/workflows/cmake.yml
vendored
197
.github/workflows/cmake.yml
vendored
|
@ -1,197 +0,0 @@
|
|||
name: CMake CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
# Eventually we will want to enable CI builds on push to specific branches, but we probably want to configure it not to happen in forks
|
||||
# push:
|
||||
# branches:
|
||||
# - master
|
||||
# - stable
|
||||
|
||||
env:
|
||||
CI_BUILD: Github
|
||||
BUILD_TYPE: Release
|
||||
RELEASE_TYPE: PR
|
||||
RELEASE_NUMBER: ${{ github.event.number }}
|
||||
VERSION_CODE: ${{ github.event.number }}
|
||||
GIT_PR_COMMIT: ${{ github.sha }}
|
||||
# FIXME should be a shortened version of the SHA
|
||||
GIT_PR_COMMIT_SHORT: ${{ github.sha }}
|
||||
HIFI_VCPKG_BOOTSTRAP: true
|
||||
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }}
|
||||
|
||||
# Settings for uploading
|
||||
APP_NAME: interface
|
||||
#APP_NAME: gpu-frame-player
|
||||
BUCKET_NAME: hifi-public
|
||||
UPLOAD_PREFIX: austin/builds
|
||||
|
||||
# OSX specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.11'
|
||||
# WIN32 specific variables
|
||||
PreferredToolArchitecture: X64
|
||||
|
||||
BS_USERNAME: gustavo@highfidelity.io
|
||||
BS_PASSWORD: ${MASKED_BUGSPLAT_PASSWORD}
|
||||
CMAKE_BACKTRACE_URL: https://highfidelity.sp.backtrace.io:6098
|
||||
GA_TRACKING_ID: UA-39558647-8
|
||||
OCULUS_APP_ID: '1255907384473836'
|
||||
# CMAKE_BACKTRACE_TOKEN: ${MASKED_CMAKE_BACKTRACE_TOKEN}
|
||||
# CMAKE_BACKTRACE_SYMBOLS_TOKEN: ${MASKED_BACKTRACE_UPLOAD_TOKEN}
|
||||
|
||||
# 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
|
||||
#OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_NIGHTLY}
|
||||
#OAUTH_CLIENT_ID=6c7d2349c0614640150db37457a1f75dce98a28ffe8f14d47f6cfae4de5b262a
|
||||
#OAUTH_REDIRECT_URI=https://dev-android-interface.highfidelity.com/auth
|
||||
#ARTIFACT_EXPRESSION=android/*.apk
|
||||
#SHA7=${GIT_PR_COMMIT_SHORT}
|
||||
#ANDROID_APK_NAME=HighFidelity-Beta-PR${RELEASE_NUMBER}-${GIT_PR_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:
|
||||
build_client_mac:
|
||||
runs-on: macOS-latest
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild-mac' || github.event.label.name == 'rebuild'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Install python modules
|
||||
run: pip3 install awscli boto3 PyGithub
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DCLIENT_ONLY:BOOLEAN=TRUE -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode -DVCPKG_APPLOCAL_DEPS=OFF
|
||||
- name: Build Console
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
||||
|
||||
build_full_mac:
|
||||
runs-on: macOS-latest
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild-mac' || github.event.label.name == 'rebuild'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Install python modules
|
||||
run: pip3 install awscli boto3 PyGithub
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode -DVCPKG_APPLOCAL_DEPS=OFF
|
||||
- name: Build Console
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Domain Server
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server
|
||||
- name: Build Assignment Client
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client
|
||||
- name: Build Installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
||||
|
||||
build_client_win32:
|
||||
runs-on: windows-latest
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild-win' || github.event.label.name == 'rebuild'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Install python modules
|
||||
run: pip install awscli boto3 PyGithub
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCLIENT_ONLY:BOOLEAN=TRUE -A x64
|
||||
- name: Build Console
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Application
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Installer
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
||||
|
||||
build_full_win32:
|
||||
runs-on: windows-latest
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild-win' || github.event.label.name == 'rebuild'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Install python modules
|
||||
run: pip install awscli boto3 PyGithub
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -A x64
|
||||
- name: Build Console
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Application
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Domain Server
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server
|
||||
- name: Build Assignment Client
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client
|
||||
- name: Build Installer
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
46
.github/workflows/dump.yml
vendored
Normal file
46
.github/workflows/dump.yml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
name: Dump Contexts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
|
||||
jobs:
|
||||
one:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macOS-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Dump job context
|
||||
shell: bash
|
||||
env:
|
||||
JOB_CONTEXT: ${{ toJson(job) }}
|
||||
run: echo "$JOB_CONTEXT"
|
||||
- name: Dump steps context
|
||||
shell: bash
|
||||
env:
|
||||
STEPS_CONTEXT: ${{ toJson(steps) }}
|
||||
run: echo "$STEPS_CONTEXT"
|
||||
- name: Dump runner context
|
||||
shell: bash
|
||||
env:
|
||||
RUNNER_CONTEXT: ${{ toJson(runner) }}
|
||||
run: echo "$RUNNER_CONTEXT"
|
||||
- name: Dump strategy context
|
||||
shell: bash
|
||||
env:
|
||||
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
|
||||
run: echo "$STRATEGY_CONTEXT"
|
||||
- name: Dump matrix context
|
||||
shell: bash
|
||||
env:
|
||||
MATRIX_CONTEXT: ${{ toJson(matrix) }}
|
||||
run: echo "$MATRIX_CONTEXT"
|
251
.github/workflows/master_build.yml
vendored
Normal file
251
.github/workflows/master_build.yml
vendored
Normal file
|
@ -0,0 +1,251 @@
|
|||
name: Master CI Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
#APP_NAME: gpu-frame-player
|
||||
APP_NAME: interface
|
||||
BUILD_TYPE: Release
|
||||
BUCKET_NAME: hifi-gh-builds
|
||||
CI_BUILD: Github
|
||||
CMAKE_BACKTRACE_URL: https://highfidelity.sp.backtrace.io:6098
|
||||
CMAKE_BACKTRACE_TOKEN: ${{ secrets.backtrace_token }}
|
||||
CMAKE_BACKTRACE_SYMBOLS_TOKEN: ${{ secrets.backtrace_symbols_token }}
|
||||
GIT_COMMIT: ${{ github.sha }}
|
||||
HIFI_VCPKG_BOOTSTRAP: true
|
||||
LAUNCHER_HMAC_SECRET: ${{ secrets.launcher_hmac_secret }}
|
||||
OCULUS_APP_ID: '${{ secrets.oculus_app_id }}'
|
||||
RELEASE_TYPE: PRODUCTION
|
||||
RELEASE_DYNAMODB_V2: ReleaseManager2-ReleaseQueue-prod
|
||||
STABLE_BUILD: 0
|
||||
|
||||
|
||||
# OSX specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.11'
|
||||
|
||||
# WIN32 specific variables
|
||||
PreferredToolArchitecture: X64
|
||||
|
||||
# Mac OS
|
||||
#PLATFORM_CMAKE_GENERATOR=Xcode
|
||||
#PLATFORM_BUILD_ARGUMENTS=--config Release --target package
|
||||
#ARTIFACT_EXPRESSION=build/*.dmg,build/*.zip
|
||||
|
||||
# Windows
|
||||
#PLATFORM_CMAKE_GENERATOR=Visual Studio 15 2017 Win64
|
||||
#PLATFORM_BUILD_ARGUMENTS=--target package --config release
|
||||
#ARTIFACT_EXPRESSION=build/*.exe,build/*.zip,*-symbols.zip
|
||||
|
||||
# Ubuntu
|
||||
#PLATFORM_CMAKE_GENERATOR=Unix Makefiles
|
||||
#PLATFORM_BUILD_ARGUMENTS=--target all -- -j4
|
||||
#ARTIFACT_EXPRESSION=build/assignment-client/**,build/domain-server/**,build/ice-server/ice-server,build/tools/ice-client/ice-client,build/tools/ac-client/ac-client,build/tools/oven,build/ext/makefiles/nvtt/project/lib/**,build/ext/makefiles/quazip/project/lib/**
|
||||
|
||||
# Android
|
||||
# branch: master
|
||||
# GA_TRACKING_ID: ${{ secrets.ga_tracking_id }}
|
||||
# ANDROID_OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_NIGHTLY}
|
||||
# ANDROID_OAUTH_CLIENT_ID=6c7d2349c0614640150db37457a1f75dce98a28ffe8f14d47f6cfae4de5b262a
|
||||
# ANDROID_OAUTH_REDIRECT_URI=https://dev-android-interface.highfidelity.com/auth
|
||||
# branch: !master
|
||||
# GA_TRACKING_ID=UA-39558647-11
|
||||
# ANDROID_OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_RELEASE}
|
||||
# ANDROID_OAUTH_CLIENT_ID= c1063ea5d0b0c405e0c9cd77351328e211a91496a3f25985a99e861f1661db1d
|
||||
# ANDROID_OAUTH_REDIRECT_URI=https://android-interface.highfidelity.com/auth
|
||||
# ARTIFACT_EXPRESSION=android/*.apk
|
||||
# ANDROID_APK_NAME=HighFidelity-Beta-PR${RELEASE_NUMBER}-${GIT_COMMIT_SHORT}.apk
|
||||
# ANDROID_BUILT_APK_NAME=interface-debug.apk
|
||||
# ANDROID_APP=interface
|
||||
# ANDROID_BUILD_DIR=debug
|
||||
# ANDROID_BUILD_TARGET=assembleDebug
|
||||
# STABLE_BUILD=0
|
||||
|
||||
|
||||
|
||||
jobs:
|
||||
generate_build_number:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate build number
|
||||
id: buildnumber
|
||||
uses: highfidelity/build-number@v3
|
||||
with:
|
||||
token: ${{secrets.github_token}}
|
||||
- name: Upload build number
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: BUILD_NUMBER
|
||||
path: BUILD_NUMBER
|
||||
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macOS-latest]
|
||||
build_type: [full, client]
|
||||
#os: [windows-latest, macOS-latest, ubuntu-latest]
|
||||
# exclude:
|
||||
# - os: ubuntu-latest
|
||||
# build_type: client
|
||||
runs-on: ${{matrix.os}}
|
||||
needs: generate_build_number
|
||||
steps:
|
||||
- name: Download build number
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: BUILD_NUMBER
|
||||
- name: Restore build number
|
||||
id: buildnumber
|
||||
uses: highfidelity/build-number@v3
|
||||
with:
|
||||
output_name: RELEASE_NUMBER
|
||||
- name: Configure Build Environment 1
|
||||
shell: bash
|
||||
id: buildenv1
|
||||
run: |
|
||||
echo ::set-env name=UPLOAD_PREFIX::master
|
||||
echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7`
|
||||
# Linux build variables
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
echo ::set-env name=INSTALLER_EXT::tgz
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=ZIP_COMMAND::zip
|
||||
echo ::set-env name=ZIP_ARGS::-r
|
||||
echo ::set-env name=INSTALLER_EXT::dmg
|
||||
echo ::set-env name=SYMBOL_REGEX::dSYM
|
||||
echo "::set-output name=symbols_archive::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-mac-symbols.zip"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python
|
||||
echo ::set-env name=ZIP_COMMAND::7z
|
||||
echo ::set-env name=ZIP_ARGS::a
|
||||
echo ::set-env name=INSTALLER_EXT::exe
|
||||
echo "::set-env name=SYMBOL_REGEX::\(exe\|dll\|pdb\)"
|
||||
echo "::set-output name=symbols_archive::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-win-symbols.zip"
|
||||
fi
|
||||
# Configureation is broken into two steps because you can't set an env var and also reference it in the same step
|
||||
- name: Configure Build Environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo ::set-env name=ARTIFACT_PATTERN::HighFidelity-Beta-*.$INSTALLER_EXT
|
||||
# Build type variables
|
||||
if [ "${{ matrix.build_type }}" = "full" ]; then
|
||||
echo ::set-env name=CLIENT_ONLY::FALSE
|
||||
echo ::set-env name=INSTALLER::HighFidelity-Beta-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
else
|
||||
echo ::set-env name=CLIENT_ONLY::TRUE
|
||||
echo ::set-env name=INSTALLER::HighFidelity-Beta-Interface-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
fi
|
||||
# Linux build variables
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=CMAKE_EXTRA::""
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
echo ::set-env name=CMAKE_EXTRA::"-A x64"
|
||||
echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}}
|
||||
echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx"
|
||||
fi
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Create Build Directory
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Decrypt Signing Key (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: gpg --batch --yes -o codesign.pfx --passphrase "${{secrets.gpg_symmetric_key}}" --decrypt $GITHUB_WORKSPACE/tools/ci-scripts/codesign.pfx.gpg
|
||||
- name: Import Signing Key (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: powershell
|
||||
run: |
|
||||
$mypwd=ConvertTo-SecureString -String ${{ secrets.pfx_key }} -Force -AsPlainText
|
||||
Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\CurrentUser\My -FilePath ${{runner.workspace}}\build\codesign.pfx
|
||||
Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\LocalMachine\My -FilePath ${{runner.workspace}}\build\codesign.pfx
|
||||
- name: Install Python modules
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
shell: bash
|
||||
run: $PYTHON_EXEC -m pip install boto3 PyGithub
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY $CMAKE_EXTRA
|
||||
- name: Build Application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Console
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Domain Server (FullBuild)
|
||||
if: matrix.build_type == 'full'
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server
|
||||
- name: Build Assignment Client (FullBuild)
|
||||
if: matrix.build_type == 'full'
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client
|
||||
- name: Build Installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
||||
- name: Sign Installer (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: powershell
|
||||
working-directory: C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64
|
||||
run: .\signtool.exe sign /fd sha256 /f ${{runner.workspace}}\build\codesign.pfx /p ${{secrets.pfx_key}} /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${{runner.workspace}}\build\${env:INSTALLER}
|
||||
- name: Upload Artifact
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }}
|
||||
run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py
|
||||
- name: Archive Symbols
|
||||
if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
working-directory: ${{runner.workspace}}
|
||||
shell: bash
|
||||
run: |
|
||||
SYMBOLS_TEMP="symbols-temp"
|
||||
mkdir $SYMBOLS_TEMP
|
||||
find "${{runner.workspace}}/build" \( -path '*/tools/gpu-frame-player/*' -or -path '*/interface/*' -or -path '*/plugins/*' \) -regex ".*\.$SYMBOL_REGEX" -exec cp -r {} $SYMBOLS_TEMP \;
|
||||
cd $SYMBOLS_TEMP
|
||||
$ZIP_COMMAND $ZIP_ARGS ../${{ steps.buildenv1.outputs.symbols_archive }} .
|
||||
- name: Upload Symbols
|
||||
if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
working-directory: ${{runner.workspace}}
|
||||
shell: bash
|
||||
run: |
|
||||
curl --data-binary @${{ steps.buildenv1.outputs.symbols_archive }} "$CMAKE_BACKTRACE_URL/post?format=symbols&token=$CMAKE_BACKTRACE_SYMBOLS_TOKEN&upload_file=${{steps.buildenv1.outputs.symbols_archive}}&tag=$RELEASE_NUMBER"
|
||||
# - name: Debug List Symbols
|
||||
# if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# shell: bash
|
||||
# run: |
|
||||
# unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
# - name: Debug Upload Symbols Artifact
|
||||
# if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
# uses: actions/upload-artifact@v1
|
||||
# with:
|
||||
# name: symbols
|
||||
# path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}
|
121
.github/workflows/pr_build.yml
vendored
Normal file
121
.github/workflows/pr_build.yml
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
name: Pull Request CI Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
env:
|
||||
APP_NAME: interface
|
||||
BUILD_TYPE: Release
|
||||
CI_BUILD: Github
|
||||
GIT_COMMIT: ${{ github.sha }}
|
||||
HIFI_VCPKG_BOOTSTRAP: true
|
||||
RELEASE_TYPE: PR
|
||||
RELEASE_NUMBER: ${{ github.event.number }}
|
||||
VERSION_CODE: ${{ github.event.number }}
|
||||
|
||||
|
||||
# OSX specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.11'
|
||||
|
||||
# WIN32 specific variables
|
||||
PreferredToolArchitecture: X64
|
||||
GIT_COMMIT_SHORT: ${{ github.sha }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macOS-latest]
|
||||
build_type: [full]
|
||||
fail-fast: false
|
||||
runs-on: ${{matrix.os}}
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild'
|
||||
steps:
|
||||
- name: Configure Build Environment 1
|
||||
shell: bash
|
||||
id: buildenv1
|
||||
run: |
|
||||
echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7`
|
||||
# Linux build variables
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::tgz
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::dmg
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python
|
||||
echo ::set-env name=INSTALLER_EXT::exe
|
||||
echo ::set-env name=CMAKE_EXTRA::"-A x64"
|
||||
fi
|
||||
# Configureation is broken into two steps because you can't set an env var and also reference it in the same step
|
||||
- name: Configure Build Environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo ::set-env name=ARTIFACT_PATTERN::HighFidelity-Beta-*.$INSTALLER_EXT
|
||||
# Build type variables
|
||||
echo ::set-env name=INSTALLER::HighFidelity-Beta-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Create Build Environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory "${{runner.workspace}}/build"
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE $CMAKE_EXTRA
|
||||
- name: Build Application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Domain Server
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server
|
||||
- name: Build Assignment Client
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client
|
||||
- name: Build Console
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
||||
|
||||
build_full_linux:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Install apt packages
|
||||
run: sudo apt install -y mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0
|
||||
- name: Install python modules
|
||||
shell: bash
|
||||
run: pip install boto3 PyGithub
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TOOLS:BOOLEAN=FALSE
|
||||
- name:
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . -- -j3
|
|
@ -257,6 +257,11 @@ if (ANDROID)
|
|||
return()
|
||||
endif()
|
||||
|
||||
if (BUILD_GPU_FRAME_PLAYER_ONLY)
|
||||
# This is for CI build testing
|
||||
add_subdirectory(tools/gpu-frame-player)
|
||||
else()
|
||||
|
||||
# add subdirectories for all targets
|
||||
if (BUILD_SERVER)
|
||||
add_subdirectory(assignment-client)
|
||||
|
@ -283,6 +288,8 @@ endif()
|
|||
# BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway
|
||||
add_subdirectory(tools)
|
||||
|
||||
endif()
|
||||
|
||||
if (BUILD_TESTS)
|
||||
# Turn on testing so that add_test works
|
||||
# MUST be in the root cmake file for ctest to work
|
||||
|
|
|
@ -29,7 +29,7 @@ docker run \
|
|||
-e CMAKE_BACKTRACE_TOKEN \
|
||||
-e CMAKE_BACKTRACE_SYMBOLS_TOKEN \
|
||||
-e GA_TRACKING_ID \
|
||||
-e GIT_PR_COMMIT \
|
||||
-e GIT_COMMIT \
|
||||
-e OAUTH_CLIENT_SECRET \
|
||||
-e OAUTH_CLIENT_ID \
|
||||
-e OAUTH_REDIRECT_URI \
|
||||
|
|
|
@ -18,21 +18,23 @@ link_hifi_libraries(
|
|||
)
|
||||
include_hifi_library_headers(procedural)
|
||||
|
||||
add_dependencies(${TARGET_NAME} oven)
|
||||
if (BUILD_TOOLS)
|
||||
add_dependencies(${TARGET_NAME} oven)
|
||||
|
||||
if (WIN32)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
$<TARGET_FILE_DIR:oven>
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>)
|
||||
else()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
$<TARGET_FILE:oven>
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/oven)
|
||||
endif()
|
||||
if (WIN32)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
$<TARGET_FILE_DIR:oven>
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>)
|
||||
else()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
$<TARGET_FILE:oven>
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/oven)
|
||||
endif()
|
||||
endif (BUILD_TOOLS)
|
||||
|
||||
if (WIN32)
|
||||
package_libraries_for_deployment()
|
||||
|
|
|
@ -23,8 +23,7 @@
|
|||
|
||||
#include "AvatarMixerSlave.h"
|
||||
|
||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) :
|
||||
NodeData(nodeID, nodeLocalID) {
|
||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID, nodeLocalID) {
|
||||
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
|
||||
_avatar->setID(nodeID);
|
||||
}
|
||||
|
@ -92,41 +91,48 @@ int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData
|
|||
}
|
||||
|
||||
namespace {
|
||||
using std::static_pointer_cast;
|
||||
using std::static_pointer_cast;
|
||||
|
||||
// Operator to find if a point is within an avatar-priority (hero) Zone Entity.
|
||||
struct FindPriorityZone {
|
||||
glm::vec3 position;
|
||||
bool isInPriorityZone { false };
|
||||
float zoneVolume { std::numeric_limits<float>::max() };
|
||||
EntityItemID id {};
|
||||
// Operator to find if a point is within an avatar-priority (hero) Zone Entity.
|
||||
struct FindContainingZone {
|
||||
glm::vec3 position;
|
||||
bool isInPriorityZone { false };
|
||||
bool isInScreenshareZone { false };
|
||||
float priorityZoneVolume { std::numeric_limits<float>::max() };
|
||||
float screenshareZoneVolume { priorityZoneVolume };
|
||||
EntityItemID screenshareZoneid{};
|
||||
|
||||
static bool operation(const OctreeElementPointer& element, void* extraData) {
|
||||
auto findPriorityZone = static_cast<FindPriorityZone*>(extraData);
|
||||
if (element->getAACube().contains(findPriorityZone->position)) {
|
||||
const EntityTreeElementPointer entityTreeElement = static_pointer_cast<EntityTreeElement>(element);
|
||||
entityTreeElement->forEachEntity([&findPriorityZone](EntityItemPointer item) {
|
||||
if (item->getType() == EntityTypes::Zone
|
||||
&& item->contains(findPriorityZone->position)) {
|
||||
auto zoneItem = static_pointer_cast<ZoneEntityItem>(item);
|
||||
if (zoneItem->getAvatarPriority() != COMPONENT_MODE_INHERIT) {
|
||||
float volume = zoneItem->getVolumeEstimate();
|
||||
if (volume < findPriorityZone->zoneVolume) { // Smaller volume wins
|
||||
findPriorityZone->isInPriorityZone = zoneItem->getAvatarPriority() == COMPONENT_MODE_ENABLED;
|
||||
findPriorityZone->zoneVolume = volume;
|
||||
findPriorityZone->id = zoneItem->getEntityItemID();
|
||||
}
|
||||
}
|
||||
static bool operation(const OctreeElementPointer& element, void* extraData) {
|
||||
auto findContainingZone = static_cast<FindContainingZone*>(extraData);
|
||||
if (element->getAACube().contains(findContainingZone->position)) {
|
||||
const EntityTreeElementPointer entityTreeElement = static_pointer_cast<EntityTreeElement>(element);
|
||||
entityTreeElement->forEachEntity([&findContainingZone](EntityItemPointer item) {
|
||||
if (item->getType() == EntityTypes::Zone && item->contains(findContainingZone->position)) {
|
||||
auto zoneItem = static_pointer_cast<ZoneEntityItem>(item);
|
||||
auto avatarPriorityProperty = zoneItem->getAvatarPriority();
|
||||
auto screenshareProperty = zoneItem->getScreenshare();
|
||||
float volume = zoneItem->getVolumeEstimate();
|
||||
if (avatarPriorityProperty != COMPONENT_MODE_INHERIT
|
||||
&& volume < findContainingZone->priorityZoneVolume) { // Smaller volume wins
|
||||
findContainingZone->isInPriorityZone = avatarPriorityProperty == COMPONENT_MODE_ENABLED;
|
||||
findContainingZone->priorityZoneVolume = volume;
|
||||
}
|
||||
});
|
||||
return true; // Keep recursing
|
||||
} else { // Position isn't within this subspace, so end recursion.
|
||||
return false;
|
||||
}
|
||||
if (screenshareProperty != COMPONENT_MODE_INHERIT
|
||||
&& volume < findContainingZone->screenshareZoneVolume) {
|
||||
findContainingZone->isInScreenshareZone = screenshareProperty == COMPONENT_MODE_ENABLED;
|
||||
findContainingZone->screenshareZoneVolume = volume;
|
||||
findContainingZone->screenshareZoneid = zoneItem->getEntityItemID();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true; // Keep recursing
|
||||
} else { // Position isn't within this subspace, so end recursion.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
} // Close anonymous namespace.
|
||||
} // namespace
|
||||
|
||||
int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveSharedData& slaveSharedData) {
|
||||
// pull the sequence number from the data first
|
||||
|
@ -152,15 +158,22 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
|
|||
auto newPosition = _avatar->getClientGlobalPosition();
|
||||
if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) {
|
||||
EntityTree& entityTree = *slaveSharedData.entityTree;
|
||||
FindPriorityZone findPriorityZone { newPosition } ;
|
||||
entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
|
||||
bool currentlyHasPriority = findPriorityZone.isInPriorityZone;
|
||||
FindContainingZone findContainingZone{ newPosition };
|
||||
entityTree.recurseTreeWithOperation(&FindContainingZone::operation, &findContainingZone);
|
||||
bool currentlyHasPriority = findContainingZone.isInPriorityZone;
|
||||
if (currentlyHasPriority != _avatar->getHasPriority()) {
|
||||
_avatar->setHasPriority(currentlyHasPriority);
|
||||
}
|
||||
bool isInScreenshareZone = findContainingZone.isInScreenshareZone;
|
||||
if (isInScreenshareZone != _avatar->isInScreenshareZone()
|
||||
|| findContainingZone.screenshareZoneid != _avatar->getScreenshareZone()) {
|
||||
_avatar->setInScreenshareZone(isInScreenshareZone);
|
||||
_avatar->setScreenshareZone(findContainingZone.screenshareZoneid);
|
||||
const QUuid& zoneId = isInScreenshareZone ? findContainingZone.screenshareZoneid : QUuid();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto packet = NLPacket::create(PacketType::AvatarZonePresence, 2 * NUM_BYTES_RFC4122_UUID, true);
|
||||
packet->write(_avatar->getSessionUUID().toRfc4122());
|
||||
packet->write(findPriorityZone.id.toRfc4122());
|
||||
packet->write(zoneId.toRfc4122());
|
||||
nodeList->sendPacket(std::move(packet), nodeList->getDomainSockAddr());
|
||||
}
|
||||
_avatar->setNeedsHeroCheck(false);
|
||||
|
@ -227,8 +240,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
}
|
||||
} else {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message.getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID +
|
||||
sizeof(AvatarTraits::TraitWireSize))) {
|
||||
if (message.getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID + sizeof(AvatarTraits::TraitWireSize))) {
|
||||
qWarning() << "Refusing to process malformed traits packet from" << message.getSenderSockAddr();
|
||||
return;
|
||||
}
|
||||
|
@ -244,8 +256,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
break;
|
||||
}
|
||||
|
||||
if (traitType == AvatarTraits::AvatarEntity ||
|
||||
traitType == AvatarTraits::Grab) {
|
||||
if (traitType == AvatarTraits::AvatarEntity || traitType == AvatarTraits::Grab) {
|
||||
auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID);
|
||||
|
||||
if (packetTraitVersion > instanceVersionRef) {
|
||||
|
@ -303,7 +314,8 @@ void AvatarMixerClientData::processBulkAvatarTraitsAckMessage(ReceivedMessage& m
|
|||
auto simpleReceivedIt = traitVersions.simpleCBegin();
|
||||
while (simpleReceivedIt != traitVersions.simpleCEnd()) {
|
||||
if (*simpleReceivedIt != AvatarTraits::DEFAULT_TRAIT_VERSION) {
|
||||
auto traitType = static_cast<AvatarTraits::TraitType>(std::distance(traitVersions.simpleCBegin(), simpleReceivedIt));
|
||||
auto traitType =
|
||||
static_cast<AvatarTraits::TraitType>(std::distance(traitVersions.simpleCBegin(), simpleReceivedIt));
|
||||
_perNodeAckedTraitVersions[nodeId][traitType] = *simpleReceivedIt;
|
||||
}
|
||||
simpleReceivedIt++;
|
||||
|
@ -361,8 +373,8 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedDa
|
|||
// make sure we're not unecessarily overriding the default avatar with the default avatar
|
||||
if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) {
|
||||
// we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change
|
||||
qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL()
|
||||
<< "to replacement" << slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID();
|
||||
qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() << "to replacement"
|
||||
<< slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID();
|
||||
_avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL);
|
||||
|
||||
auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true);
|
||||
|
@ -463,9 +475,7 @@ void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
|||
|
||||
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
|
||||
return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
|
||||
[&](const ConicalViewFrustum& viewFrustum) {
|
||||
return viewFrustum.intersects(otherAvatarBox);
|
||||
});
|
||||
[&](const ConicalViewFrustum& viewFrustum) { return viewFrustum.intersects(otherAvatarBox); });
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
||||
|
@ -484,7 +494,8 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
|||
jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView;
|
||||
}
|
||||
|
||||
AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const {
|
||||
AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(
|
||||
Node::LocalID otherAvatar) const {
|
||||
auto it = _lastSentTraitsTimestamps.find(otherAvatar);
|
||||
|
||||
if (it != _lastSentTraitsTimestamps.end()) {
|
||||
|
|
|
@ -43,6 +43,11 @@ public:
|
|||
};
|
||||
Q_ENUM(VerifyState)
|
||||
|
||||
bool isInScreenshareZone() const { return _inScreenshareZone; }
|
||||
void setInScreenshareZone(bool value = true) { _inScreenshareZone = value; }
|
||||
const QUuid& getScreenshareZone() const { return _screenshareZone; }
|
||||
void setScreenshareZone(QUuid zone) { _screenshareZone = zone; }
|
||||
|
||||
private:
|
||||
bool _needsHeroCheck { false };
|
||||
static const char* stateToName(VerifyState state);
|
||||
|
@ -65,6 +70,8 @@ private:
|
|||
int _numberChallenges { 0 };
|
||||
bool _certifyFailed { false };
|
||||
bool _needsIdentityUpdate { false };
|
||||
bool _inScreenshareZone { false };
|
||||
QUuid _screenshareZone;
|
||||
|
||||
bool generateFSTHash();
|
||||
bool validateFSTHash(const QString& publicKey) const;
|
||||
|
|
|
@ -91,13 +91,11 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
endif ()
|
||||
|
||||
if ((PRODUCTION_BUILD OR PR_BUILD) AND NOT STABLE_BUILD)
|
||||
set(GIT_PR_COMMIT $ENV{GIT_PR_COMMIT})
|
||||
#set(GIT_COMMIT_HASH ${GIT_PR_COMMIT})
|
||||
string(SUBSTRING ${GIT_PR_COMMIT} 0 7 GIT_COMMIT_HASH)
|
||||
set(GIT_COMMIT_SHORT $ENV{GIT_COMMIT_SHORT})
|
||||
# append the abbreviated commit SHA to the build version
|
||||
# since this is a PR build or master/nightly builds
|
||||
set(BUILD_VERSION_NO_SHA ${BUILD_VERSION})
|
||||
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_COMMIT_HASH}")
|
||||
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_COMMIT_SHORT}")
|
||||
|
||||
# pass along a release number without the SHA in case somebody
|
||||
# wants to compare master or PR builds as integers
|
||||
|
|
|
@ -21,6 +21,23 @@ macro(TARGET_POLYVOX)
|
|||
list(APPEND POLYVOX_LIBRARY_DEBUG ${POLYVOX_UTIL_LIBRARY_DEBUG})
|
||||
select_library_configurations(POLYVOX)
|
||||
list(APPEND POLYVOX_INCLUDE_DIRS ${VCPKG_INSTALL_ROOT}/include)
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(_LIB_GLOBS)
|
||||
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||
set(_PV_LIBRARIES ${POLYVOX_LIBRARY_DEBUG})
|
||||
else()
|
||||
set(_PV_LIBRARIES ${POLYVOX_LIBRARY_RELEASE})
|
||||
endif()
|
||||
foreach(_lib ${_PV_LIBRARIES})
|
||||
list(APPEND _LIB_GLOBS ${_lib}*)
|
||||
endforeach()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND cp
|
||||
ARGS -d ${_LIB_GLOBS} ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
||||
|
|
|
@ -10,4 +10,4 @@ macro(TARGET_QUAZIP)
|
|||
find_library(QUAZIP_LIBRARY_DEBUG quazip5 PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH)
|
||||
select_library_configurations(QUAZIP)
|
||||
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -3616,25 +3616,18 @@ void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMes
|
|||
}
|
||||
|
||||
void DomainServer::processAvatarZonePresencePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
QUuid avatar = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
QUuid zone = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
QUuid avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
QUuid zoneID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (avatar.isNull()) {
|
||||
if (avatarID.isNull()) {
|
||||
qCWarning(domain_server) << "Ignoring null avatar presence";
|
||||
return;
|
||||
}
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto matchingNode = limitedNodeList->nodeWithUUID(avatar);
|
||||
if (!matchingNode) {
|
||||
qCWarning(domain_server) << "Ignoring avatar presence for unknown avatar" << avatar;
|
||||
return;
|
||||
}
|
||||
QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
|
||||
static const int SCREENSHARE_EXPIRATION_SECONDS = 24 * 60 * 60;
|
||||
screensharePresence(zone.isNull() ? "" : zone.toString(), verifiedUsername, SCREENSHARE_EXPIRATION_SECONDS);
|
||||
screensharePresence(zoneID.isNull() ? "" : zoneID.toString(), avatarID, SCREENSHARE_EXPIRATION_SECONDS);
|
||||
}
|
||||
|
||||
void DomainServer::screensharePresence(QString roomname, QString username, int expirationSeconds) {
|
||||
void DomainServer::screensharePresence(QString roomname, QUuid avatarID, int expirationSeconds) {
|
||||
if (!DependencyManager::get<AccountManager>()->hasValidAccessToken()) {
|
||||
static std::once_flag presenceAuthorityWarning;
|
||||
std::call_once(presenceAuthorityWarning, [] {
|
||||
|
@ -3642,14 +3635,33 @@ void DomainServer::screensharePresence(QString roomname, QString username, int e
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto matchingNode = limitedNodeList->nodeWithUUID(avatarID);
|
||||
if (!matchingNode) {
|
||||
qCWarning(domain_server) << "Ignoring avatar presence for unknown avatar ID" << avatarID;
|
||||
return;
|
||||
}
|
||||
QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
|
||||
if (verifiedUsername.isEmpty()) { // Silently bail for users who are not logged in.
|
||||
return;
|
||||
}
|
||||
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.callbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "handleSuccessfulScreensharePresence";
|
||||
callbackParams.errorCallbackMethod = "handleFailedScreensharePresence";
|
||||
// Construct `callbackData`, which is data that will be available to the callback functions.
|
||||
// In this case, the "success" callback needs access to the "roomname" (the zone ID) and the
|
||||
// relevant avatar's UUID.
|
||||
QJsonObject callbackData;
|
||||
callbackData.insert("roomname", roomname);
|
||||
callbackData.insert("avatarID", avatarID.toString());
|
||||
callbackParams.callbackData = callbackData;
|
||||
const QString PATH = "api/v1/domains/%1/screenshare";
|
||||
QString domain_id = uuidStringWithoutCurlyBraces(getID());
|
||||
QJsonObject json, screenshare;
|
||||
screenshare["username"] = username;
|
||||
screenshare["username"] = verifiedUsername;
|
||||
screenshare["roomname"] = roomname;
|
||||
if (expirationSeconds > 0) {
|
||||
screenshare["expiration"] = expirationSeconds;
|
||||
|
@ -3663,11 +3675,18 @@ void DomainServer::screensharePresence(QString roomname, QString username, int e
|
|||
);
|
||||
}
|
||||
|
||||
void DomainServer::handleSuccessfulScreensharePresence(QNetworkReply* requestReply) {
|
||||
void DomainServer::handleSuccessfulScreensharePresence(QNetworkReply* requestReply, QJsonObject callbackData) {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply->readAll()).object();
|
||||
if (jsonObject["status"].toString() != "success") {
|
||||
qCWarning(domain_server) << "screensharePresence api call failed:" << QJsonDocument(jsonObject).toJson(QJsonDocument::Compact);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the client that we just authorized to screenshare which zone ID in which they are authorized to screenshare.
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto packet = NLPacket::create(PacketType::AvatarZonePresence, NUM_BYTES_RFC4122_UUID, true);
|
||||
packet->write(QUuid(callbackData["roomname"].toString()).toRfc4122());
|
||||
nodeList->sendPacket(std::move(packet), *(nodeList->nodeWithUUID(QUuid(callbackData["avatarID"].toString()))));
|
||||
}
|
||||
|
||||
void DomainServer::handleFailedScreensharePresence(QNetworkReply* requestReply) {
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
|
||||
bool isAssetServerEnabled();
|
||||
|
||||
void screensharePresence(QString roomname, QString username, int expiration_seconds = 0);
|
||||
void screensharePresence(QString roomname, QUuid avatarID, int expiration_seconds = 0);
|
||||
|
||||
public slots:
|
||||
/// Called by NodeList to inform us a node has been added
|
||||
|
@ -132,7 +132,7 @@ private slots:
|
|||
void handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply);
|
||||
void handleFailedICEServerAddressUpdate(QNetworkReply* requestReply);
|
||||
|
||||
void handleSuccessfulScreensharePresence(QNetworkReply* requestReply);
|
||||
void handleSuccessfulScreensharePresence(QNetworkReply* requestReply, QJsonObject callbackData);
|
||||
void handleFailedScreensharePresence(QNetworkReply* requestReply);
|
||||
|
||||
void updateReplicatedNodes();
|
||||
|
|
|
@ -75,6 +75,7 @@ endif()
|
|||
self.bootstrapEnv = os.environ.copy()
|
||||
self.buildEnv = os.environ.copy()
|
||||
self.prebuiltArchive = None
|
||||
usePrebuilt = ('CI_BUILD' in os.environ) and os.environ["CI_BUILD"] == "Github" and (not self.noClean)
|
||||
|
||||
if 'Windows' == system:
|
||||
self.exe = os.path.join(self.path, 'vcpkg.exe')
|
||||
|
@ -82,7 +83,7 @@ endif()
|
|||
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/builds/vcpkg-win32-client.zip?versionId=tSFzbw01VkkVFeRQ6YuAY4dro2HxJR9U'
|
||||
self.vcpkgHash = 'a650db47a63ccdc9904b68ddd16af74772e7e78170b513ea8de5a3b47d032751a3b73dcc7526d88bcb500753ea3dd9880639ca842bb176e2bddb1710f9a58cd3'
|
||||
self.hostTriplet = 'x64-windows'
|
||||
if ('CI_BUILD' in os.environ) and os.environ["CI_BUILD"] == "Github" and (not self.noClean):
|
||||
if usePrebuilt:
|
||||
self.prebuiltArchive = "https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/builds/vcpkg-win32.zip?versionId=3SF3mDC8dkQH1JP041m88xnYmWNzZflx"
|
||||
elif 'Darwin' == system:
|
||||
self.exe = os.path.join(self.path, 'vcpkg')
|
||||
|
@ -90,7 +91,7 @@ endif()
|
|||
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/builds/vcpkg-osx-client.tgz?versionId=j0b4azo_zTlH_Q9DElEWOz1UMYZ2nqQw'
|
||||
self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d'
|
||||
self.hostTriplet = 'x64-osx'
|
||||
if ('CI_BUILD' in os.environ) and os.environ["CI_BUILD"] == "Github" and (not self.noClean):
|
||||
if usePrebuilt:
|
||||
self.prebuiltArchive = "https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/builds/vcpkg-osx.tgz?versionId=6JrIMTdvpBF3MAsjA92BMkO79Psjzs6Z"
|
||||
else:
|
||||
self.exe = os.path.join(self.path, 'vcpkg')
|
||||
|
|
|
@ -185,7 +185,10 @@ if (BUILD_TOOLS AND NPM_EXECUTABLE)
|
|||
add_dependencies(resources jsdoc)
|
||||
endif()
|
||||
|
||||
add_dependencies(${TARGET_NAME} resources)
|
||||
if (WIN32 OR APPLE)
|
||||
add_dependencies(${TARGET_NAME} resources screenshare)
|
||||
endif()
|
||||
|
||||
|
||||
if (WIN32)
|
||||
# These are external plugins, but we need to do the 'add dependency' here so that their
|
||||
|
@ -323,6 +326,10 @@ if (APPLE)
|
|||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/scripts"
|
||||
"${RESOURCES_DEV_DIR}/scripts"
|
||||
# copy screenshare app to the resource folder
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/../screenshare/hifi-screenshare-darwin-x64/hifi-screenshare.app"
|
||||
"${RESOURCES_DEV_DIR}/hifi-screenshare.app"
|
||||
# copy JSDoc files beside the executable
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
|
||||
|
@ -347,7 +354,7 @@ if (APPLE)
|
|||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"${PROJECT_SOURCE_DIR}/resources/serverless/redirect.json"
|
||||
"${RESOURCES_DEV_DIR}/serverless/redirect.json"
|
||||
)
|
||||
)
|
||||
|
||||
# call the fixup_interface macro to add required bundling commands for installation
|
||||
fixup_interface()
|
||||
|
|
|
@ -3395,6 +3395,10 @@ void Application::initializeUi() {
|
|||
|
||||
|
||||
setIsInterstitialMode(true);
|
||||
|
||||
#if defined(DISABLE_QML) && defined(Q_OS_LINUX)
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -5662,6 +5666,7 @@ void Application::resumeAfterLoginDialogActionTaken() {
|
|||
return;
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
if (!isHMDMode() && getDesktopTabletBecomesToolbarSetting()) {
|
||||
auto toolbar = DependencyManager::get<ToolbarScriptingInterface>()->getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
toolbar->writeProperty("visible", true);
|
||||
|
@ -5671,6 +5676,7 @@ void Application::resumeAfterLoginDialogActionTaken() {
|
|||
}
|
||||
|
||||
updateSystemTabletMode();
|
||||
#endif
|
||||
|
||||
{
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
|
|
|
@ -17,12 +17,64 @@
|
|||
|
||||
static const int VR_TARGET_RATE = 90;
|
||||
|
||||
/**jsdoc
|
||||
* <p>Refresh rate profile.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Refresh Rate Profile</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"Eco"</code></td><td>Low refresh rate, which is reduced when Interface doesn't have focus or is
|
||||
* minimized.</td></tr>
|
||||
* <tr><td><code>"Interactive"</code></td><td>Medium refresh rate, which is reduced when Interface doesn't have focus or is
|
||||
* minimized.</td></tr>
|
||||
* <tr><td><code>"Realtime"</code></td><td>High refresh rate, even when Interface doesn't have focus or is minimized.
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* @typedef {string} RefreshRateProfile
|
||||
*/
|
||||
static const std::array<std::string, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILE_TO_STRING =
|
||||
{ { "Eco", "Interactive", "Realtime" } };
|
||||
|
||||
/**jsdoc
|
||||
* <p>Interface states that affect the refresh rate.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Refresh Rate Regime</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"FocusActive"</code></td><td>Interface has focus and the user is active or is in VR.</td></tr>
|
||||
* <tr><td><code>"FocusInactive"</code></td><td>Interface has focus and the user is inactive.</td></tr>
|
||||
* <tr><td><code>"Unfocus"</code></td><td>Interface doesn't have focus.</td></tr>
|
||||
* <tr><td><code>"Minimized"</code></td><td>Interface is minimized.</td></tr>
|
||||
* <tr><td><code>"StartUp"</code></td><td>Interface is starting up.</td></tr>
|
||||
* <tr><td><code>"ShutDown"</code></td><td>Interface is shutting down.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* @typedef {string} RefreshRateRegime
|
||||
*/
|
||||
static const std::array<std::string, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIME_TO_STRING =
|
||||
{ { "FocusActive", "FocusInactive", "Unfocus", "Minimized", "StartUp", "ShutDown" } };
|
||||
|
||||
/**jsdoc
|
||||
* <p>Interface operates in different modes to provide different user experiences (UX).</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>UX Mode</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"Desktop"</code></td><td>Desktop user experience mode.</td></tr>
|
||||
* <tr><td><code>"VR"</code></td><td>VR user experience mode.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* @typedef {string} UXMode
|
||||
*/
|
||||
static const std::array<std::string, RefreshRateManager::UXMode::UX_NUM> UX_MODE_TO_STRING =
|
||||
{ { "Desktop", "VR" } };
|
||||
|
||||
|
|
|
@ -436,38 +436,72 @@ class MyAvatar : public Avatar {
|
|||
const bool DEFAULT_STRAFE_ENABLED = true;
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* The <code>DriveKeys</code> API provides constant numeric values that represent different logical keys that drive your
|
||||
* avatar and camera.
|
||||
*
|
||||
* @namespace DriveKeys
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @property {number} TRANSLATE_X - Move the user's avatar in the direction of its x-axis, if the camera isn't in
|
||||
* independent or mirror modes.
|
||||
* @property {number} TRANSLATE_Y - Move the user's avatar in the direction of its y-axis, if the camera isn't in
|
||||
* independent or mirror modes.
|
||||
* @property {number} TRANSLATE_Z - Move the user's avatar in the direction of its z-axis, if the camera isn't in
|
||||
* independent or mirror modes.
|
||||
* @property {number} YAW - Rotate the user's avatar about its y-axis at a rate proportional to the control value, if the
|
||||
* camera isn't in independent or mirror modes.
|
||||
* @property {number} STEP_TRANSLATE_X - No action.
|
||||
* @property {number} STEP_TRANSLATE_Y - No action.
|
||||
* @property {number} STEP_TRANSLATE_Z - No action.
|
||||
* @property {number} STEP_YAW - Rotate the user's avatar about its y-axis in a step increment, if the camera isn't in
|
||||
* independent or mirror modes.
|
||||
* @property {number} PITCH - Rotate the user's avatar head and attached camera about its negative x-axis (i.e., positive
|
||||
* values pitch down) at a rate proportional to the control value, if the camera isn't in HMD, independent, or mirror
|
||||
* modes.
|
||||
* @property {number} ZOOM - Zoom the camera in or out.
|
||||
* @property {number} DELTA_YAW - Rotate the user's avatar about its y-axis by an amount proportional to the control value,
|
||||
* if the camera isn't in independent or mirror modes.
|
||||
* @property {number} DELTA_PITCH - Rotate the user's avatar head and attached camera about its negative x-axis (i.e.,
|
||||
* positive values pitch down) by an amount proportional to the control value, if the camera isn't in HMD, independent,
|
||||
* or mirror modes.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* <p>Logical keys that drive your avatar and camera.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>TRANSLATE_X</td><td>Move the user's avatar in the direction of its x-axis, if the
|
||||
* camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>1</code></td><td>TRANSLATE_Y</td><td>Move the user's avatar in the direction of its y-axis, if the
|
||||
* camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>TRANSLATE_Z</td><td>Move the user's avatar in the direction of its z-axis, if the
|
||||
* camera isn't in independent or mirror modes</td></tr>
|
||||
* <tr><td><code>3</code></td><td>YAW</td><td>Rotate the user's avatar about its y-axis at a rate proportional to the
|
||||
* control value, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>4</code></td><td>STEP_TRANSLATE_X</td><td>No action.</td></tr>
|
||||
* <tr><td><code>5</code></td><td>STEP_TRANSLATE_Y</td><td>No action.</td></tr>
|
||||
* <tr><td><code>6</code></td><td>STEP_TRANSLATE_Z</td><td>No action.</td></tr>
|
||||
* <tr><td><code>7</code></td><td>STEP_YAW</td><td>Rotate the user's avatar about its y-axis in a step increment, if
|
||||
* the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>8</code></td><td>PITCH</td><td>Rotate the user's avatar head and attached camera about its negative
|
||||
* x-axis (i.e., positive values pitch down) at a rate proportional to the control value, if the camera isn't in HMD,
|
||||
* independent, or mirror modes.</td></tr>
|
||||
* <tr><td><code>9</code></td><td>ZOOM</td><td>Zoom the camera in or out.</td></tr>
|
||||
* <tr><td><code>10</code></td><td>DELTA_YAW</td><td>Rotate the user's avatar about its y-axis by an amount proportional
|
||||
* to the control value, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>11</code></td><td>DELTA_PITCH</td><td>Rotate the user's avatar head and attached camera about its
|
||||
* negative x-axis (i.e., positive values pitch down) by an amount proportional to the control value, if the camera
|
||||
* isn't in HMD, independent, or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.TRANSLATE_X}</code></td><td>Move the user's avatar in the direction of its
|
||||
* x-axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.TRANSLATE_Y}</code></td><td>Move the user's avatar in the direction of its
|
||||
* -axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.TRANSLATE_Z}</code></td><td>Move the user's avatar in the direction of its
|
||||
* z-axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.YAW}</code></td><td>Rotate the user's avatar about its y-axis at a rate
|
||||
* proportional to the control value, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.STEP_TRANSLATE_X}</code></td><td>No action.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.STEP_TRANSLATE_Y}</code></td><td>No action.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.STEP_TRANSLATE_Z}</code></td><td>No action.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.STEP_YAW}</code></td><td>Rotate the user's avatar about its y-axis in a
|
||||
* step increment, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.PITCH}</code></td><td>Rotate the user's avatar head and attached camera
|
||||
* about its negative x-axis (i.e., positive values pitch down) at a rate proportional to the control value, if the
|
||||
* camera isn't in HMD, independent, or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.ZOOM}</code></td><td>Zoom the camera in or out.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.DELTA_YAW}</code></td><td>Rotate the user's avatar about its y-axis by an
|
||||
* amount proportional to the control value, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>{@link DriveKeys|DriveKeys.DELTA_PITCH}</code></td><td>Rotate the user's avatar head and attached
|
||||
* camera about its negative x-axis (i.e., positive values pitch down) by an amount proportional to the control
|
||||
* value, if the camera isn't in HMD, independent, or mirror modes.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} MyAvatar.DriveKeys
|
||||
* @typedef {number} DriveKey
|
||||
*/
|
||||
enum DriveKeys {
|
||||
TRANSLATE_X = 0,
|
||||
|
@ -1019,7 +1053,7 @@ public:
|
|||
/**jsdoc
|
||||
* Gets the value of a drive key, regardless of whether it is disabled.
|
||||
* @function MyAvatar.getRawDriveKey
|
||||
* @param {MyAvatar.DriveKeys} key - The drive key.
|
||||
* @param {DriveKey} key - The drive key.
|
||||
* @returns {number} The value of the drive key.
|
||||
*/
|
||||
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
|
||||
|
@ -1029,11 +1063,10 @@ public:
|
|||
/**jsdoc
|
||||
* Disables the action associated with a drive key.
|
||||
* @function MyAvatar.disableDriveKey
|
||||
* @param {MyAvatar.DriveKeys} key - The drive key to disable.
|
||||
* @param {DriveKey} key - The drive key to disable.
|
||||
* @example <caption>Disable rotating your avatar using the keyboard for a couple of seconds.</caption>
|
||||
* var YAW = 3;
|
||||
* print("Disable");
|
||||
* MyAvatar.disableDriveKey(YAW);
|
||||
* MyAvatar.disableDriveKey(DriveKeys.YAW);
|
||||
* Script.setTimeout(function () {
|
||||
* print("Enable");
|
||||
* MyAvatar.enableDriveKey(YAW);
|
||||
|
@ -1045,14 +1078,14 @@ public:
|
|||
* Enables the action associated with a drive key. The action may have been disabled with
|
||||
* {@link MyAvatar.disableDriveKey|disableDriveKey}.
|
||||
* @function MyAvatar.enableDriveKey
|
||||
* @param {MyAvatar.DriveKeys} key - The drive key to enable.
|
||||
* @param {DriveKey} key - The drive key to enable.
|
||||
*/
|
||||
Q_INVOKABLE void enableDriveKey(DriveKeys key);
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether a drive key is disabled.
|
||||
* @function MyAvatar.isDriveKeyDisabled
|
||||
* @param {DriveKeys} key - The drive key to check.
|
||||
* @param {DriveKey} key - The drive key to check.
|
||||
* @returns {boolean} <code>true</code> if the drive key is disabled, <code>false</code> if it isn't.
|
||||
*/
|
||||
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
|
||||
|
|
|
@ -46,6 +46,7 @@ class AccountServicesScriptingInterface : public QObject {
|
|||
* @hifi-avatar
|
||||
*
|
||||
* @namespace AccountServices
|
||||
*
|
||||
* @property {string} username - The user name of the user logged in. If there is no user logged in, it is
|
||||
* <code>"Unknown user"</code>. <em>Read-only.</em>
|
||||
* @property {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
|
||||
|
@ -61,6 +62,86 @@ class AccountServicesScriptingInterface : public QObject {
|
|||
* — typically <code>"https://metaverse.highfidelity.com"</code>. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* The <code>Account</code> API provides functions that give information on user connectivity, visibility, and asset
|
||||
* download progress.
|
||||
*
|
||||
* @deprecated This API is the same as the {@link AccountServices} API and will be removed.
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @namespace Account
|
||||
*
|
||||
* @property {string} username - The user name of the user logged in. If there is no user logged in, it is
|
||||
* <code>"Unknown user"</code>. <em>Read-only.</em>
|
||||
* @property {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} findableBy - The user's visibility to other users:
|
||||
* <ul>
|
||||
* <li><code>"none"</code> — user appears offline.</li>
|
||||
* <li><code>"friends"</code> — user is visible only to friends.</li>
|
||||
* <li><code>"connections"</code> — user is visible to friends and connections.</li>
|
||||
* <li><code>"all"</code> — user is visible to everyone.</li>
|
||||
* </ul>
|
||||
* @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in
|
||||
* — typically <code>"https://metaverse.highfidelity.com"</code>. <em>Read-only.</em>
|
||||
*
|
||||
* @borrows AccountServices.getDownloadInfo as getDownloadInfo
|
||||
* @borrows AccountServices.updateDownloadInfo as updateDownloadInfo
|
||||
* @borrows AccountServices.isLoggedIn as isLoggedIn
|
||||
* @borrows AccountServices.checkAndSignalForAccessToken as checkAndSignalForAccessToken
|
||||
* @borrows AccountServices.logOut as logOut
|
||||
*
|
||||
* @borrows AccountServices.connected as connected
|
||||
* @borrows AccountServices.disconnected as disconnected
|
||||
* @borrows AccountServices.myUsernameChanged as myUsernameChanged
|
||||
* @borrows AccountServices.downloadInfoChanged as downloadInfoChanged
|
||||
* @borrows AccountServices.findableByChanged as findableByChanged
|
||||
* @borrows AccountServices.loggedInChanged as loggedInChanged
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* The <code>GlobalServices</code> API provides functions that give information on user connectivity, visibility, and asset
|
||||
* download progress.
|
||||
*
|
||||
* @deprecated This API is the same as the {@link AccountServices} API and will be removed.
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @namespace GlobalServices
|
||||
*
|
||||
* @property {string} username - The user name of the user logged in. If there is no user logged in, it is
|
||||
* <code>"Unknown user"</code>. <em>Read-only.</em>
|
||||
* @property {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} findableBy - The user's visibility to other users:
|
||||
* <ul>
|
||||
* <li><code>"none"</code> — user appears offline.</li>
|
||||
* <li><code>"friends"</code> — user is visible only to friends.</li>
|
||||
* <li><code>"connections"</code> — user is visible to friends and connections.</li>
|
||||
* <li><code>"all"</code> — user is visible to everyone.</li>
|
||||
* </ul>
|
||||
* @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in
|
||||
* — typically <code>"https://metaverse.highfidelity.com"</code>. <em>Read-only.</em>
|
||||
*
|
||||
* @borrows AccountServices.getDownloadInfo as getDownloadInfo
|
||||
* @borrows AccountServices.updateDownloadInfo as updateDownloadInfo
|
||||
* @borrows AccountServices.isLoggedIn as isLoggedIn
|
||||
* @borrows AccountServices.checkAndSignalForAccessToken as checkAndSignalForAccessToken
|
||||
* @borrows AccountServices.logOut as logOut
|
||||
*
|
||||
* @borrows AccountServices.connected as connected
|
||||
* @borrows AccountServices.disconnected as disconnected
|
||||
* @borrows AccountServices.myUsernameChanged as myUsernameChanged
|
||||
* @borrows AccountServices.downloadInfoChanged as downloadInfoChanged
|
||||
* @borrows AccountServices.findableByChanged as findableByChanged
|
||||
* @borrows AccountServices.loggedInChanged as loggedInChanged
|
||||
*/
|
||||
|
||||
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
|
||||
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
|
||||
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged)
|
||||
|
|
|
@ -14,19 +14,56 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
/**jsdoc
|
||||
* The <code>DialogsMamnager</code> API provides facilities to work with some key dialogs.
|
||||
*
|
||||
* @namespace DialogsManager
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*/
|
||||
|
||||
class DialogsManagerScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DialogsManagerScriptingInterface();
|
||||
static DialogsManagerScriptingInterface* getInstance();
|
||||
|
||||
/**jsdoc
|
||||
* <em>Currently performs no action.</em>
|
||||
* @function DialogsManager.showFeed
|
||||
*/
|
||||
Q_INVOKABLE void showFeed();
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Shows the "Goto" dialog.
|
||||
* @function DialogsManager.showAddressBar
|
||||
*/
|
||||
void showAddressBar();
|
||||
|
||||
/**jsdoc
|
||||
* Hides the "Goto" dialog.
|
||||
* @function DialogsManager.hideAddressBar
|
||||
*/
|
||||
void hideAddressBar();
|
||||
|
||||
/**jsdoc
|
||||
* Shows the login dialog.
|
||||
* @function DialogsManager.showLoginDialog
|
||||
*/
|
||||
void showLoginDialog();
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Triggered when the "Goto" dialog is opened or closed.
|
||||
* <p><strong>Warning:</strong> Currently isn't always triggered.</p>
|
||||
* @function DialogsManager.addressBarShown
|
||||
* @param {boolean} visible - <code>true</code> if the Goto dialog has been opened, <code>false</code> if it has been
|
||||
* closed.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void addressBarShown(bool visible);
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
|
@ -37,14 +38,48 @@ ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
|
|||
_requestScreenshareInfoRetryTimer->setSingleShot(true);
|
||||
_requestScreenshareInfoRetryTimer->setInterval(SCREENSHARE_INFO_REQUEST_RETRY_TIMEOUT_MS);
|
||||
connect(_requestScreenshareInfoRetryTimer, &QTimer::timeout, this, &ScreenshareScriptingInterface::requestScreenshareInfo);
|
||||
|
||||
// This packet listener handles the packet containing information about the latest zone ID in which we are allowed to share.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacketOnClient");
|
||||
};
|
||||
|
||||
ScreenshareScriptingInterface::~ScreenshareScriptingInterface() {
|
||||
stopScreenshare();
|
||||
}
|
||||
|
||||
void ScreenshareScriptingInterface::processAvatarZonePresencePacketOnClient(QSharedPointer<ReceivedMessage> message) {
|
||||
QUuid zone = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (zone.isNull()) {
|
||||
qWarning() << "Ignoring avatar zone presence packet that doesn't specify a zone.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the last known authorized screenshare zone ID to the zone that the Domain Server just told us about.
|
||||
_lastAuthorizedZoneID = zone;
|
||||
|
||||
// If we had previously started the screenshare process but knew that we weren't going to be authorized to screenshare,
|
||||
// let's continue the screenshare process here.
|
||||
if (_waitingForAuthorization) {
|
||||
requestScreenshareInfo();
|
||||
}
|
||||
}
|
||||
|
||||
static const int MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES = 5;
|
||||
void ScreenshareScriptingInterface::requestScreenshareInfo() {
|
||||
// If the screenshare zone that we're currently in (i.e. `startScreenshare()` was called) is different from
|
||||
// the zone in which we are authorized to screenshare...
|
||||
// ...return early here and wait for the DS to send us a packet containing this zone's ID.
|
||||
if (_screenshareZoneID != _lastAuthorizedZoneID) {
|
||||
qDebug() << "Client not yet authorized to screenshare. Waiting for authorization message from domain server...";
|
||||
_waitingForAuthorization = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_waitingForAuthorization = false;
|
||||
|
||||
_requestScreenshareInfoRetries++;
|
||||
|
||||
if (_requestScreenshareInfoRetries >= MAX_NUM_SCREENSHARE_INFO_REQUEST_RETRIES) {
|
||||
|
@ -90,7 +125,8 @@ static const EntityTypes::EntityType LOCAL_SCREENSHARE_WEB_ENTITY_TYPE = EntityT
|
|||
static const uint8_t LOCAL_SCREENSHARE_WEB_ENTITY_FPS = 30;
|
||||
// This is going to be a good amount of work to make this work dynamically for any screensize.
|
||||
// V1 will have only hardcoded values.
|
||||
static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0128f, -0.0918f, 0.0771f);
|
||||
// The `z` value here is dynamic.
|
||||
static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0128f, -0.0918f, 0.0f);
|
||||
static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(3.6790f, 2.0990f, 0.0100f);
|
||||
static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL =
|
||||
"https://content.highfidelity.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html";
|
||||
|
@ -124,7 +160,7 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon
|
|||
// Ensure that the screenshare executable exists where we expect it to.
|
||||
// Error out and reset the screen share state machine if the executable doesn't exist.
|
||||
QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH);
|
||||
if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) {
|
||||
if (!screenshareExecutable.exists() || !(screenshareExecutable.isFile() || screenshareExecutable.isBundle())) {
|
||||
qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH;
|
||||
stopScreenshare();
|
||||
emit screenshareError();
|
||||
|
@ -173,6 +209,7 @@ void ScreenshareScriptingInterface::stopScreenshare() {
|
|||
_projectAPIKey = "";
|
||||
_sessionID = "";
|
||||
_isPresenter = false;
|
||||
_waitingForAuthorization = false;
|
||||
}
|
||||
|
||||
// Called when the Metaverse returns the information necessary to start/view a screen share.
|
||||
|
@ -241,7 +278,9 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR
|
|||
EntityItemProperties localScreenshareWebEntityProps;
|
||||
localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE);
|
||||
localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS);
|
||||
localScreenshareWebEntityProps.setLocalPosition(LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION);
|
||||
glm::vec3 localPosition(LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION);
|
||||
localPosition.z = _localWebEntityZOffset;
|
||||
localScreenshareWebEntityProps.setLocalPosition(localPosition);
|
||||
localScreenshareWebEntityProps.setSourceUrl(LOCAL_SCREENSHARE_WEB_ENTITY_URL);
|
||||
localScreenshareWebEntityProps.setParentID(_smartboardEntityID);
|
||||
localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS);
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
#include <QNetworkReply>
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <ReceivedMessage.h>
|
||||
|
||||
class ScreenshareScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float localWebEntityZOffset MEMBER _localWebEntityZOffset NOTIFY localWebEntityZOffsetChanged)
|
||||
public:
|
||||
ScreenshareScriptingInterface();
|
||||
~ScreenshareScriptingInterface();
|
||||
|
@ -32,8 +34,10 @@ signals:
|
|||
void screenshareError();
|
||||
void screenshareProcessTerminated();
|
||||
void startScreenshareViewer();
|
||||
void localWebEntityZOffsetChanged(const float& newZOffset);
|
||||
|
||||
private slots:
|
||||
void processAvatarZonePresencePacketOnClient(QSharedPointer<ReceivedMessage> message);
|
||||
void onWebEventReceived(const QUuid& entityID, const QVariant& message);
|
||||
void handleSuccessfulScreenshareInfoGet(QNetworkReply* reply);
|
||||
void handleFailedScreenshareInfoGet(QNetworkReply* reply);
|
||||
|
@ -43,16 +47,16 @@ private:
|
|||
#ifdef Q_OS_WIN
|
||||
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-win32-x64/hifi-screenshare.exe" };
|
||||
#elif defined(Q_OS_MAC)
|
||||
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/screenshare-darwin-x64/hifi-screenshare.app" };
|
||||
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/../Resources/hifi-screenshare.app" };
|
||||
#else
|
||||
// This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked.
|
||||
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/screenshare-other-os/hifi-screenshare" };
|
||||
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-other-os/hifi-screenshare" };
|
||||
#endif
|
||||
#else
|
||||
#ifdef Q_OS_WIN
|
||||
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.exe" };
|
||||
#elif defined(Q_OS_MAC)
|
||||
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.app" };
|
||||
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/../Resources/hifi-screenshare.app" };
|
||||
#else
|
||||
// This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked.
|
||||
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare" };
|
||||
|
@ -63,6 +67,15 @@ private:
|
|||
int _requestScreenshareInfoRetries{ 0 };
|
||||
void requestScreenshareInfo();
|
||||
|
||||
// Empirically determined. The default value here can be changed in Screenshare scripts, which enables faster iteration when we discover
|
||||
// positional issues with various Smartboard entities.
|
||||
// The following four values are closely linked:
|
||||
// 1. The z-offset of whiteboard polylines (`STROKE_FORWARD_OFFSET_M` in `drawSphereClient.js`).
|
||||
// 2. The z-offset of the screenshare local web entity (`LOCAL_WEB_ENTITY_Z_OFFSET` in `smartboardZoneClient.js`).
|
||||
// 3. The z-offset of the screenshare "glass bezel" (`DEFAULT_SMARTBOARD_SCREENSHARE_GLASS_PROPS` in `smartboardZoneClient.js`).
|
||||
// 4. The z-offset of the screenshare "status icon" (handled in the screenshare JSON file).
|
||||
float _localWebEntityZOffset{ 0.0375f };
|
||||
|
||||
std::unique_ptr<QProcess> _screenshareProcess{ nullptr };
|
||||
QUuid _screenshareViewerLocalWebEntityUUID;
|
||||
QString _token{ "" };
|
||||
|
@ -71,6 +84,9 @@ private:
|
|||
QUuid _screenshareZoneID;
|
||||
QUuid _smartboardEntityID;
|
||||
bool _isPresenter{ false };
|
||||
|
||||
QUuid _lastAuthorizedZoneID;
|
||||
bool _waitingForAuthorization{ false };
|
||||
};
|
||||
|
||||
#endif // hifi_ScreenshareScriptingInterface_h
|
||||
|
|
File diff suppressed because it is too large
Load diff
13
launchers/qt/BUILD.md
Normal file
13
launchers/qt/BUILD.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Dependencies
|
||||
- [cmake](https://cmake.org/download/): 3.9
|
||||
|
||||
# Windows
|
||||
* Download `Visual Studio 2019`
|
||||
`cmake -G "Visual Studio 16 2019" ..`
|
||||
|
||||
# MacOS
|
||||
* Install `Xcode`
|
||||
`cmake -G Xcode ..`
|
||||
|
||||
|
||||
If you wish to not use the compiled qml files, pass the `-DLAUNCHER_SOURCE_TREE_RESOURCES=On` argument to cmake.
|
34
launchers/qt/readme.md
Normal file
34
launchers/qt/readme.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# HQ Launcher
|
||||
Behavior of the HQ Launcher is as follows:
|
||||
* Update the HQ Launcher to the latest version
|
||||
* Sign up or sign in if is the user is not already signed in
|
||||
* Download the latest Interface client
|
||||
* Launch the user in the current HQ domain
|
||||
|
||||
# directory structure
|
||||
|
||||
## src/ - contains the c++ and objective-c.
|
||||
* `BuildsRequest` - getting / parsing the build info from thunder api
|
||||
* `CommandlineOptions` - parses and stores commandline arguments
|
||||
* `Helper` - helper functions
|
||||
* `Helper_darwin` - objective-c implemention of helper funcions
|
||||
* `Helper_windows` - helper function that depend on windows api
|
||||
* `Launcher` - initialized the Launcher Application and resources
|
||||
* `LauncherInstaller_windows` - logic of how to install/uninstall HQ Launcher on windows
|
||||
* `LauncherState` - hold majority of the logic of the launcher (signin, config file, updating, running launcher)
|
||||
* config files hold the following saved data
|
||||
* logged in
|
||||
* home location
|
||||
* `LauncherWindows` - wrapper for `QQuickWindow` that implements drag feature
|
||||
* `LoginRequest` - checks the login credentials the user typed in.
|
||||
* `NSTask+NSTaskExecveAdditions` - Extension of NSTask for replacing Launcher process with interface client process
|
||||
* `PathUtils` - Helper class for getting relative paths for HQ Launcher
|
||||
* `SignupRequest` - Determines if the users request to signup for a new account succeeded based on the entered credentials
|
||||
* `Unzipper` - helper class for extracting zip files
|
||||
* `UserSettingsRequest` - getting the users setting (home location) from metaverse
|
||||
|
||||
## resources/
|
||||
* `images/`- Holds the images and icon that are used by the launcher
|
||||
* `qml/`
|
||||
* UI elements
|
||||
* `QML_FILE_FOR_UI_STATE` variable in `LauncherState` defines what QML files are used by the Launcher.
|
|
@ -38,6 +38,10 @@ class AnimationCacheScriptingInterface : public ScriptableResourceCache, public
|
|||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @borrows ResourceCache.getResourceList as getResourceList
|
||||
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||
|
|
|
@ -335,9 +335,9 @@ AudioClient::AudioClient() {
|
|||
connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat);
|
||||
|
||||
// initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash
|
||||
getAvailableDevices(QAudio::AudioInput, QString());
|
||||
getAvailableDevices(QAudio::AudioOutput, QString());
|
||||
|
||||
defaultAudioDeviceName(QAudio::AudioInput);
|
||||
defaultAudioDeviceName(QAudio::AudioOutput);
|
||||
|
||||
// start a thread to detect any device changes
|
||||
_checkDevicesTimer = new QTimer(this);
|
||||
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
|
||||
|
@ -787,8 +787,11 @@ void AudioClient::start() {
|
|||
inputName = _hmdInputName;
|
||||
outputName = _hmdOutputName;
|
||||
}
|
||||
|
||||
//initialize input to the dummy device to prevent starves
|
||||
switchInputToAudioDevice(HifiAudioDeviceInfo());
|
||||
switchOutputToAudioDevice(defaultAudioDeviceForMode(QAudio::AudioOutput, QString()));
|
||||
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
connect(&_checkInputTimer, &QTimer::timeout, this, &AudioClient::checkInputTimeout);
|
||||
_checkInputTimer.start(CHECK_INPUT_READS_MSECS);
|
||||
|
@ -2064,6 +2067,11 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi
|
|||
Lock localAudioLock(_localAudioMutex);
|
||||
_localSamplesAvailable.exchange(0, std::memory_order_release);
|
||||
|
||||
//wait on local injectors prep to finish running
|
||||
if ( !_localPrepInjectorFuture.isFinished()) {
|
||||
_localPrepInjectorFuture.waitForFinished();
|
||||
}
|
||||
|
||||
// cleanup any previously initialized device
|
||||
if (_audioOutput) {
|
||||
_audioOutputIODevice.close();
|
||||
|
@ -2342,9 +2350,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, _localInjectorsStream.samplesAvailable(), samplesRequested);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// prepare injectors for the next callback
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
_audio->_localPrepInjectorFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
_audio->prepareLocalAudioInjectors();
|
||||
});
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
|
@ -506,7 +507,8 @@ private:
|
|||
#endif
|
||||
|
||||
AudioSolo _solo;
|
||||
|
||||
|
||||
QFuture<void> _localPrepInjectorFuture;
|
||||
QReadWriteLock _hmdNameLock;
|
||||
Mutex _checkDevicesMutex;
|
||||
QTimer* _checkDevicesTimer { nullptr };
|
||||
|
|
|
@ -39,6 +39,10 @@ class SoundCacheScriptingInterface : public ScriptableResourceCache, public Depe
|
|||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @borrows ResourceCache.getResourceList as getResourceList
|
||||
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||
|
|
|
@ -299,6 +299,16 @@ void EntityItemProperties::setAvatarPriorityFromString(const QString& mode) {
|
|||
}
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getScreenshareAsString() const { return getComponentModeAsString(_screenshare); }
|
||||
void EntityItemProperties::setScreenshareFromString(const QString& mode) {
|
||||
auto modeItr = stringToComponentMode.find(mode.toLower());
|
||||
if (modeItr != stringToComponentMode.end()) {
|
||||
_screenshare = modeItr.value();
|
||||
_screenshareChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void addTextEffect(QHash<QString, TextEffect>& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; }
|
||||
const QHash<QString, TextEffect> stringToTextEffectLookup = [] {
|
||||
QHash<QString, TextEffect> toReturn;
|
||||
|
@ -566,6 +576,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SCREENSHARE, screenshare);
|
||||
|
||||
// Polyvox
|
||||
CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize);
|
||||
|
@ -1429,6 +1440,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the
|
||||
* zone to other clients.
|
||||
*
|
||||
* @property {Entities.ScreenshareMode} screenshare="inherit" - Configures a zone for screen-sharing.
|
||||
*
|
||||
* @example <caption>Create a zone that casts a red key light along the x-axis.</caption>
|
||||
* var zone = Entities.addEntity({
|
||||
* type: "Zone",
|
||||
|
@ -1779,6 +1792,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AVATAR_PRIORITY, avatarPriority, getAvatarPriorityAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SCREENSHARE, screenshare, getScreenshareAsString());
|
||||
}
|
||||
|
||||
// Web only
|
||||
|
@ -2150,6 +2164,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(avatarPriority, AvatarPriority);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(screenshare, Screenshare);
|
||||
|
||||
// Polyvox
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize);
|
||||
|
@ -2438,6 +2453,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(hazeMode);
|
||||
COPY_PROPERTY_IF_CHANGED(bloomMode);
|
||||
COPY_PROPERTY_IF_CHANGED(avatarPriority);
|
||||
COPY_PROPERTY_IF_CHANGED(screenshare);
|
||||
|
||||
// Polyvox
|
||||
COPY_PROPERTY_IF_CHANGED(voxelVolumeSize);
|
||||
|
@ -2834,6 +2850,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t);
|
||||
|
||||
// Polyvox
|
||||
ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3);
|
||||
|
@ -3252,6 +3269,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, (uint32_t)properties.getAvatarPriority());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, (uint32_t)properties.getScreenshare());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::PolyVox) {
|
||||
|
@ -3726,6 +3744,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCREENSHARE, uint32_t, setScreenshare);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::PolyVox) {
|
||||
|
@ -4117,6 +4136,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_hazeModeChanged = true;
|
||||
_bloomModeChanged = true;
|
||||
_avatarPriorityChanged = true;
|
||||
_screenshareChanged = true;
|
||||
|
||||
// Polyvox
|
||||
_voxelVolumeSizeChanged = true;
|
||||
|
@ -4739,6 +4759,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (avatarPriorityChanged()) {
|
||||
out += "avatarPriority";
|
||||
}
|
||||
if (screenshareChanged()) {
|
||||
out += "screenshare";
|
||||
}
|
||||
|
||||
// Polyvox
|
||||
if (voxelVolumeSizeChanged()) {
|
||||
|
|
|
@ -337,6 +337,7 @@ public:
|
|||
DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
|
||||
// Polyvox
|
||||
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);
|
||||
|
@ -699,6 +700,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AvatarPriority, avatarPriority, "");
|
||||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Screenshare, screenshare, "");
|
||||
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, "");
|
||||
|
||||
|
|
|
@ -161,6 +161,7 @@ enum EntityPropertyList {
|
|||
PROP_DERIVED_31,
|
||||
PROP_DERIVED_32,
|
||||
PROP_DERIVED_33,
|
||||
PROP_DERIVED_34,
|
||||
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
||||
|
@ -290,6 +291,8 @@ enum EntityPropertyList {
|
|||
PROP_BLOOM_MODE = PROP_DERIVED_32,
|
||||
// Avatar priority
|
||||
PROP_AVATAR_PRIORITY = PROP_DERIVED_33,
|
||||
// Screen-sharing
|
||||
PROP_SCREENSHARE = PROP_DERIVED_34,
|
||||
|
||||
// Polyvox
|
||||
PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0,
|
||||
|
|
|
@ -71,6 +71,7 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(screenshare, getScreenshare);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
@ -118,6 +119,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare);
|
||||
|
||||
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
|
||||
_skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
|
||||
|
@ -194,6 +196,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode);
|
||||
READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode);
|
||||
READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority);
|
||||
READ_ENTITY_PROPERTY(PROP_SCREENSHARE, uint32_t, setScreenshare);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
@ -214,6 +217,7 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
requestedProperties += PROP_GHOSTING_ALLOWED;
|
||||
requestedProperties += PROP_FILTER_URL;
|
||||
requestedProperties += PROP_AVATAR_PRIORITY;
|
||||
requestedProperties += PROP_SCREENSHARE;
|
||||
|
||||
requestedProperties += PROP_KEY_LIGHT_MODE;
|
||||
requestedProperties += PROP_AMBIENT_LIGHT_MODE;
|
||||
|
@ -260,6 +264,7 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, getScreenshare());
|
||||
}
|
||||
|
||||
void ZoneEntityItem::debugDump() const {
|
||||
|
@ -471,9 +476,9 @@ bool ZoneEntityItem::matchesJSONFilters(const QJsonObject& jsonFilters) const {
|
|||
|
||||
static const QString AVATAR_PRIORITY_PROPERTY = "avatarPriority";
|
||||
|
||||
// If set ignore only priority-inherit zones:
|
||||
// If set match zones of interest to avatar mixer:
|
||||
if (jsonFilters.contains(AVATAR_PRIORITY_PROPERTY) && jsonFilters[AVATAR_PRIORITY_PROPERTY].toBool()
|
||||
&& _avatarPriority != COMPONENT_MODE_INHERIT) {
|
||||
&& (_avatarPriority != COMPONENT_MODE_INHERIT || _screenshare != COMPONENT_MODE_INHERIT)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,9 @@ public:
|
|||
uint32_t getAvatarPriority() const { return _avatarPriority; }
|
||||
void setAvatarPriority(uint32_t value) { _avatarPriority = value; }
|
||||
|
||||
uint32_t getScreenshare() const { return _screenshare; }
|
||||
void setScreenshare(uint32_t value) { _screenshare = value; }
|
||||
|
||||
bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
|
||||
bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
|
||||
bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; }
|
||||
|
@ -156,6 +159,9 @@ protected:
|
|||
// Avatar-updates priority
|
||||
uint32_t _avatarPriority { COMPONENT_MODE_INHERIT };
|
||||
|
||||
// Screen-sharing zone
|
||||
uint32_t _screenshare { COMPONENT_MODE_INHERIT };
|
||||
|
||||
// Dirty flags turn true when either keylight properties is changing values.
|
||||
bool _keyLightPropertiesChanged { false };
|
||||
bool _ambientLightPropertiesChanged { false };
|
||||
|
|
|
@ -138,23 +138,28 @@ bool KeyboardMouseDevice::isWheelByTouchPad(QWheelEvent* event) {
|
|||
QPoint delta = event->angleDelta();
|
||||
int deltaValueX = abs(delta.x());
|
||||
int deltaValueY = abs(delta.y());
|
||||
const int MAX_WHEEL_DELTA_REPEAT = 20;
|
||||
const int COMMON_WHEEL_DELTA_VALUE = 120;
|
||||
if (deltaValueX != 0) {
|
||||
if (abs(_lastWheelDelta.x()) == deltaValueX) {
|
||||
_wheelDeltaRepeatCount.setX(_wheelDeltaRepeatCount.x() + 1);
|
||||
} else {
|
||||
_wheelDeltaRepeatCount.setX(0);
|
||||
// If deltaValueX or deltaValueY are multiple of 120 they are triggered by a mouse wheel
|
||||
bool isMouseWheel = (deltaValueX + deltaValueY) % COMMON_WHEEL_DELTA_VALUE == 0;
|
||||
if (!isMouseWheel) {
|
||||
// We track repetition in wheel values to detect non-standard mouse wheels
|
||||
const int MAX_WHEEL_DELTA_REPEAT = 10;
|
||||
if (deltaValueX != 0) {
|
||||
if (abs(_lastWheelDelta.x()) == deltaValueX) {
|
||||
_wheelDeltaRepeatCount.setX(_wheelDeltaRepeatCount.x() + 1);
|
||||
} else {
|
||||
_wheelDeltaRepeatCount.setX(0);
|
||||
}
|
||||
return _wheelDeltaRepeatCount.x() < MAX_WHEEL_DELTA_REPEAT;
|
||||
}
|
||||
return deltaValueX != COMMON_WHEEL_DELTA_VALUE && _wheelDeltaRepeatCount.x() < MAX_WHEEL_DELTA_REPEAT;
|
||||
}
|
||||
if (deltaValueY != 0) {
|
||||
if (abs(_lastWheelDelta.y()) == deltaValueY) {
|
||||
_wheelDeltaRepeatCount.setY(_wheelDeltaRepeatCount.y() + 1);
|
||||
} else {
|
||||
_wheelDeltaRepeatCount.setY(0);
|
||||
if (deltaValueY != 0) {
|
||||
if (abs(_lastWheelDelta.y()) == deltaValueY) {
|
||||
_wheelDeltaRepeatCount.setY(_wheelDeltaRepeatCount.y() + 1);
|
||||
} else {
|
||||
_wheelDeltaRepeatCount.setY(0);
|
||||
}
|
||||
return _wheelDeltaRepeatCount.y() < MAX_WHEEL_DELTA_REPEAT;
|
||||
}
|
||||
return deltaValueY != COMMON_WHEEL_DELTA_VALUE && _wheelDeltaRepeatCount.y() < MAX_WHEEL_DELTA_REPEAT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -166,8 +171,9 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) {
|
|||
QPoint delta = event->angleDelta();
|
||||
float deltaX = (float)delta.x();
|
||||
float deltaY = (float)delta.y();
|
||||
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()].value = (deltaX > 0 ? deltaX : 0.0f);
|
||||
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()].value = (deltaX < 0 ? -deltaX : 0.0f);
|
||||
const float WHEEL_X_ATTENUATION = 0.3f;
|
||||
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()].value = (deltaX > 0 ? WHEEL_X_ATTENUATION * deltaX : 0.0f);
|
||||
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()].value = (deltaX < 0 ? -WHEEL_X_ATTENUATION * deltaX : 0.0f);
|
||||
// Y mouse is inverted positive is pointing up the screen
|
||||
const float WHEEL_Y_ATTENUATION = 0.02f;
|
||||
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()].value = (deltaY < 0 ? -WHEEL_Y_ATTENUATION * deltaY : 0.0f);
|
||||
|
|
|
@ -37,6 +37,10 @@ class TextureCacheScriptingInterface : public ScriptableResourceCache, public De
|
|||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @borrows ResourceCache.getResourceList as getResourceList
|
||||
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||
|
|
|
@ -37,6 +37,10 @@ class ModelCacheScriptingInterface : public ScriptableResourceCache, public Depe
|
|||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @borrows ResourceCache.getResourceList as getResourceList
|
||||
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||
|
|
|
@ -52,11 +52,13 @@ const int PULL_SETTINGS_RETRY_INTERVAL = 2 * MSECS_PER_SECOND;
|
|||
const int MAX_PULL_RETRIES = 10;
|
||||
|
||||
JSONCallbackParameters::JSONCallbackParameters(QObject* callbackReceiver,
|
||||
const QString& jsonCallbackMethod,
|
||||
const QString& errorCallbackMethod) :
|
||||
const QString& jsonCallbackMethod,
|
||||
const QString& errorCallbackMethod,
|
||||
const QJsonObject& callbackData) :
|
||||
callbackReceiver(callbackReceiver),
|
||||
jsonCallbackMethod(jsonCallbackMethod),
|
||||
errorCallbackMethod(errorCallbackMethod)
|
||||
errorCallbackMethod(errorCallbackMethod),
|
||||
callbackData(callbackData)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -348,9 +350,17 @@ void AccountManager::sendRequest(const QString& path,
|
|||
[callbackParams, networkReply] {
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (!callbackParams.jsonCallbackMethod.isEmpty()) {
|
||||
bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
|
||||
qPrintable(callbackParams.jsonCallbackMethod),
|
||||
Q_ARG(QNetworkReply*, networkReply));
|
||||
bool invoked = false;
|
||||
if (callbackParams.callbackData.isEmpty()) {
|
||||
invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
|
||||
qPrintable(callbackParams.jsonCallbackMethod),
|
||||
Q_ARG(QNetworkReply*, networkReply));
|
||||
} else {
|
||||
invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
|
||||
qPrintable(callbackParams.jsonCallbackMethod),
|
||||
Q_ARG(QNetworkReply*, networkReply),
|
||||
Q_ARG(QJsonObject, callbackParams.callbackData));
|
||||
}
|
||||
|
||||
if (!invoked) {
|
||||
QString error = "Could not invoke " + callbackParams.jsonCallbackMethod + " with QNetworkReply* "
|
||||
|
@ -366,9 +376,18 @@ void AccountManager::sendRequest(const QString& path,
|
|||
}
|
||||
} else {
|
||||
if (!callbackParams.errorCallbackMethod.isEmpty()) {
|
||||
bool invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
|
||||
qPrintable(callbackParams.errorCallbackMethod),
|
||||
Q_ARG(QNetworkReply*, networkReply));
|
||||
bool invoked = false;
|
||||
if (callbackParams.callbackData.isEmpty()) {
|
||||
invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
|
||||
qPrintable(callbackParams.errorCallbackMethod),
|
||||
Q_ARG(QNetworkReply*, networkReply));
|
||||
}
|
||||
else {
|
||||
invoked = QMetaObject::invokeMethod(callbackParams.callbackReceiver,
|
||||
qPrintable(callbackParams.errorCallbackMethod),
|
||||
Q_ARG(QNetworkReply*, networkReply),
|
||||
Q_ARG(QJsonObject, callbackParams.callbackData));
|
||||
}
|
||||
|
||||
if (!invoked) {
|
||||
QString error = "Could not invoke " + callbackParams.errorCallbackMethod + " with QNetworkReply* "
|
||||
|
|
|
@ -30,14 +30,16 @@
|
|||
class JSONCallbackParameters {
|
||||
public:
|
||||
JSONCallbackParameters(QObject* callbackReceiver = nullptr,
|
||||
const QString& jsonCallbackMethod = QString(),
|
||||
const QString& errorCallbackMethod = QString());
|
||||
const QString& jsonCallbackMethod = QString(),
|
||||
const QString& errorCallbackMethod = QString(),
|
||||
const QJsonObject& callbackData = QJsonObject());
|
||||
|
||||
bool isEmpty() const { return !callbackReceiver; }
|
||||
|
||||
QObject* callbackReceiver;
|
||||
QString jsonCallbackMethod;
|
||||
QString errorCallbackMethod;
|
||||
QJsonObject callbackData;
|
||||
};
|
||||
|
||||
namespace AccountManagerAuth {
|
||||
|
|
|
@ -68,6 +68,66 @@ const QString GET_PLACE = "/api/v1/places/%1";
|
|||
* <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* The <code>AddressManager</code> API provides facilities related to your current location in the metaverse.
|
||||
*
|
||||
* @namespace AddressManager
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
*
|
||||
* @deprecated This API is deprecated and will be removed. Use the {@link location} or {@link Window|Window.location} APIs
|
||||
* instead.
|
||||
*
|
||||
* @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid(0)|Uuid.NULL} if you're not
|
||||
* connected to the domain or are in a serverless domain.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} hostname - The name of the domain for your current metaverse address (e.g., <code>"DomainName"</code>,
|
||||
* <code>localhost</code>, or an IP address). Is blank if you're in a serverless domain.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} href - Your current metaverse address (e.g., <code>"hifi://domainname/15,-10,26/0,0,0,1"</code>)
|
||||
* regardless of whether or not you're connected to the domain. Starts with <code>"file:///"</code> if you're in a
|
||||
* serverless domain.
|
||||
* <em>Read-only.</em>
|
||||
* @property {boolean} isConnected - <code>true</code> if you're connected to the domain in your current <code>href</code>
|
||||
* metaverse address, otherwise <code>false</code>.
|
||||
* @property {string} pathname - The location and orientation in your current <code>href</code> metaverse address
|
||||
* (e.g., <code>"/15,-10,26/0,0,0,1"</code>).
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} placename - The place name in your current <code>href</code> metaverse address
|
||||
* (e.g., <code>"DomainName"</code>). Is blank if your <code>hostname</code> is an IP address.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} protocol - The protocol of your current <code>href</code> metaverse address (e.g., <code>"hifi"</code>).
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @borrows location.handleLookupString as handleLookupString
|
||||
* @borrows location.goToViewpointForPath as goToViewpointForPath
|
||||
* @borrows location.goBack as goBack
|
||||
* @borrows location.goForward as goForward
|
||||
* @borrows location.goToLocalSandbox as goToLocalSandbox
|
||||
* @borrows location.goToEntry as goToEntry
|
||||
* @borrows location.goToUser as goToUser
|
||||
* @borrows location.goToLastAddress as goToLastAddress
|
||||
* @borrows location.canGoBack as canGoBack
|
||||
* @borrows location.refreshPreviousLookup as refreshPreviousLookup
|
||||
* @borrows location.storeCurrentAddress as storeCurrentAddress
|
||||
* @borrows location.copyAddress as copyAddress
|
||||
* @borrows location.copyPath as copyPath
|
||||
* @borrows location.lookupShareableNameForDomainID as lookupShareableNameForDomainID
|
||||
*
|
||||
* @borrows location.lookupResultsFinished as lookupResultsFinished
|
||||
* @borrows location.lookupResultIsOffline as lookupResultIsOffline
|
||||
* @borrows location.lookupResultIsNotFound as lookupResultIsNotFound
|
||||
* @borrows location.possibleDomainChangeRequired as possibleDomainChangeRequired
|
||||
* @borrows location.locationChangeRequired as locationChangeRequired
|
||||
* @borrows location.possibleDomainChangeRequiredViaICEForID as possibleDomainChangeRequiredViaICEForID
|
||||
* @borrows location.pathChangeRequired as pathChangeRequired
|
||||
* @borrows location.hostChanged as hostChanged
|
||||
* @borrows location.goBackPossible as goBackPossible
|
||||
* @borrows location.goForwardPossible as goForwardPossible
|
||||
*/
|
||||
|
||||
class AddressManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
|
|
@ -91,8 +91,8 @@ private:
|
|||
class ScriptableResource : public QObject {
|
||||
|
||||
/**jsdoc
|
||||
* Information about a cached resource. Created by {@link AnimationCache.prefetch}, {@link ModelCache.prefetch},
|
||||
* {@link SoundCache.prefetch}, or {@link TextureCache.prefetch}.
|
||||
* Information about a cached resource. Created by {@link AnimationCache.prefetch}, {@link MaterialCache.prefetch},
|
||||
* {@link ModelCache.prefetch}, {@link SoundCache.prefetch}, or {@link TextureCache.prefetch}.
|
||||
*
|
||||
* @class ResourceObject
|
||||
*
|
||||
|
@ -318,9 +318,11 @@ class ScriptableResourceCache : public QObject {
|
|||
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
|
||||
|
||||
/**jsdoc
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource managers). <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource managers). <em>Read-only.</em>
|
||||
*/
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
*/
|
||||
Q_PROPERTY(size_t numGlobalQueriesPending READ getNumGlobalQueriesPending NOTIFY dirty)
|
||||
Q_PROPERTY(size_t numGlobalQueriesLoading READ getNumGlobalQueriesLoading NOTIFY dirty)
|
||||
|
||||
|
@ -332,7 +334,7 @@ public:
|
|||
* @function ResourceCache.getResourceList
|
||||
* @returns {string[]} The URLs of all resources in the cache.
|
||||
* @example <caption>Report cached resources.</caption>
|
||||
* // Replace AnimationCache with ModelCache, SoundCache, or TextureCache as appropriate.
|
||||
* // Replace AnimationCache with MaterialCache, ModelCache, SoundCache, or TextureCache as appropriate.
|
||||
*
|
||||
* var cachedResources = AnimationCache.getResourceList();
|
||||
* print("Cached resources: " + JSON.stringify(cachedResources));
|
||||
|
@ -352,7 +354,7 @@ public:
|
|||
* @param {string} url - The URL of the resource to prefetch.
|
||||
* @returns {ResourceObject} A resource object.
|
||||
* @example <caption>Prefetch a resource and wait until it has loaded.</caption>
|
||||
* // Replace AnimationCache with ModelCache, SoundCache, or TextureCache as appropriate.
|
||||
* // Replace AnimationCache with MaterialCache, ModelCache, SoundCache, or TextureCache as appropriate.
|
||||
* // TextureCache has its own version of this function.
|
||||
*
|
||||
* var resourceURL = "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/animations/sitting_idle.fbx";
|
||||
|
|
|
@ -277,6 +277,7 @@ enum class EntityVersion : PacketVersion {
|
|||
ShadowBiasAndDistance,
|
||||
TextEntityFonts,
|
||||
ScriptServerKinematicMotion,
|
||||
ScreenshareZone,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
|
@ -93,7 +93,7 @@ public:
|
|||
* collisions.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
/**jsdoc
|
||||
* <p>A type of pick.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
|
|
|
@ -24,7 +24,7 @@ class MaterialCacheScriptingInterface : public ScriptableResourceCache, public D
|
|||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* The <code>TextureCache</code> API manages texture cache resources.
|
||||
* The <code>MaterialCache</code> API manages material cache resources.
|
||||
*
|
||||
* @namespace MaterialCache
|
||||
*
|
||||
|
@ -36,6 +36,10 @@ class MaterialCacheScriptingInterface : public ScriptableResourceCache, public D
|
|||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource cache managers).
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @borrows ResourceCache.getResourceList as getResourceList
|
||||
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||
|
|
|
@ -113,9 +113,9 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
|
|||
/**jsdoc
|
||||
* A material used in a {@link Entities.MaterialResource|MaterialResource}.
|
||||
* @typedef {object} Entities.Material
|
||||
* @property {string} name="" - A name for the material. Supported by all material models.
|
||||
* @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes.
|
||||
* Supported models are: <code>"hifi_pbr"</code>, <code>"hifi_shader_simple"</code>.
|
||||
* @property {string} name="" - A name for the material. Supported by all material models.
|
||||
* @property {ColorFloat|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A
|
||||
* {@link ColorFloat} value is treated as sRGB and must have component values in the range <code>0.0</code> –
|
||||
* <code>1.0</code>. A {@link RGBS} value can be either RGB or sRGB.
|
||||
|
@ -144,12 +144,17 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
|
|||
* value for transparency.
|
||||
* <code>"hifi_pbr"</code> model only.
|
||||
* @property {string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be:
|
||||
* <code>"OPACITY_MAP_OPAQUE"</code> for ignoring the opacity map information.
|
||||
* <code>"OPACITY_MAP_MASK"</code> for using the opacity map as a mask, where only the texel greater than opacityCutoff are visible and rendered opaque.
|
||||
* <code>"OPACITY_MAP_BLEND"</code> for using the opacity map for alpha blending the material surface with the background.
|
||||
* <ul>
|
||||
* <li><code>"OPACITY_MAP_OPAQUE"</code> for ignoring the <code>opacityMap</code> information.</li>
|
||||
* <li><code>"OPACITY_MAP_MASK"</code> for using the <code>opacityMap</code> as a mask, where only the texel greater
|
||||
* than <code>opacityCutoff</code> are visible and rendered opaque.</li>
|
||||
* <li><code>"OPACITY_MAP_BLEND"</code> for using the <code>opacityMap</code> for alpha blending the material surface
|
||||
* with the background.</li>
|
||||
* </ul>
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the Opacity map
|
||||
* when opacityMapMode is "OPACITY_MAP_MASK", range <code>0.0</code> – <code>1.0</code>.
|
||||
* @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the
|
||||
* <code>opacityMap</code> when <code>opacityMapMode</code> is <code>"OPACITY_MAP_MASK"</code>, range <code>0.0</code>
|
||||
* – <code>1.0</code>.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {string} cullFaceMode - The mode defining which side of the geometry should be rendered. Values can be:
|
||||
* <ul>
|
||||
|
@ -186,7 +191,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
|
|||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {Mat4|string} texCoordTransform1 - The transform to use for <code>occlusionMap</code> and <code>lightMap</code>.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {string} lightmapParams - Parameters for controlling how lightMap is used.
|
||||
* @property {string} lightmapParams - Parameters for controlling how <code>lightMap</code> is used.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* <p><em>Currently not used.</em></p>
|
||||
* @property {string} materialParams - Parameters for controlling the material projection and repetition.
|
||||
|
|
|
@ -26,25 +26,190 @@
|
|||
#include <QtCore/QHash>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
/**jsdoc
|
||||
* The <code>console</code> API provides program logging facilities.
|
||||
*
|
||||
* @namespace console
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*/
|
||||
// Scriptable interface of "console" object. Used exclusively in the JavaScript API
|
||||
class ConsoleScriptingInterface : public QObject, protected QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**jsdoc
|
||||
* Logs an "INFO" message to the program log and triggers {@link Script.infoMessage}.
|
||||
* The message logged is "INFO -" followed by the message values separated by spaces.
|
||||
* @function console.info
|
||||
* @param {...*} [message] - The message values to log.
|
||||
*/
|
||||
static QScriptValue info(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Logs a message to the program log and triggers {@link Script.printedMessage}.
|
||||
* The message logged is the message values separated by spaces.
|
||||
* <p>If a {@link console.group} is in effect, the message is indented by an amount proportional to the group level.</p>
|
||||
* @function console.log
|
||||
* @param {...*} [message] - The message values to log.
|
||||
* @example <caption>Log some values.</caption>
|
||||
* Script.printedMessage.connect(function (message, scriptName) {
|
||||
* console.info("Console.log message:", "\"" + message + "\"", "in", "[" + scriptName + "]");
|
||||
* });
|
||||
*
|
||||
* console.log("string", 7, true);
|
||||
*
|
||||
* // string 7 true
|
||||
* // INFO - Console.log message: "string 7 true" in [console.js]
|
||||
*/
|
||||
static QScriptValue log(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Logs a message to the program log and triggers {@link Script.printedMessage}.
|
||||
* The message logged is the message values separated by spaces.
|
||||
* @function console.debug
|
||||
* @param {...*} [message] - The message values to log.
|
||||
*/
|
||||
static QScriptValue debug(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Logs a "WARNING" message to the program log and triggers {@link Script.warningMessage}.
|
||||
* The message logged is "WARNING - " followed by the message values separated by spaces.
|
||||
* @function console.warn
|
||||
* @param {...*} [message] - The message values to log.
|
||||
*/
|
||||
static QScriptValue warn(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}.
|
||||
* The message logged is "ERROR - " followed by the message values separated by spaces.
|
||||
* @function console.error
|
||||
* @param {...*} [message] - The message values to log.
|
||||
*/
|
||||
static QScriptValue error(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* A synonym of {@link console.error}.
|
||||
* Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}.
|
||||
* The message logged is "ERROR - " followed by the message values separated by spaces.
|
||||
* @function console.exception
|
||||
* @param {...*} [message] - The message values to log.
|
||||
*/
|
||||
static QScriptValue exception(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Logs an "ERROR" message to the program log and triggers {@link Script.errorMessage}, if a test condition fails.
|
||||
* The message logged is "ERROR - Assertion failed : " followed by the message values separated by spaces.
|
||||
* <p>Note: Script execution continues whether or not the test condition fails.</p>
|
||||
* @function console.assert
|
||||
* @param {boolean} assertion - The test condition value.
|
||||
* @param {...*} [message] - The message values to log if the assertion evaluates to <code>false</code>.
|
||||
* @example <caption>Demonstrate assertion behavior.</caption>
|
||||
* Script.errorMessage.connect(function (message, scriptName) {
|
||||
* console.info("Error message logged:", "\"" + message + "\"", "in", "[" + scriptName + "]");
|
||||
* });
|
||||
*
|
||||
* console.assert(5 === 5, "5 equals 5.", "This assertion will succeed."); // No log output.
|
||||
* console.assert(5 > 6, "5 is not greater than 6.", "This assertion will fail."); // Log output is generated.
|
||||
* console.info("Script continues running.");
|
||||
*
|
||||
* // ERROR - Assertion failed : 5 is not greater than 6. This assertion will fail.
|
||||
* // INFO - Error message logged: "Assertion failed : 5 is not greater than 6. This assertion will fail." in [console.js]
|
||||
* // INFO - Script continues running.
|
||||
*/
|
||||
// Note: Is registered in the script engine as "assert"
|
||||
static QScriptValue assertion(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Logs a message to the program log and triggers {@link Script.printedMessage}, then starts indenting subsequent
|
||||
* {@link console.log} messages until a {@link console.groupEnd}. Groups may be nested.
|
||||
* @function console.group
|
||||
* @param {*} message - The message value to log.
|
||||
* @example <caption>Nested groups.</caption>
|
||||
* console.group("Group 1");
|
||||
* console.log("Sentence 1");
|
||||
* console.log("Sentence 2");
|
||||
* console.group("Group 2");
|
||||
* console.log("Sentence 3");
|
||||
* console.log("Sentence 4");
|
||||
* console.groupEnd();
|
||||
* console.log("Sentence 5");
|
||||
* console.groupEnd();
|
||||
* console.log("Sentence 6");
|
||||
*
|
||||
* //Group 1
|
||||
* // Sentence 1
|
||||
* // Sentence 2
|
||||
* // Group 2
|
||||
* // Sentence 3
|
||||
* // Sentence 4
|
||||
* // Sentence 5
|
||||
* //Sentence 6
|
||||
*/
|
||||
static QScriptValue group(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Has the same behavior as {@link console.group}.
|
||||
* Logs a message to the program log and triggers {@link Script.printedMessage}, then starts indenting subsequent
|
||||
* {@link console.log} messages until a {@link console.groupEnd}. Groups may be nested.
|
||||
* @function console.groupCollapsed
|
||||
* @param {*} message - The message value to log.
|
||||
*/
|
||||
static QScriptValue groupCollapsed(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Finishes a group of indented {@link console.log} messages.
|
||||
* @function console.groupEnd
|
||||
*/
|
||||
static QScriptValue groupEnd(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Starts a timer, logs a message to the program log, and triggers {@link Script.printedMessage}. The message logged has
|
||||
* the timer name and "Timer started".
|
||||
* @function console.time
|
||||
* @param {string} name - The name of the timer.
|
||||
* @example <caption>Time some processing.</caption>
|
||||
* function sleep(milliseconds) {
|
||||
* var start = new Date().getTime();
|
||||
* for (var i = 0; i < 1e7; i++) {
|
||||
* if ((new Date().getTime() - start) > milliseconds) {
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* console.time("MyTimer");
|
||||
* sleep(1000); // Do some processing.
|
||||
* console.timeEnd("MyTimer");
|
||||
*
|
||||
* // MyTimer: Timer started
|
||||
* // MyTimer: 1001ms */
|
||||
void time(QString labelName);
|
||||
|
||||
/**jsdoc
|
||||
* Finishes a timer, logs a message to the program log, and triggers {@link Script.printedMessage}. The message logged has
|
||||
* the timer name and the number of milliseconds since the timer was started.
|
||||
* @function console.timeEnd
|
||||
* @param {string} name - The name of the timer.
|
||||
*/
|
||||
void timeEnd(QString labelName);
|
||||
|
||||
/**jsdoc
|
||||
* Logs the JavaScript call stack at the point of call to the program log.
|
||||
* @function console.trace
|
||||
*/
|
||||
void trace();
|
||||
|
||||
/**jsdoc
|
||||
* Clears the Developer > Scripting > Script Log debug window.
|
||||
* @function console.clear
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
|
|
|
@ -731,6 +731,13 @@ void ScriptEngine::init() {
|
|||
QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor);
|
||||
globalObject().setProperty("WebSocket", webSocketConstructorValue);
|
||||
|
||||
/**jsdoc
|
||||
* Prints a message to the program log and emits {@link Script.printedMessage}.
|
||||
* The message logged is the message values separated by spaces.
|
||||
* <p>Alternatively, you can use {@link Script.print} or one of the {@link console} API methods.</p>
|
||||
* @function print
|
||||
* @param {...*} [message] - The message values to print.
|
||||
*/
|
||||
globalObject().setProperty("print", newFunction(debugPrint));
|
||||
|
||||
QScriptValue audioEffectOptionsConstructorValue = newFunction(AudioEffectOptions::constructor);
|
||||
|
|
|
@ -499,17 +499,11 @@ public:
|
|||
Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
|
||||
|
||||
/**jsdoc
|
||||
* Prints a message to the program log.
|
||||
* <p>Alternatively, you can use {@link print}, {@link console.log}, or one of the other {@link console} methods.</p>
|
||||
* Prints a message to the program log and emits {@link Script.printedMessage}.
|
||||
* <p>Alternatively, you can use {@link print} or one of the {@link console} API methods.</p>
|
||||
* @function Script.print
|
||||
* @param {string} message - The message to print.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Prints a message to the program log.
|
||||
* <p>This is an alias of {@link Script.print}.</p>
|
||||
* @function print
|
||||
* @param {string} message - The message to print.
|
||||
*/
|
||||
Q_INVOKABLE void print(const QString& message);
|
||||
|
||||
/**jsdoc
|
||||
|
@ -763,7 +757,8 @@ signals:
|
|||
|
||||
/**jsdoc
|
||||
* Triggered when the script prints a message to the program log via {@link print}, {@link Script.print},
|
||||
* {@link console.log}, or {@link console.debug}.
|
||||
* {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or
|
||||
* {@link console.timeEnd}.
|
||||
* @function Script.printedMessage
|
||||
* @param {string} message - The message.
|
||||
* @param {string} scriptName - The name of the script that generated the message.
|
||||
|
@ -772,7 +767,8 @@ signals:
|
|||
void printedMessage(const QString& message, const QString& scriptName);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the script generates an error or {@link console.error} is called.
|
||||
* Triggered when the script generates an error, {@link console.error} or {@link console.exception} is called, or
|
||||
* {@link console.assert} is called and fails.
|
||||
* @function Script.errorMessage
|
||||
* @param {string} message - The error message.
|
||||
* @param {string} scriptName - The name of the script that generated the error message.
|
||||
|
|
|
@ -33,13 +33,20 @@
|
|||
#include "MainWindow.h"
|
||||
|
||||
/**jsdoc
|
||||
* The <code>OffscreenFlags</code> API enables gamepad joystick navigation of UI.
|
||||
*
|
||||
* <p><em>This API currently has no effect and is not used.</em></p>
|
||||
*
|
||||
* @namespace OffscreenFlags
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-avatar
|
||||
* @property {boolean} navigationFocused
|
||||
* @property {boolean} navigationFocusDisabled
|
||||
*
|
||||
* @property {boolean} navigationFocused - <code>true</code> if UI has joystick navigation focus, <code>false</code> if it
|
||||
* doesn't.
|
||||
* @property {boolean} navigationFocusDisabled - <code>true</code> if UI joystick navigation focus is disabled,
|
||||
* <code>false</code> if it isn't.
|
||||
*/
|
||||
|
||||
// Needs to match the constants in resources/qml/Global.js
|
||||
|
@ -72,12 +79,14 @@ public:
|
|||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>navigationFocused</code> property changes.
|
||||
* @function OffscreenFlags.navigationFocusedChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void navigationFocusedChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>navigationFocusDisabled</code> property changes.
|
||||
* @function OffscreenFlags.navigationFocusDisabledChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
|
|
@ -23,13 +23,6 @@ if (WIN32)
|
|||
|
||||
set(EXECUTABLE_PATH "${SCREENSHARE_DESTINATION}/${SCREENSHARE_EXEC_NAME}")
|
||||
optional_win_executable_signing()
|
||||
elseif (APPLE)
|
||||
set(PACKAGED_SCREENSHARE_FOLDER "hifi-screenshare-darwin-x64/${SCREENSHARE_EXEC_NAME}")
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_SCREENSHARE_FOLDER}"
|
||||
USE_SOURCE_PERMISSIONS
|
||||
DESTINATION ${SCREENSHARE_INSTALL_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
# DO build the Screenshare Electron app when building the `ALL_BUILD` target.
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
const { remote } = require('electron');
|
||||
|
||||
// Helpers
|
||||
function handleError(error) {
|
||||
if (error) {
|
||||
|
@ -188,6 +190,10 @@ function stopSharing() {
|
|||
|
||||
// Callback to start publishing after we have setup the chromium stream
|
||||
function gotStream(stream) {
|
||||
if (localStream) {
|
||||
stopSharing();
|
||||
}
|
||||
|
||||
localStream = stream;
|
||||
startTokboxPublisher(localStream);
|
||||
|
||||
|
@ -206,6 +212,9 @@ function onAccessApproved(desktop_id) {
|
|||
console.log('Desktop Capture access rejected.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
document.getElementById('screenshare').style.visibility = "block";
|
||||
desktopSharing = true;
|
||||
navigator.webkitGetUserMedia({
|
||||
|
@ -214,13 +223,13 @@ function onAccessApproved(desktop_id) {
|
|||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: desktop_id,
|
||||
minWidth: 1280,
|
||||
maxWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxHeight: 720
|
||||
maxHeight: 720,
|
||||
maxFrameRate: 7
|
||||
}
|
||||
}
|
||||
}, gotStream, handleError);
|
||||
remote.getCurrentWindow().minimize();
|
||||
}
|
||||
|
||||
|
||||
|
@ -248,11 +257,15 @@ var publisher;
|
|||
function startTokboxPublisher(stream) {
|
||||
publisher = document.createElement("div");
|
||||
var publisherOptions = {
|
||||
videoSource: stream.getVideoTracks()[0],
|
||||
audioFallbackEnabled: false,
|
||||
audioSource: null,
|
||||
fitMode: 'contain',
|
||||
frameRate: 7,
|
||||
height: 720,
|
||||
insertMode: 'append',
|
||||
width: 1280,
|
||||
height: 720
|
||||
publishAudio: false,
|
||||
videoSource: stream.getVideoTracks()[0],
|
||||
width: 1280
|
||||
};
|
||||
|
||||
publisher = OT.initPublisher(publisher, publisherOptions, function(error){
|
||||
|
|
1344
scripts/developer/automaticLookAt.js
Normal file
1344
scripts/developer/automaticLookAt.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -147,6 +147,9 @@
|
|||
"avatarPriority": {
|
||||
"tooltip": "Alter Avatars' update priorities."
|
||||
},
|
||||
"screenshare": {
|
||||
"tooltip": "Enable screen-sharing within this zone"
|
||||
},
|
||||
"modelURL": {
|
||||
"tooltip": "A mesh model from an FBX or OBJ file."
|
||||
},
|
||||
|
|
|
@ -393,7 +393,8 @@ const DEFAULT_ENTITY_PROPERTIES = {
|
|||
},
|
||||
shapeType: "box",
|
||||
bloomMode: "inherit",
|
||||
avatarPriority: "inherit"
|
||||
avatarPriority: "inherit",
|
||||
screenshare: "inherit",
|
||||
},
|
||||
Model: {
|
||||
collisionShape: "none",
|
||||
|
|
|
@ -497,6 +497,12 @@ const GROUPS = [
|
|||
options: { inherit: "Inherit", crowd: "Crowd", hero: "Hero" },
|
||||
propertyID: "avatarPriority",
|
||||
},
|
||||
{
|
||||
label: "Screen-share",
|
||||
type: "dropdown",
|
||||
options: { inherit: "Inherit", disabled: "Off", enabled: "On" },
|
||||
propertyID: "screenshare",
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
|
|
1
tools/animedit/.gitignore
vendored
Normal file
1
tools/animedit/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.pro.user
|
4
tools/animedit/LICENCE
Normal file
4
tools/animedit/LICENCE
Normal file
|
@ -0,0 +1,4 @@
|
|||
Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
Distributed under the Apache License, Version 2.0.
|
||||
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
14
tools/animedit/README.md
Normal file
14
tools/animedit/README.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
animedit
|
||||
-------------
|
||||
avatar-animation.json editor for High Fidelity.
|
||||
|
||||
Use QtCreator to build.
|
||||
|
||||
Known issues:
|
||||
|
||||
* When switching node types, clear old types fields & set new fields to default values.
|
||||
* Name field does not change when it has focus and leftHandPane selection is changed.
|
||||
|
||||
|
||||
|
||||
|
35
tools/animedit/animedit.pro
Normal file
35
tools/animedit/animedit.pro
Normal file
|
@ -0,0 +1,35 @@
|
|||
QT += quick
|
||||
CONFIG += c++11
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Refer to the documentation for the
|
||||
# deprecated API to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
treeitem.cpp \
|
||||
treemodel.cpp
|
||||
|
||||
RESOURCES += qml.qrc
|
||||
|
||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||
QML_IMPORT_PATH =
|
||||
|
||||
# Additional import path used to resolve QML modules just for Qt Quick Designer
|
||||
QML_DESIGNER_IMPORT_PATH =
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
HEADERS += \
|
||||
treeitem.h \
|
||||
treemodel.h
|
36
tools/animedit/main.cpp
Normal file
36
tools/animedit/main.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include "treemodel.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
// expose model
|
||||
TreeModel model;
|
||||
engine.rootContext()->setContextProperty("theModel", &model);
|
||||
|
||||
const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl) {
|
||||
// failure loading main.qml
|
||||
QCoreApplication::exit(-1);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
engine.load(url);
|
||||
|
||||
return app.exec();
|
||||
}
|
26
tools/animedit/qml.qrc
Normal file
26
tools/animedit/qml.qrc
Normal file
|
@ -0,0 +1,26 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml/main.qml</file>
|
||||
<file>qml/TreeDelegate.qml</file>
|
||||
<file>qml/fields/BooleanField.qml</file>
|
||||
<file>qml/fields/IdField.qml</file>
|
||||
<file>qml/fields/JSONField.qml</file>
|
||||
<file>qml/fields/NumberArrayField.qml</file>
|
||||
<file>qml/fields/NumberField.qml</file>
|
||||
<file>qml/fields/StringField.qml</file>
|
||||
<file>qml/fields/TypeField.qml</file>
|
||||
<file>qml/nodes/BlendDirectional.qml</file>
|
||||
<file>qml/nodes/BlendLinear.qml</file>
|
||||
<file>qml/nodes/BlendLinearMove.qml</file>
|
||||
<file>qml/nodes/ClipData.qml</file>
|
||||
<file>qml/nodes/DefaultPose.qml</file>
|
||||
<file>qml/nodes/InverseKinematics.qml</file>
|
||||
<file>qml/nodes/Manipulator.qml</file>
|
||||
<file>qml/nodes/Overlay.qml</file>
|
||||
<file>qml/nodes/PoleVector.qml</file>
|
||||
<file>qml/nodes/RandomStateMachine.qml</file>
|
||||
<file>qml/nodes/StateMachine.qml</file>
|
||||
<file>qml/nodes/SplineIK.qml</file>
|
||||
<file>qml/nodes/TwoBoneIK.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
10
tools/animedit/qml/TreeDelegate.qml
Normal file
10
tools/animedit/qml/TreeDelegate.qml
Normal file
|
@ -0,0 +1,10 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
color: styleData.textColor
|
||||
elide: styleData.elideMode
|
||||
text: styleData.value
|
||||
}
|
||||
}
|
44
tools/animedit/qml/fields/BooleanField.qml
Normal file
44
tools/animedit/qml/fields/BooleanField.qml
Normal file
|
@ -0,0 +1,44 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
|
||||
Row {
|
||||
id: row
|
||||
height: 20
|
||||
width: 300
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string key
|
||||
property bool value
|
||||
property bool optional
|
||||
|
||||
function setValue(newValue) {
|
||||
parent.fieldChanged(key, newValue);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: keyText
|
||||
y: 5
|
||||
width: 100
|
||||
text: key + ":"
|
||||
font.pixelSize: 12
|
||||
color: optional ? "blue" : "black"
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: valueCheckBox
|
||||
x: 100
|
||||
y: 5
|
||||
width: 200
|
||||
checked: value ? Qt.Checked : Qt.Unchecked
|
||||
onCheckedChanged: {
|
||||
setValue(checked);
|
||||
}
|
||||
}
|
||||
}
|
41
tools/animedit/qml/fields/IdField.qml
Normal file
41
tools/animedit/qml/fields/IdField.qml
Normal file
|
@ -0,0 +1,41 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Row {
|
||||
id: row
|
||||
x: 0
|
||||
y: 0
|
||||
height: 20
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string theValue
|
||||
|
||||
function setValue(newValue) {
|
||||
var ROLE_NAME = 0x0101;
|
||||
theModel.setData(leftHandPane.currentIndex, newValue, ROLE_NAME);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: element
|
||||
y: 5
|
||||
width: 100
|
||||
text: qsTr("Id:")
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: textField
|
||||
x: 100
|
||||
width: 200
|
||||
text: theValue
|
||||
onEditingFinished: {
|
||||
setValue(text);
|
||||
}
|
||||
}
|
||||
}
|
52
tools/animedit/qml/fields/JSONField.qml
Normal file
52
tools/animedit/qml/fields/JSONField.qml
Normal file
|
@ -0,0 +1,52 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
|
||||
Column {
|
||||
id: row
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string key
|
||||
property var value
|
||||
property bool optional
|
||||
|
||||
spacing: 5
|
||||
|
||||
function setValue(newValue) {
|
||||
parent.fieldChanged(key, newValue);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: keyText
|
||||
y: 5
|
||||
width: row.width
|
||||
text: key + ":"
|
||||
font.pixelSize: 12
|
||||
color: optional ? "blue" : "black"
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: valueTextField
|
||||
x: 0
|
||||
width: (keyText.width - 20)
|
||||
height: (parent.height - 120)
|
||||
wrapMode: TextEdit.NoWrap
|
||||
|
||||
// TODO: validate
|
||||
|
||||
text: JSON.stringify(value, null, 4)
|
||||
|
||||
onEditingFinished: {
|
||||
value = JSON.parse(text);
|
||||
setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
54
tools/animedit/qml/fields/NumberArrayField.qml
Normal file
54
tools/animedit/qml/fields/NumberArrayField.qml
Normal file
|
@ -0,0 +1,54 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
|
||||
Row {
|
||||
id: row
|
||||
height: 20
|
||||
width: 300
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string key
|
||||
property var value
|
||||
property bool optional
|
||||
|
||||
function setValue(newValue) {
|
||||
parent.fieldChanged(key, newValue);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: keyText
|
||||
y: 5
|
||||
width: 100
|
||||
text: key + ":"
|
||||
font.pixelSize: 12
|
||||
color: optional ? "blue" : "black"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: valueTextField
|
||||
x: 100
|
||||
width: 200
|
||||
|
||||
// first start with a regex for an array of numbers
|
||||
// ^\[\s*(\d+)(\s*,\s*(\d+))*\]$|\[\s*\]
|
||||
// then a regex for a floating point number
|
||||
// \d+\.?\d*
|
||||
// then substitue the second into the \d+ of the first, yeilding this monstrocity.
|
||||
// ^\[\s*(\d+\.?\d*)(\s*,\s*(\d+\.?\d*))*\]$|\[\s*\]
|
||||
|
||||
//validator: RegExpValidator { regExp: /^\[\s*(\d+\.?\d*)(\s*,\s*(\d+\.?\d*))*\]$|\[\s*\]/ }
|
||||
|
||||
text: JSON.stringify(value)
|
||||
onEditingFinished: {
|
||||
value = JSON.parse(text);
|
||||
setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
45
tools/animedit/qml/fields/NumberField.qml
Normal file
45
tools/animedit/qml/fields/NumberField.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
|
||||
Row {
|
||||
id: row
|
||||
height: 20
|
||||
width: 300
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string key
|
||||
property real value
|
||||
property bool optional
|
||||
|
||||
function setValue(newValue) {
|
||||
parent.fieldChanged(key, newValue);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: keyText
|
||||
y: 5
|
||||
width: 100
|
||||
text: key + ":"
|
||||
font.pixelSize: 12
|
||||
color: optional ? "blue" : "black"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: valueTextField
|
||||
x: 100
|
||||
width: 200
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
text: value
|
||||
onEditingFinished: {
|
||||
value = text;
|
||||
setValue(parseInt(text, 10));
|
||||
}
|
||||
}
|
||||
}
|
44
tools/animedit/qml/fields/StringField.qml
Normal file
44
tools/animedit/qml/fields/StringField.qml
Normal file
|
@ -0,0 +1,44 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
|
||||
Row {
|
||||
id: row
|
||||
height: 20
|
||||
width: 300
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string key
|
||||
property string value
|
||||
property bool optional
|
||||
|
||||
function setValue(newValue) {
|
||||
parent.fieldChanged(key, newValue);
|
||||
}
|
||||
|
||||
Text {
|
||||
id: keyText
|
||||
y: 5
|
||||
width: 100
|
||||
text: key + ":"
|
||||
font.pixelSize: 12
|
||||
color: optional ? "blue" : "black"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: valueTextField
|
||||
x: 100
|
||||
width: 200
|
||||
text: value
|
||||
onEditingFinished: {
|
||||
value = text;
|
||||
setValue(text);
|
||||
}
|
||||
}
|
||||
}
|
67
tools/animedit/qml/fields/TypeField.qml
Normal file
67
tools/animedit/qml/fields/TypeField.qml
Normal file
|
@ -0,0 +1,67 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
|
||||
Row {
|
||||
id: row
|
||||
|
||||
function getTypes() {
|
||||
return [
|
||||
"clip",
|
||||
"blendDirectional",
|
||||
"blendLinear",
|
||||
"overlay",
|
||||
"stateMachine",
|
||||
"randomSwitchStateMachine",
|
||||
"manipulator",
|
||||
"inverseKinematics",
|
||||
"defaultPose",
|
||||
"twoBoneIK",
|
||||
"splineIK",
|
||||
"poleVectorConstraint"
|
||||
];
|
||||
}
|
||||
|
||||
function indexFromString(str) {
|
||||
var index = getTypes().indexOf(str);
|
||||
return (index === -1) ? 0 : index;
|
||||
}
|
||||
|
||||
x: 0
|
||||
y: 0
|
||||
height: 20
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
|
||||
property string theValue: "clip"
|
||||
property int theIndex: indexFromString(theValue)
|
||||
|
||||
function setValue(newValue) {
|
||||
var ROLE_TYPE = 0x0102;
|
||||
theModel.setData(leftHandPane.currentIndex, newValue, ROLE_TYPE);
|
||||
rightHandPane.reset();
|
||||
}
|
||||
|
||||
Text {
|
||||
id: element
|
||||
y: 5
|
||||
width: 100
|
||||
text: qsTr("Type:")
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: comboBox
|
||||
x: 100
|
||||
width: 200
|
||||
model: getTypes()
|
||||
currentIndex: theIndex
|
||||
onActivated: {
|
||||
setValue(currentText);
|
||||
}
|
||||
}
|
||||
}
|
262
tools/animedit/qml/main.qml
Normal file
262
tools/animedit/qml/main.qml
Normal file
|
@ -0,0 +1,262 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "fields"
|
||||
import "nodes"
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
visible: true
|
||||
width: 1600
|
||||
height: 1000
|
||||
color: "#ffffff"
|
||||
opacity: 1
|
||||
title: qsTr("AnimEdit")
|
||||
menuBar: appMenuBar
|
||||
|
||||
SplitView {
|
||||
id: splitView
|
||||
anchors.fill: parent
|
||||
|
||||
TreeView {
|
||||
id: leftHandPane
|
||||
width: 1000
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
model: theModel
|
||||
itemDelegate: TreeDelegate {}
|
||||
|
||||
TableViewColumn {
|
||||
role: "name"
|
||||
title: "Name"
|
||||
width: 500
|
||||
}
|
||||
|
||||
TableViewColumn {
|
||||
role: "type"
|
||||
title: "Type"
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
rightHandPane.setIndex(index);
|
||||
}
|
||||
|
||||
function expandTreeView() {
|
||||
function expandAll(index) {
|
||||
leftHandPane.expand(index);
|
||||
var children = theModel.getChildrenModelIndices(index);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
leftHandPane.expand(children[i]);
|
||||
expandAll(children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var index = theModel.index(0, 0);
|
||||
expandAll(index);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rightHandPane
|
||||
color: "#adadad"
|
||||
height: parent.height
|
||||
width: 500
|
||||
anchors.left: leftHandPane.right
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
|
||||
function createCustomData(qml, index) {
|
||||
var component = Qt.createComponent(qml, Component.PreferSynchronous);
|
||||
if (component.status === Component.Ready) {
|
||||
var obj = component.createObject(rightHandPaneColumn);
|
||||
obj.setIndex(index);
|
||||
} else if (component.status === Component.Error) {
|
||||
console.log("ERROR: " + component.errorString());
|
||||
} else if (component.status === Component.Loading) {
|
||||
console.log("ERROR: NOT READY");
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setIndex(leftHandPane.currentIndex);
|
||||
}
|
||||
|
||||
function setIndex(index) {
|
||||
var ROLE_NAME = 0x0101;
|
||||
var ROLE_TYPE = 0x0102;
|
||||
var ROLE_DATA = 0x0103;
|
||||
|
||||
var idValue = theModel.data(index, ROLE_NAME);
|
||||
var typeValue = theModel.data(index, ROLE_TYPE);
|
||||
|
||||
idField.theValue = idValue;
|
||||
typeField.theValue = typeValue;
|
||||
|
||||
// delete previous custom data obj, if present
|
||||
var orig = rightHandPaneColumn.children[2];
|
||||
if (orig) {
|
||||
orig.destroy();
|
||||
}
|
||||
|
||||
if (typeValue === "clip") {
|
||||
createCustomData("nodes/ClipData.qml", index);
|
||||
} else if (typeValue === "blendDirectional") {
|
||||
createCustomData("nodes/BlendDirectional.qml", index);
|
||||
} else if (typeValue === "blendLinear") {
|
||||
createCustomData("nodes/BlendLinear.qml", index);
|
||||
} else if (typeValue === "blendLinearMove") {
|
||||
createCustomData("nodes/BlendLinearMove.qml", index);
|
||||
} else if (typeValue === "overlay") {
|
||||
createCustomData("nodes/Overlay.qml", index);
|
||||
} else if (typeValue === "stateMachine") {
|
||||
createCustomData("nodes/StateMachine.qml", index);
|
||||
} else if (typeValue === "randomSwitchStateMachine") {
|
||||
createCustomData("nodes/RandomStateMachine.qml", index);
|
||||
} else if (typeValue === "inverseKinematics") {
|
||||
createCustomData("nodes/InverseKinematics.qml", index);
|
||||
} else if (typeValue === "twoBoneIK") {
|
||||
createCustomData("nodes/TwoBoneIK.qml", index);
|
||||
} else if (typeValue === "defaultPose") {
|
||||
createCustomData("nodes/DefaultPose.qml", index);
|
||||
} else if (typeValue === "manipulator") {
|
||||
createCustomData("nodes/Manipulator.qml", index);
|
||||
} else if (typeValue === "splineIK") {
|
||||
createCustomData("nodes/SplineIK.qml", index);
|
||||
} else if (typeValue === "poleVectorConstraint") {
|
||||
createCustomData("nodes/PoleVector.qml", index);
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: rightHandPaneColumn
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
|
||||
spacing: 6
|
||||
|
||||
IdField {
|
||||
id: idField
|
||||
}
|
||||
|
||||
TypeField {
|
||||
id: typeField
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuBar {
|
||||
id: appMenuBar
|
||||
Menu {
|
||||
title: "File"
|
||||
MenuItem {
|
||||
text: "Open..."
|
||||
onTriggered: openFileDialog.open()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Save As..."
|
||||
onTriggered: saveAsFileDialog.open()
|
||||
}
|
||||
}
|
||||
Menu {
|
||||
title: "Edit"
|
||||
MenuItem {
|
||||
text: "Add New Node as Child"
|
||||
onTriggered: {
|
||||
theModel.newNode(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Delete Selected"
|
||||
onTriggered: {
|
||||
theModel.deleteNode(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Insert New Node Above"
|
||||
onTriggered: {
|
||||
theModel.insertNodeAbove(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Copy Node Only"
|
||||
onTriggered: {
|
||||
theModel.copyNode(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Copy Node And Children"
|
||||
onTriggered: {
|
||||
theModel.copyNodeAndChildren(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Paste Over"
|
||||
onTriggered: {
|
||||
theModel.pasteOver(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Paste As Child"
|
||||
onTriggered: {
|
||||
theModel.pasteAsChild(leftHandPane.currentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: openFileDialog
|
||||
title: "Open an animation json file"
|
||||
folder: shortcuts.home
|
||||
nameFilters: ["Json files (*.json)"]
|
||||
onAccepted: {
|
||||
var path = openFileDialog.fileUrl.toString();
|
||||
// remove prefixed "file:///"
|
||||
path = path.replace(/^(file:\/{3})/,"");
|
||||
// unescape html codes like '%23' for '#'
|
||||
var cleanPath = decodeURIComponent(path);
|
||||
console.log("You chose: " + cleanPath);
|
||||
theModel.loadFromFile(cleanPath);
|
||||
leftHandPane.expandTreeView();
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled");
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: saveAsFileDialog
|
||||
title: "Save an animation json file"
|
||||
folder: shortcuts.home
|
||||
nameFilters: ["Json files (*.json)"]
|
||||
selectExisting: false
|
||||
onAccepted: {
|
||||
var path = saveAsFileDialog.fileUrl.toString();
|
||||
// remove prefixed "file:///"
|
||||
path = path.replace(/^(file:\/{3})/,"");
|
||||
// unescape html codes like '%23' for '#'
|
||||
var cleanPath = decodeURIComponent(path);
|
||||
console.log("You chose: " + cleanPath);
|
||||
theModel.saveToFile(cleanPath);
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled");
|
||||
}
|
||||
}
|
||||
}
|
129
tools/animedit/qml/nodes/BlendDirectional.qml
Normal file
129
tools/animedit/qml/nodes/BlendDirectional.qml
Normal file
|
@ -0,0 +1,129 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["alpha", "alphaVar", "centerId", "upId", "downId", "leftId", "rightId", "upLeftId", "upRightId", "downLeftId", "downRightId"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberArrayField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: [0, 0, 0]
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: centerIdField
|
||||
key: "centerId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: upIdField
|
||||
key: "upId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: downIdField
|
||||
key: "downId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: leftIdField
|
||||
key: "leftId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: rightIdField
|
||||
key: "rightId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: upLeftIdField
|
||||
key: "upLeftId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: upRightIdField
|
||||
key: "upRightId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: downLeftIdField
|
||||
key: "downLeftId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: downRightIdField
|
||||
key: "downRightId"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
|
73
tools/animedit/qml/nodes/BlendLinear.qml
Normal file
73
tools/animedit/qml/nodes/BlendLinear.qml
Normal file
|
@ -0,0 +1,73 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["alpha", "blendType", "alphaVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: blendTypeField
|
||||
key: "blendType"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
|
85
tools/animedit/qml/nodes/BlendLinearMove.qml
Normal file
85
tools/animedit/qml/nodes/BlendLinearMove.qml
Normal file
|
@ -0,0 +1,85 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["alpha", "desiredSpeed", "characteristicSpeeds", "alphaVar", "desiredSpeedVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: desiredSpeedField
|
||||
key: "desiredSpeed"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
NumberArrayField {
|
||||
id: characteristicSpeedsField
|
||||
key: "characteristicSpeeds"
|
||||
value: []
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: desiredSpeedVarField
|
||||
key: "desiredSpeedVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
|
148
tools/animedit/qml/nodes/ClipData.qml
Normal file
148
tools/animedit/qml/nodes/ClipData.qml
Normal file
|
@ -0,0 +1,148 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["url", "startFrame", "endFrame", "timeScale", "loopFlag", "mirrorFlag",
|
||||
"blendType", "baseURL", "baseFrame", "startFrameVar", "endFrameVar",
|
||||
"timeScaleVar", "loopFlagVar", "mirrorFlagVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: urlField
|
||||
key: "url"
|
||||
value: "qrc:///avatar/animations/idle.fbx"
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: startFrameField
|
||||
key: "startFrame"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: endFrameField
|
||||
key: "endFrame"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: timeScaleField
|
||||
key: "timeScale"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
BooleanField {
|
||||
id: loopFlagField
|
||||
key: "loopFlag"
|
||||
value: false
|
||||
}
|
||||
|
||||
BooleanField {
|
||||
id: mirrorFlagField
|
||||
key: "mirrorFlag"
|
||||
value: false
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: blendTypeField
|
||||
key: "blendType"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: baseURLField
|
||||
key: "baseURL"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: baseFrameField
|
||||
key: "baseFrame"
|
||||
value: 0.0
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: startFrameVarField
|
||||
key: "startFrameVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: endFrameVarField
|
||||
key: "endFrameVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: timeScaleVarField
|
||||
key: "timeScaleVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: loopFlagVarField
|
||||
key: "loopFlagVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: mirrorFlagVarField
|
||||
key: "mirrorFlagVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
|
53
tools/animedit/qml/nodes/DefaultPose.qml
Normal file
53
tools/animedit/qml/nodes/DefaultPose.qml
Normal file
|
@ -0,0 +1,53 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = []; // This node has no data values
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
}
|
||||
|
71
tools/animedit/qml/nodes/InverseKinematics.qml
Normal file
71
tools/animedit/qml/nodes/InverseKinematics.qml
Normal file
|
@ -0,0 +1,71 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
width: parent.width
|
||||
height: (parent.height - 50)
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["solutionSource", "solutionSourceVar", "targets"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: solutionSourceField
|
||||
key: "solutionSource"
|
||||
value: "relaxToUnderPoses"
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: solutionSourceVarField
|
||||
key: "solutionSourceVar"
|
||||
value: "solutionSource"
|
||||
}
|
||||
|
||||
JSONField {
|
||||
id: statesField
|
||||
key: "targets"
|
||||
value: {}
|
||||
}
|
||||
}
|
||||
|
72
tools/animedit/qml/nodes/Manipulator.qml
Normal file
72
tools/animedit/qml/nodes/Manipulator.qml
Normal file
|
@ -0,0 +1,72 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["alpha", "joints", "alphaVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: 1.0
|
||||
}
|
||||
|
||||
JSONField {
|
||||
id: statesField
|
||||
key: "joints"
|
||||
value: {}
|
||||
}
|
||||
|
||||
StringField {
|
||||
optional: true
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
|
80
tools/animedit/qml/nodes/Overlay.qml
Normal file
80
tools/animedit/qml/nodes/Overlay.qml
Normal file
|
@ -0,0 +1,80 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["boneSet", "alpha", "boneSetVar", "alphaVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: boneSetField
|
||||
key: "boneSet"
|
||||
value: "fullBody"
|
||||
optional: true
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: 0.0
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: boneSetVarField
|
||||
key: "boneSetVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
|
96
tools/animedit/qml/nodes/PoleVector.qml
Normal file
96
tools/animedit/qml/nodes/PoleVector.qml
Normal file
|
@ -0,0 +1,96 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["referenceVector", "enabled", "baseJointName", "midJointName", "tipJointName",
|
||||
"enabledVar", "poleVectorVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberArrayField {
|
||||
id: referenceVectorField
|
||||
key: "referenceVector"
|
||||
value: [1, 0, 0]
|
||||
}
|
||||
|
||||
BooleanField {
|
||||
id: enabledField
|
||||
key: "enabled"
|
||||
value: false
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: baseJointNameField
|
||||
key: "baseJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: midJointNameField
|
||||
key: "midJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: tipJointNameField
|
||||
key: "tipJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: enabledVarField
|
||||
key: "enabledVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: poleVectorVarField
|
||||
key: "poleVectorVar"
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
|
109
tools/animedit/qml/nodes/RandomStateMachine.qml
Normal file
109
tools/animedit/qml/nodes/RandomStateMachine.qml
Normal file
|
@ -0,0 +1,109 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
width: parent.width
|
||||
height: (parent.height - 150)
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["currentState", "randomSwitchTimeMin", "randomSwitchTimeMax", "triggerRandomSwitch",
|
||||
"triggerTimeMin", "triggerTimeMax", "transitionVar", "states"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: currentStateField
|
||||
key: "currentState"
|
||||
value: ""
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: randomSwitchTimeMinField
|
||||
key: "randomSwitchTimeMin"
|
||||
value: 1.0
|
||||
optional: true
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: randomSwitchTimeMaxField
|
||||
key: "randomSwitchTimeMax"
|
||||
value: 10.0
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: triggerRandomSwitchField
|
||||
key: "triggerRandomSwitch"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: triggerTimeMinField
|
||||
key: "triggerTimeMin"
|
||||
value: 1.0
|
||||
optional: true
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: triggerTimeMaxField
|
||||
key: "triggerTimeMax"
|
||||
value: 10.0
|
||||
optional: true
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: transitionVarField
|
||||
key: "transitionVar"
|
||||
value: ""
|
||||
optional: true
|
||||
}
|
||||
|
||||
JSONField {
|
||||
id: statesField
|
||||
key: "states"
|
||||
value: {}
|
||||
}
|
||||
}
|
||||
|
139
tools/animedit/qml/nodes/SplineIK.qml
Normal file
139
tools/animedit/qml/nodes/SplineIK.qml
Normal file
|
@ -0,0 +1,139 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["alpha", "enabled", "interpDuration", "baseJointName", "midJointName", "tipJointName",
|
||||
"basePositionVar", "baseRotationVar", "midPositionVar", "midRotationVar", "tipPositionVar", "tipRotationVar",
|
||||
"alphaVar", "enabledVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: 1.0
|
||||
}
|
||||
|
||||
BooleanField {
|
||||
id: enabledField
|
||||
key: "enabled"
|
||||
value: false
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: interpDurationField
|
||||
key: "interpDuration"
|
||||
value: 15.0
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: baseJointNameField
|
||||
key: "baseJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: midJointNameField
|
||||
key: "midJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: tipJointNameField
|
||||
key: "tipJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: basePositionVarField
|
||||
key: "basePositionVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: baseRotationVarField
|
||||
key: "baseRotationVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: midPositionVarField
|
||||
key: "midPositionVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: midRotationVarField
|
||||
key: "midRotationVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: tipPositionVarField
|
||||
key: "tipPositionVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: tipRotationVarField
|
||||
key: "tipRotationVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: enabledVarField
|
||||
key: "enabledVar"
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
|
65
tools/animedit/qml/nodes/StateMachine.qml
Normal file
65
tools/animedit/qml/nodes/StateMachine.qml
Normal file
|
@ -0,0 +1,65 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["currentState", "states"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: currentStateField
|
||||
key: "currentState"
|
||||
value: "default"
|
||||
}
|
||||
|
||||
JSONField {
|
||||
id: statesField
|
||||
key: "states"
|
||||
value: {}
|
||||
}
|
||||
}
|
||||
|
120
tools/animedit/qml/nodes/TwoBoneIK.qml
Normal file
120
tools/animedit/qml/nodes/TwoBoneIK.qml
Normal file
|
@ -0,0 +1,120 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 1.6
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Dialogs 1.0
|
||||
import "../fields"
|
||||
|
||||
Column {
|
||||
id: column
|
||||
x: 0
|
||||
y: 0
|
||||
height: 50
|
||||
width: 200
|
||||
spacing: 6
|
||||
property var modelIndex
|
||||
|
||||
signal fieldChanged(string key, var newValue)
|
||||
|
||||
// called by each field when its value is changed.
|
||||
function fieldChangedMethod(key, newValue) {
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
dataValue[key] = newValue;
|
||||
|
||||
// copy the new value into the model.
|
||||
theModel.setData(modelIndex, dataValue, ROLE_DATA);
|
||||
}
|
||||
|
||||
// called when a new model is loaded
|
||||
function setIndex(index) {
|
||||
modelIndex = index;
|
||||
|
||||
var ROLE_DATA = 0x0103;
|
||||
var dataValue = theModel.data(modelIndex, ROLE_DATA);
|
||||
|
||||
var fields = ["alpha", "enabled", "interpDuration", "baseJointName", "midJointName", "tipJointName", "midHingeAxis",
|
||||
"alphaVar", "enabledVar", "endEffectorRotationVarVar", "endEffectorPositionVarVar"];
|
||||
|
||||
// copy data from theModel into each field.
|
||||
var l = fields.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
var val = dataValue[fields[i]];
|
||||
if (val) {
|
||||
column.children[i].value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// this signal is fired from each data field when values are changed.
|
||||
column.fieldChanged.connect(fieldChangedMethod)
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: alphaField
|
||||
key: "alpha"
|
||||
value: 1.0
|
||||
}
|
||||
|
||||
BooleanField {
|
||||
id: enabledField
|
||||
key: "enabled"
|
||||
value: false
|
||||
}
|
||||
|
||||
NumberField {
|
||||
id: interpDurationField
|
||||
key: "interpDuration"
|
||||
value: 15.0
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: baseJointNameField
|
||||
key: "baseJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: midJointNameField
|
||||
key: "midJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: tipJointNameField
|
||||
key: "tipJointName"
|
||||
value: ""
|
||||
}
|
||||
|
||||
NumberArrayField {
|
||||
id: midHingeAxisField
|
||||
key: "midHingeAxis"
|
||||
value: [1, 0, 0]
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: alphaVarField
|
||||
key: "alphaVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: enabledVarField
|
||||
key: "enabledVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: endEffectorRotationVarVarField
|
||||
key: "endEffectorRotationVarVar"
|
||||
value: ""
|
||||
}
|
||||
|
||||
StringField {
|
||||
id: endEffectorPositionVarVarField
|
||||
key: "endEffectorPositionVarVar"
|
||||
value: ""
|
||||
}
|
||||
}
|
||||
|
91
tools/animedit/treeitem.cpp
Normal file
91
tools/animedit/treeitem.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// TreeItem
|
||||
//
|
||||
// Created by Anthony Thibault on 6/5/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "treeitem.h"
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
|
||||
TreeItem::TreeItem(const QList<QVariant>& data) {
|
||||
m_parentItem = nullptr;
|
||||
m_itemData = data;
|
||||
}
|
||||
|
||||
TreeItem::~TreeItem() {
|
||||
qDeleteAll(m_childItems);
|
||||
}
|
||||
|
||||
void TreeItem::appendChild(TreeItem *item) {
|
||||
item->m_parentItem = this;
|
||||
m_childItems.append(item);
|
||||
}
|
||||
|
||||
int TreeItem::findChild(TreeItem* child) {
|
||||
for (int i = 0; i < m_childItems.size(); i++) {
|
||||
if (m_childItems[i] == child) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TreeItem::removeChild(int index) {
|
||||
// TODO: delete TreeItem
|
||||
m_childItems.removeAt(index);
|
||||
}
|
||||
|
||||
void TreeItem::insertChild(int index, TreeItem* child) {
|
||||
child->m_parentItem = this;
|
||||
m_childItems.insert(index, child);
|
||||
}
|
||||
|
||||
TreeItem* TreeItem::child(int row) {
|
||||
return m_childItems.value(row);
|
||||
}
|
||||
|
||||
int TreeItem::childCount() const {
|
||||
return m_childItems.count();
|
||||
}
|
||||
|
||||
int TreeItem::columnCount() const {
|
||||
return m_itemData.count();
|
||||
}
|
||||
|
||||
QVariant TreeItem::data(int column) const {
|
||||
return m_itemData.value(column);
|
||||
}
|
||||
|
||||
bool TreeItem::setData(int column, const QVariant& value) {
|
||||
m_itemData[column] = QVariant(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
TreeItem* TreeItem::parentItem() {
|
||||
return m_parentItem;
|
||||
}
|
||||
|
||||
int TreeItem::row() const {
|
||||
if (m_parentItem) {
|
||||
return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TreeItem* TreeItem::cloneNode() const {
|
||||
return new TreeItem(m_itemData);
|
||||
}
|
||||
|
||||
TreeItem* TreeItem::cloneNodeAndChildren() const {
|
||||
TreeItem* newNode = new TreeItem(m_itemData);
|
||||
for (int i = 0; i < m_childItems.size(); ++i) {
|
||||
newNode->appendChild(m_childItems[i]->cloneNodeAndChildren());
|
||||
}
|
||||
return newNode;
|
||||
}
|
45
tools/animedit/treeitem.h
Normal file
45
tools/animedit/treeitem.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// TreeItem
|
||||
//
|
||||
// Created by Anthony Thibault on 6/5/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_TreeItem_h
|
||||
#define hifi_TreeItem_h
|
||||
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
|
||||
class TreeItem
|
||||
{
|
||||
public:
|
||||
explicit TreeItem(const QList<QVariant>& data);
|
||||
~TreeItem();
|
||||
|
||||
void appendChild(TreeItem* child);
|
||||
int findChild(TreeItem* child);
|
||||
void removeChild(int index);
|
||||
void insertChild(int index, TreeItem* child);
|
||||
|
||||
TreeItem* child(int row);
|
||||
int childCount() const;
|
||||
int columnCount() const;
|
||||
QVariant data(int column) const;
|
||||
bool setData(int column, const QVariant& value);
|
||||
int row() const;
|
||||
TreeItem* parentItem();
|
||||
|
||||
TreeItem* cloneNode() const;
|
||||
TreeItem* cloneNodeAndChildren() const;
|
||||
|
||||
private:
|
||||
QList<TreeItem*> m_childItems;
|
||||
QList<QVariant> m_itemData;
|
||||
TreeItem* m_parentItem;
|
||||
};
|
||||
|
||||
#endif
|
418
tools/animedit/treemodel.cpp
Normal file
418
tools/animedit/treemodel.cpp
Normal file
|
@ -0,0 +1,418 @@
|
|||
//
|
||||
// TreeModel
|
||||
//
|
||||
// Created by Anthony Thibault on 6/5/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "treeitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QtGlobal>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QStringList>
|
||||
#include <QJSValue>
|
||||
|
||||
#include "treemodel.h"
|
||||
|
||||
static TreeItem* newBlankTreeItem() {
|
||||
QList<QVariant> columnData;
|
||||
columnData << "newNode";
|
||||
columnData << "clip";
|
||||
columnData << QJsonObject(); // blank
|
||||
return new TreeItem(columnData);
|
||||
}
|
||||
|
||||
TreeModel::TreeModel(QObject* parent) : QAbstractItemModel(parent) {
|
||||
_roleNameMapping[TreeModelRoleName] = "name";
|
||||
_roleNameMapping[TreeModelRoleType] = "type";
|
||||
_roleNameMapping[TreeModelRoleData] = "data";
|
||||
|
||||
QList<QVariant> rootData;
|
||||
rootData << "Name" << "Type" << "Data";
|
||||
_rootItem = new TreeItem(rootData);
|
||||
_clipboard = nullptr;
|
||||
}
|
||||
|
||||
TreeModel::~TreeModel() {
|
||||
delete _rootItem;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> TreeModel::roleNames() const {
|
||||
return _roleNameMapping;
|
||||
}
|
||||
|
||||
Qt::ItemFlags TreeModel::flags(const QModelIndex& index) const {
|
||||
if (!index.isValid()) {
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
|
||||
return QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
QVariant TreeModel::data(const QModelIndex& index, int role) const {
|
||||
TreeItem* item = getItem(index);
|
||||
return item->data(role - Qt::UserRole - 1);
|
||||
}
|
||||
|
||||
QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
return _rootItem->data(section);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::index(int row, int column, const QModelIndex& parent) const {
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
TreeItem *parentItem;
|
||||
|
||||
if (!parent.isValid()) {
|
||||
parentItem = _rootItem;
|
||||
} else {
|
||||
parentItem = static_cast<TreeItem*>(parent.internalPointer());
|
||||
}
|
||||
|
||||
TreeItem *childItem = parentItem->child(row);
|
||||
if (childItem) {
|
||||
return createIndex(row, column, childItem);
|
||||
} else {
|
||||
return QModelIndex();
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::parent(const QModelIndex& index) const {
|
||||
if (!index.isValid()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
|
||||
TreeItem *parentItem = childItem->parentItem();
|
||||
|
||||
if (parentItem == _rootItem) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
int TreeModel::rowCount(const QModelIndex& parent) const {
|
||||
TreeItem* parentItem = getItem(parent);
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
int TreeModel::columnCount(const QModelIndex& parent) const {
|
||||
return _rootItem->columnCount();
|
||||
}
|
||||
|
||||
bool TreeModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||
TreeItem* item = getItem(index);
|
||||
|
||||
bool returnValue = item->setData(role - Qt::UserRole - 1, value);
|
||||
|
||||
emit dataChanged(index, index);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
bool TreeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::insertColumns(int position, int columns, const QModelIndex& parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::removeColumns(int position, int columns, const QModelIndex& parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::insertRows(int position, int rows, const QModelIndex& parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::removeRows(int position, int rows, const QModelIndex& parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TreeModel::loadFromFile(const QString& filename) {
|
||||
|
||||
beginResetModel();
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.exists()) {
|
||||
qCritical() << "TreeModel::loadFromFile, failed to open file" << filename;
|
||||
} else if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "TreeModel::loadFromFile, failed to open file" << filename;
|
||||
} else {
|
||||
qDebug() << "TreeModel::loadFromFile, success opening file" << filename;
|
||||
QByteArray contents = file.readAll();
|
||||
QJsonParseError error;
|
||||
auto doc = QJsonDocument::fromJson(contents, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCritical() << "TreeModel::loadFromFile, failed to parse json, error" << error.errorString();
|
||||
} else {
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
// version
|
||||
QJsonValue versionVal = obj.value("version");
|
||||
if (!versionVal.isString()) {
|
||||
qCritical() << "TreeModel::loadFromFile, bad string \"version\"";
|
||||
return;
|
||||
}
|
||||
QString version = versionVal.toString();
|
||||
|
||||
// check version
|
||||
if (version != "1.0" && version != "1.1") {
|
||||
qCritical() << "TreeModel::loadFromFile, bad version number" << version << "expected \"1.0\" or \"1.1\"";
|
||||
return;
|
||||
}
|
||||
|
||||
// root
|
||||
QJsonValue rootVal = obj.value("root");
|
||||
if (!rootVal.isObject()) {
|
||||
qCritical() << "TreeModel::loadFromFile, bad object \"root\"";
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QVariant> columnData;
|
||||
columnData << QString("root");
|
||||
columnData << QString("root");
|
||||
columnData << QString("root");
|
||||
|
||||
// create root item
|
||||
_rootItem = new TreeItem(columnData);
|
||||
_rootItem->appendChild(loadNode(rootVal.toObject()));
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TreeModel::saveToFile(const QString& filename) {
|
||||
|
||||
QJsonObject obj;
|
||||
obj.insert("version", "1.1");
|
||||
|
||||
const int FIRST_CHILD = 0;
|
||||
obj.insert("root", jsonFromItem(_rootItem->child(FIRST_CHILD)));
|
||||
|
||||
QJsonDocument doc(obj);
|
||||
QByteArray byteArray = doc.toJson(QJsonDocument::Indented);
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qCritical() << "TreeModel::safeToFile, failed to open file" << filename;
|
||||
} else {
|
||||
file.write(byteArray);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeModel::newNode(const QModelIndex& parent) {
|
||||
TreeItem* parentItem = _rootItem;
|
||||
if (parent.isValid()) {
|
||||
parentItem = static_cast<TreeItem*>(parent.internalPointer());
|
||||
}
|
||||
|
||||
beginInsertRows(parent, parentItem->childCount(), parentItem->childCount());
|
||||
TreeItem* childItem = newBlankTreeItem();
|
||||
parentItem->appendChild(childItem);
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void TreeModel::deleteNode(const QModelIndex& index) {
|
||||
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
|
||||
TreeItem* parentItem = item->parentItem();
|
||||
int childNum = parentItem->findChild(item);
|
||||
|
||||
if (childNum >= 0) {
|
||||
beginRemoveRows(createIndex(0, 0, reinterpret_cast<quintptr>(parentItem)), childNum, childNum);
|
||||
parentItem->removeChild(childNum);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
void TreeModel::insertNodeAbove(const QModelIndex& index) {
|
||||
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
|
||||
TreeItem* parentItem = item->parentItem();
|
||||
int childNum = parentItem->findChild(item);
|
||||
|
||||
if (childNum >= 0) {
|
||||
TreeItem* newItem = newBlankTreeItem();
|
||||
QModelIndex parentIndex = createIndex(0, 0, reinterpret_cast<quintptr>(parentItem));
|
||||
|
||||
// remove item
|
||||
beginRemoveRows(parentIndex, childNum, childNum);
|
||||
parentItem->removeChild(childNum);
|
||||
endRemoveRows();
|
||||
|
||||
// append item to newItem
|
||||
newItem->appendChild(item);
|
||||
|
||||
// then insert newItem
|
||||
beginInsertRows(parentIndex, childNum, childNum);
|
||||
parentItem->insertChild(childNum, newItem);
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList TreeModel::getChildrenModelIndices(const QModelIndex& index) {
|
||||
QVariantList indices;
|
||||
|
||||
TreeItem* parent = static_cast<TreeItem*>(index.internalPointer());
|
||||
for (int i = 0; i < parent->childCount(); ++i) {
|
||||
TreeItem* child = parent->child(i);
|
||||
indices.push_back(createIndex(i, 0, reinterpret_cast<quintptr>(child)));
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
void TreeModel::copyNode(const QModelIndex& index) {
|
||||
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
|
||||
// TODO: delete previous clipboard
|
||||
_clipboard = item->cloneNode();
|
||||
}
|
||||
|
||||
void TreeModel::copyNodeAndChildren(const QModelIndex& index) {
|
||||
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
|
||||
// TODO: delete previous clipboard
|
||||
_clipboard = item->cloneNodeAndChildren();
|
||||
}
|
||||
|
||||
void TreeModel::pasteOver(const QModelIndex& index) {
|
||||
if (_clipboard) {
|
||||
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
|
||||
TreeItem* parentItem = item->parentItem();
|
||||
int childNum = parentItem->findChild(item);
|
||||
|
||||
if (childNum >= 0) {
|
||||
QModelIndex parentIndex = createIndex(0, 0, reinterpret_cast<quintptr>(parentItem));
|
||||
|
||||
// remove item
|
||||
beginRemoveRows(parentIndex, childNum, childNum);
|
||||
parentItem->removeChild(childNum);
|
||||
endRemoveRows();
|
||||
|
||||
// then insert clone of _clipboard
|
||||
beginInsertRows(parentIndex, childNum, childNum);
|
||||
parentItem->insertChild(childNum, _clipboard->cloneNodeAndChildren());
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeModel::pasteAsChild(const QModelIndex& index) {
|
||||
if (_clipboard) {
|
||||
TreeItem* parentItem = _rootItem;
|
||||
if (index.isValid()) {
|
||||
parentItem = static_cast<TreeItem*>(index.internalPointer());
|
||||
}
|
||||
|
||||
beginInsertRows(index, parentItem->childCount(), parentItem->childCount());
|
||||
parentItem->appendChild(_clipboard->cloneNodeAndChildren());
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
TreeItem* TreeModel::loadNode(const QJsonObject& jsonObj) {
|
||||
|
||||
// id
|
||||
auto idVal = jsonObj.value("id");
|
||||
if (!idVal.isString()) {
|
||||
qCritical() << "loadNode, bad string \"id\"";
|
||||
return nullptr;
|
||||
}
|
||||
QString id = idVal.toString();
|
||||
|
||||
// type
|
||||
auto typeVal = jsonObj.value("type");
|
||||
if (!typeVal.isString()) {
|
||||
qCritical() << "loadNode, bad object \"type\", id =" << id;
|
||||
return nullptr;
|
||||
}
|
||||
QString typeStr = typeVal.toString();
|
||||
|
||||
// data
|
||||
auto dataValue = jsonObj.value("data");
|
||||
if (!dataValue.isObject()) {
|
||||
qCritical() << "AnimNodeLoader, bad string \"data\", id =" << id;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QList<QVariant> columnData;
|
||||
columnData << id;
|
||||
columnData << typeStr;
|
||||
columnData << dataValue.toVariant();
|
||||
|
||||
// create node
|
||||
TreeItem* node = new TreeItem(columnData);
|
||||
|
||||
// children
|
||||
auto childrenValue = jsonObj.value("children");
|
||||
if (!childrenValue.isArray()) {
|
||||
qCritical() << "AnimNodeLoader, bad array \"children\", id =" << id;
|
||||
return nullptr;
|
||||
}
|
||||
auto childrenArray = childrenValue.toArray();
|
||||
for (const auto& childValue : childrenArray) {
|
||||
if (!childValue.isObject()) {
|
||||
qCritical() << "AnimNodeLoader, bad object in \"children\", id =" << id;
|
||||
return nullptr;
|
||||
}
|
||||
TreeItem* child = loadNode(childValue.toObject());
|
||||
if (child) {
|
||||
node->appendChild(child);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeItem* TreeModel::getItem(const QModelIndex& index) const {
|
||||
if (index.isValid()) {
|
||||
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return _rootItem;
|
||||
}
|
||||
|
||||
QJsonObject TreeModel::jsonFromItem(TreeItem* treeItem) {
|
||||
QJsonObject obj;
|
||||
|
||||
const int ID_COLUMN = 0;
|
||||
obj.insert("id", treeItem->data(ID_COLUMN).toJsonValue());
|
||||
|
||||
const int TYPE_COLUMN = 1;
|
||||
obj.insert("type", treeItem->data(TYPE_COLUMN).toJsonValue());
|
||||
|
||||
const int DATA_COLUMN = 2;
|
||||
QVariant data = treeItem->data(DATA_COLUMN);
|
||||
if (data.canConvert<QJSValue>()) {
|
||||
obj.insert("data", data.value<QJSValue>().toVariant().toJsonValue());
|
||||
} else {
|
||||
obj.insert("data", data.toJsonValue());
|
||||
}
|
||||
|
||||
QJsonArray children;
|
||||
for (int i = 0; i < treeItem->childCount(); i++) {
|
||||
children.push_back(jsonFromItem(treeItem->child(i)));
|
||||
}
|
||||
obj.insert("children", children);
|
||||
|
||||
return obj;
|
||||
}
|
79
tools/animedit/treemodel.h
Normal file
79
tools/animedit/treemodel.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// TreeModel
|
||||
//
|
||||
// Created by Anthony Thibault on 6/5/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_TreeModel_h
|
||||
#define hifi_TreeModel_h
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QJsonObject>
|
||||
#include <QModelIndex>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
class TreeItem;
|
||||
|
||||
class TreeModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum TreeModelRoles
|
||||
{
|
||||
TreeModelRoleName = Qt::UserRole + 1,
|
||||
TreeModelRoleType,
|
||||
TreeModelRoleData
|
||||
};
|
||||
|
||||
explicit TreeModel(QObject* parent = nullptr);
|
||||
~TreeModel() override;
|
||||
|
||||
// QAbstractItemModel interface
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
// read methods
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = TreeModelRoleName) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex& index) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
// write methods
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = TreeModelRoleName) override;
|
||||
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant& value, int role = TreeModelRoleName) override;
|
||||
bool insertColumns(int position, int columns, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool removeColumns(int position, int columns, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool insertRows(int position, int rows, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool removeRows(int position, int rows, const QModelIndex& parent = QModelIndex()) override;
|
||||
|
||||
// invokable from qml
|
||||
Q_INVOKABLE void loadFromFile(const QString& filename);
|
||||
Q_INVOKABLE void saveToFile(const QString& filename);
|
||||
Q_INVOKABLE void newNode(const QModelIndex& parent);
|
||||
Q_INVOKABLE void deleteNode(const QModelIndex& index);
|
||||
Q_INVOKABLE void insertNodeAbove(const QModelIndex& index);
|
||||
Q_INVOKABLE QVariantList getChildrenModelIndices(const QModelIndex& index);
|
||||
Q_INVOKABLE void copyNode(const QModelIndex& index);
|
||||
Q_INVOKABLE void copyNodeAndChildren(const QModelIndex& index);
|
||||
Q_INVOKABLE void pasteOver(const QModelIndex& index);
|
||||
Q_INVOKABLE void pasteAsChild(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
TreeItem* loadNode(const QJsonObject& jsonObj);
|
||||
TreeItem* getItem(const QModelIndex& index) const;
|
||||
QJsonObject jsonFromItem(TreeItem* treeItem);
|
||||
|
||||
TreeItem* _rootItem;
|
||||
QHash<int, QByteArray> _roleNameMapping;
|
||||
TreeItem* _clipboard;
|
||||
};
|
||||
|
||||
#endif
|
BIN
tools/ci-scripts/codesign.pfx.gpg
Normal file
BIN
tools/ci-scripts/codesign.pfx.gpg
Normal file
Binary file not shown.
32
tools/ci-scripts/linux-gha/Dockerfile
Normal file
32
tools/ci-scripts/linux-gha/Dockerfile
Normal file
|
@ -0,0 +1,32 @@
|
|||
FROM simonwalton1/hifi_base_ubuntu:1.1
|
||||
MAINTAINER DevOps Team (devops@highfidelity.io)
|
||||
|
||||
EXPOSE 40100 40101 40102
|
||||
EXPOSE 40100/udp 40101/udp 40102/udp
|
||||
EXPOSE 48000/udp 48001/udp 48002/udp 48003/udp 48004/udp 48005/udp 48006/udp
|
||||
|
||||
RUN mkdir -p /etc/hifi/server/plugins /etc/hifi/server/resources /etc/hifi/server/imageformats/ && \
|
||||
ln -s /usr/local/Qt5.12.3/5.12.3/gcc_64/plugins/imageformats/* /etc/hifi/server/imageformats/
|
||||
COPY ./assignment-client /etc/hifi/server/
|
||||
#COPY ./oven /etc/hifi/server/
|
||||
COPY ./domain-server /etc/hifi/server/
|
||||
COPY ./plugins/hifiCodec/libhifiCodec.so /etc/hifi/server/plugins/
|
||||
RUN true
|
||||
COPY ./plugins/pcmCodec/libpcmCodec.so /etc/hifi/server/plugins/
|
||||
# Dummy statement
|
||||
RUN true
|
||||
COPY ./*.so /lib/
|
||||
RUN ln -sf /lib/libquazip5.so /lib/libquazip5.so.1
|
||||
COPY ./domain-server/resources/ /etc/hifi/server/resources/
|
||||
RUN true
|
||||
COPY ./hifi.conf /etc/supervisor/conf.d/hifi.conf
|
||||
RUN for fn in /usr/local/Qt5.12.3/5.12.3/gcc_64/plugins/imageformats/*.so; do \
|
||||
if [ ! -x $fn ]; then ln -s $fn /etc/hifi/server/imageformats; fi; done
|
||||
RUN chmod +x /etc/hifi/server/domain-server
|
||||
RUN chmod +x /etc/hifi/server/assignment-client
|
||||
|
||||
# Ensure `domain-server` and `assignment-client` execute.
|
||||
RUN /etc/hifi/server/domain-server --version > /etc/hifi/server/version && \
|
||||
/etc/hifi/server/assignment-client --version >> /etc/hifi/server/version
|
||||
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/hifi.conf"]
|
24
tools/ci-scripts/linux-gha/Dockerfile_linuxbase
Normal file
24
tools/ci-scripts/linux-gha/Dockerfile_linuxbase
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Docker file for hifi_base_ubuntu:1.1
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER DevOps Team (devops@highfidelity.io)
|
||||
|
||||
EXPOSE 40100 40101 40102
|
||||
EXPOSE 40100/udp 40101/udp 40102/udp
|
||||
EXPOSE 48000/udp 48001/udp 48002/udp 48003/udp 48004/udp 48005/udp 48006/udp
|
||||
|
||||
RUN apt-get update && apt-get install -y software-properties-common apt-utils curl
|
||||
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 15FF1AAE && add-apt-repository "deb http://debian.highfidelity.com stable main"
|
||||
RUN echo UTC >/etc/timezone
|
||||
# Installing via dependency causes interactive hang:
|
||||
RUN apt-get -y install tzdata
|
||||
RUN curl http://archive.ubuntu.com/ubuntu/pool/main/b/bison/libbison-dev_3.0.4.dfsg-1build1_amd64.deb >/tmp/libbison-dev.deb
|
||||
RUN dpkg -i /tmp/libbison-dev.deb && rm /tmp/libbison-dev.deb
|
||||
RUN apt-get update && apt-get -y --allow-unauthenticated install libglib2.0-0 libgl1-mesa-dev gdb python-pip build-essential \
|
||||
openssl libssl-dev libssl1.0.0 unzip flex bison gperf perl libsqlite3-dev \
|
||||
libfontconfig1-dev libicu-dev libfreetype6 libssl-dev libpng-dev libjpeg-dev \
|
||||
python libx11-dev libxext-dev libtbb2 hifiqt5.12.3 \
|
||||
ntp unzip libwww-perl libdatetime-perl make pkg-config libnss3 libxi6 \
|
||||
libxcursor1 libxcomposite1 libasound2 libxtst6 libxslt1.1 supervisor
|
||||
# Additional packages for v1.1:
|
||||
RUN apt-get -y --allow-unauthenticated install mesa-common-dev libegl1 libglvnd-dev \
|
||||
libdouble-conversion1 libpulse0
|
60
tools/ci-scripts/linux-gha/hifi.conf
Normal file
60
tools/ci-scripts/linux-gha/hifi.conf
Normal file
|
@ -0,0 +1,60 @@
|
|||
[supervisord]
|
||||
user=root
|
||||
nodaemon=true
|
||||
|
||||
[program:domain-server]
|
||||
command=/etc/hifi/server/domain-server
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:audio-mixer]
|
||||
command=/etc/hifi/server/assignment-client -t 0 -a localhost -p 48000
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:avatar-mixer]
|
||||
command=/etc/hifi/server/assignment-client -t 1 -a localhost -p 48001
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:entities-server]
|
||||
command=/etc/hifi/server/assignment-client -t 6 -a localhost -p 48006
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:asset-server]
|
||||
command=/etc/hifi/server/assignment-client -t 3 -a localhost -p 48003
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:entity-script-server]
|
||||
command=/etc/hifi/server/assignment-client -t 5 -a localhost -p 48005
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:messages-mixer]
|
||||
command=/etc/hifi/server/assignment-client -t 4 -a localhost -p 48004
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
||||
[program:scripted-agent]
|
||||
command=/etc/hifi/server/assignment-client -t 2 -a localhost --max 100
|
||||
autorestart=unexpected
|
||||
directory=/etc/hifi/server
|
||||
stderr_logfile=/var/log/hifi-err.log
|
||||
stdout_logfile=/var/log/hifi-out.log
|
||||
|
|
@ -31,14 +31,14 @@ elif sys.platform == "darwin":
|
|||
def computeArchiveName(prefix):
|
||||
RELEASE_TYPE = os.getenv("RELEASE_TYPE", "DEV")
|
||||
RELEASE_NUMBER = os.getenv("RELEASE_NUMBER", "")
|
||||
GIT_PR_COMMIT_SHORT = os.getenv("SHA7", "")
|
||||
if GIT_PR_COMMIT_SHORT == '':
|
||||
GIT_PR_COMMIT_SHORT = os.getenv("GIT_PR_COMMIT_SHORT", "")
|
||||
GIT_COMMIT_SHORT = os.getenv("SHA7", "")
|
||||
if GIT_COMMIT_SHORT == '':
|
||||
GIT_COMMIT_SHORT = os.getenv("GIT_COMMIT_SHORT", "")
|
||||
|
||||
if RELEASE_TYPE == "PRODUCTION":
|
||||
BUILD_VERSION = "{}-{}".format(RELEASE_NUMBER, GIT_PR_COMMIT_SHORT)
|
||||
BUILD_VERSION = "{}-{}".format(RELEASE_NUMBER, GIT_COMMIT_SHORT)
|
||||
elif RELEASE_TYPE == "PR":
|
||||
BUILD_VERSION = "PR{}-{}".format(RELEASE_NUMBER, GIT_PR_COMMIT_SHORT)
|
||||
BUILD_VERSION = "PR{}-{}".format(RELEASE_NUMBER, GIT_COMMIT_SHORT)
|
||||
else:
|
||||
BUILD_VERSION = "dev"
|
||||
|
||||
|
|
24
tools/ci-scripts/pr_comment.py
Normal file
24
tools/ci-scripts/pr_comment.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Post build script
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import glob
|
||||
from github import Github
|
||||
|
||||
|
||||
def main():
|
||||
bucket_name = os.environ['BUCKET_NAME']
|
||||
upload_prefix = os.environ['UPLOAD_PREFIX']
|
||||
context = json.loads(os.environ['GITHUB_CONTEXT'])
|
||||
baseUrl = 'https://{}.s3.amazonaws.com/{}/'.format(bucket_name, upload_prefix)
|
||||
g = Github(os.environ['GITHUB_TOKEN'])
|
||||
repo = g.get_repo(context['repository'])
|
||||
pr = repo.get_pull(context['event']['number'])
|
||||
|
||||
path = os.path.join(os.getcwd(), os.environ['ARTIFACT_PATTERN'])
|
||||
files = glob.glob(path, recursive=False)
|
||||
for archiveFile in files:
|
||||
filePath, fileName = os.path.split(archiveFile)
|
||||
pr.create_issue_comment("Build artifact uploaded as [{}]({}{})".format(fileName, baseUrl, fileName))
|
||||
|
||||
main()
|
|
@ -10,18 +10,15 @@ from github import Github
|
|||
def main():
|
||||
bucket_name = os.environ['BUCKET_NAME']
|
||||
upload_prefix = os.environ['UPLOAD_PREFIX']
|
||||
context = json.loads(os.environ['GITHUB_CONTEXT'])
|
||||
baseUrl = 'https://{}.s3.amazonaws.com/{}/'.format(bucket_name, upload_prefix)
|
||||
release_number = os.environ['RELEASE_NUMBER']
|
||||
full_prefix = upload_prefix + '/' + release_number[0:-2] + '/' + release_number
|
||||
S3 = boto3.client('s3')
|
||||
g = Github(os.environ['GITHUB_TOKEN'])
|
||||
repo = g.get_repo(context['repository'])
|
||||
pr = repo.get_pull(context['event']['number'])
|
||||
|
||||
path = os.path.join(os.getcwd(), os.environ['ARTIFACT_PATTERN'])
|
||||
files = glob.glob(path, recursive=False)
|
||||
for archiveFile in files:
|
||||
filePath, fileName = os.path.split(archiveFile)
|
||||
pr.create_issue_comment("Build artifact uploaded as [{}]({}{})".format(fileName, baseUrl, fileName))
|
||||
S3.upload_file(os.path.join(filePath, fileName), bucket_name, upload_prefix + '/' + fileName)
|
||||
S3.upload_file(os.path.join(filePath, fileName), bucket_name, full_prefix + '/' + fileName)
|
||||
print("Uploaded Artifact to S3: https://{}.s3-us-west-2.amazonaws.com/{}/{}".format(bucket_name, full_prefix, fileName))
|
||||
print("Finished")
|
||||
|
||||
main()
|
||||
|
|
|
@ -25,3 +25,17 @@ target_opengl()
|
|||
#target_vulkan()
|
||||
|
||||
package_libraries_for_deployment()
|
||||
|
||||
if (BUILD_GPU_FRAME_PLAYER_ONLY)
|
||||
|
||||
# setup install of executable and things copied by fixup/windeployqt
|
||||
install(
|
||||
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||
DESTINATION "."
|
||||
COMPONENT "client"
|
||||
PATTERN "*.pdb" EXCLUDE
|
||||
PATTERN "*.lib" EXCLUDE
|
||||
PATTERN "*.exp" EXCLUDE
|
||||
)
|
||||
|
||||
endif()
|
|
@ -57,6 +57,7 @@ exports.handlers = {
|
|||
'../../libraries/physics/src',
|
||||
'../../libraries/platform/src/platform/backend',
|
||||
'../../libraries/plugins/src/plugins',
|
||||
'../../libraries/procedural/src/procedural',
|
||||
'../../libraries/pointers/src',
|
||||
'../../libraries/render-utils/src',
|
||||
'../../libraries/script-engine/src',
|
||||
|
|
Loading…
Reference in a new issue