mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 10:32:25 +02:00
Merge branch 'overte-org:master' into mouselook_refactor
This commit is contained in:
commit
f2e1ae9a62
117 changed files with 1696 additions and 710 deletions
106
.github/workflows/linux_server_build.yml
vendored
106
.github/workflows/linux_server_build.yml
vendored
|
@ -10,8 +10,10 @@ on:
|
|||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
# Release tags. E.g. 2024.06.1
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
|
||||
- "[0-9][0-9][0-9][0-9].[0-9][0-9].**"
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
@ -19,8 +21,8 @@ env:
|
|||
UPLOAD_BUCKET: overte-public
|
||||
UPLOAD_REGION: fra1
|
||||
UPLOAD_ENDPOINT: "https://fra1.digitaloceanspaces.com"
|
||||
CMAKE_BACKTRACE_URL: ${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}
|
||||
CMAKE_BACKTRACE_TOKEN: server_${{ github.event.number }}_${{ github.sha }}
|
||||
# Disable VCPKG caching to save time.
|
||||
VCPKG_FEATURE_FLAGS: -binarycaching
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -33,73 +35,77 @@ jobs:
|
|||
- os: debian-11
|
||||
image: docker.io/overte/overte-server-build:0.1.3-debian-11-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
# https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Meta-Labels
|
||||
# self_hosted makes the Hetzner auto-scaler put up the job.
|
||||
# type-cx52 is a Hetzner VPS server type. In this case cs52 is a server with 16-cores and 32GB of RAM.
|
||||
# image-x86-app-docker-ce is a Hetzner image.
|
||||
# https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Specifying-The-Runner-Image
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
- os: debian-11
|
||||
image: docker.io/overte/overte-server-build:0.1.3-debian-11-aarch64
|
||||
arch: aarch64
|
||||
runner: linux_aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
|
||||
- os: debian-12
|
||||
image: docker.io/overte/overte-server-build:0.1.3-debian-12-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
- os: debian-12
|
||||
image: docker.io/overte/overte-server-build:0.1.3-debian-12-aarch64
|
||||
arch: aarch64
|
||||
runner: linux_aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
|
||||
- os: ubuntu-20.04
|
||||
image: docker.io/overte/overte-server-build:0.1.3-ubuntu-20.04-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
- os: ubuntu-22.04
|
||||
image: docker.io/overte/overte-server-build:0.1.3-ubuntu-22.04-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
- os: ubuntu-22.04
|
||||
image: docker.io/overte/overte-server-build:0.1.3-ubuntu-22.04-aarch64
|
||||
arch: aarch64
|
||||
runner: linux_aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
|
||||
- os: ubuntu-24.04
|
||||
image: docker.io/overte/overte-server-build:0.1.3-ubuntu-24.04-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
- os: ubuntu-24.04
|
||||
image: docker.io/overte/overte-server-build:0.1.3-ubuntu-24.04-aarch64
|
||||
arch: aarch64
|
||||
runner: linux_aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
|
||||
- os: fedora-38
|
||||
image: docker.io/overte/overte-server-build:0.1.3-fedora-38-amd64
|
||||
- os: fedora-39
|
||||
image: docker.io/overte/overte-server-build:0.1.4-fedora-39-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
- os: fedora-38
|
||||
image: docker.io/overte/overte-server-build:0.1.3-fedora-38-aarch64
|
||||
- os: fedora-39
|
||||
image: docker.io/overte/overte-server-build:0.1.4-fedora-39-aarch64
|
||||
arch: aarch64
|
||||
runner: linux_aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
|
||||
# Packaging broken; See: https://github.com/overte-org/overte/issues/968
|
||||
#~ - os: fedora-39
|
||||
#~ image: docker.io/overte/overte-server-build:0.1.4-fedora-39-amd64
|
||||
#~ arch: amd64
|
||||
#~ runner: linux_amd64
|
||||
- os: fedora-40
|
||||
image: docker.io/overte/overte-server-build:0.1.4-fedora-40-amd64
|
||||
arch: amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
#~ - os: fedora-39
|
||||
#~ image: docker.io/overte/overte-server-build:0.1.4-fedora-39-aarch64
|
||||
#~ arch: aarch64
|
||||
#~ runner: linux_aarch64
|
||||
- os: fedora-40
|
||||
image: docker.io/overte/overte-server-build:0.1.4-fedora-40-aarch64
|
||||
arch: aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
|
||||
- os: rockylinux-9
|
||||
image: docker.io/overte/overte-server-build:0.1.3-rockylinux-9-amd64
|
||||
arch: amd64
|
||||
runner: linux_amd64
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
|
||||
fail-fast: false
|
||||
|
||||
|
@ -143,7 +149,7 @@ jobs:
|
|||
fi
|
||||
|
||||
# Tagged builds. E.g. release or release candidate builds.
|
||||
if [ "${{github.event_name}}" != "pull_request" ]; then
|
||||
if [ "${{github.ref_type}}" == "tag" ]; then
|
||||
echo "PRODUCTION_BUILD=true" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
|
@ -177,16 +183,30 @@ jobs:
|
|||
echo "UPLOAD_PREFIX=build/overte/master" >> $GITHUB_ENV
|
||||
echo "RELEASE_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
|
||||
else # tagged
|
||||
echo "DEBVERSION=${{ github.run_number }}-${{ github.ref_name }}-$GIT_COMMIT_SHORT-${{ matrix.os }}" >> $GITHUB_ENV
|
||||
echo "RPMVERSION=${${{ github.ref_name }}//-/.}.${{ github.run_number }}.$GIT_COMMIT_SHORT" >> $GITHUB_ENV
|
||||
echo "DEBVERSION=${{ github.ref_name }}-$GIT_COMMIT_SHORT-${{ matrix.os }}" >> $GITHUB_ENV
|
||||
echo "RPMVERSION=${{ github.ref_name }}.$GIT_COMMIT_SHORT" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
if [[ "${{ github.ref_name }}" != "master" && "${{ github.ref_name }}" != "pull_request" ]]; then # tagged
|
||||
echo "RELEASE_NUMBER=/${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
if [ "${{ github.ref_name }}" == *"rc"* ]; then # release candidate
|
||||
echo "UPLOAD_PREFIX=build/overte/release-candidate" >> $GITHUB_ENV
|
||||
if [ "${{ github.ref_type }}" == "tag" ]; then # tagged
|
||||
echo "RELEASE_NUMBER=${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
if [[ "${{ github.ref_name }}" == *"rc"* ]]; then # release candidate
|
||||
# The uploader already creates a subfolder for each RELEASE_NUMBER.
|
||||
echo "UPLOAD_PREFIX=build/overte/release-candidate/" >> $GITHUB_ENV
|
||||
else # release
|
||||
echo "UPLOAD_PREFIX=build/overte/release" >> $GITHUB_ENV
|
||||
echo "UPLOAD_PREFIX=build/overte/release/" >> $GITHUB_ENV
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "BUILD_NUMBER=$GIT_COMMIT_SHORT" >> $GITHUB_ENV
|
||||
|
||||
if [ -z "$CMAKE_BACKTRACE_URL" ]; then
|
||||
if [ "${{ github.ref_type }}" == "tag" ]; then
|
||||
export CMAKE_BACKTRACE_URL="${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}"
|
||||
export CMAKE_BACKTRACE_TOKEN="${{ github.ref_name }}_${{ matrix.os }}_${{ github.sha }}"
|
||||
else
|
||||
# We're building a PR, default to the PR endpoint
|
||||
export CMAKE_BACKTRACE_URL="https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32"
|
||||
export CMAKE_BACKTRACE_TOKEN="server_pr_${{ github.event.number }}_${{ github.sha }}"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -207,6 +227,8 @@ jobs:
|
|||
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc38.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
elif [ "${{ matrix.os }}" == "fedora-39" ]; then
|
||||
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc39.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
elif [ "${{ matrix.os }}" == "fedora-40" ]; then
|
||||
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc40.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Error! ARTIFACT_PATTERN not set!"
|
||||
exit 1 # Fail
|
||||
|
@ -214,7 +236,7 @@ jobs:
|
|||
fi
|
||||
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
fetch-depth: 1
|
||||
|
@ -227,11 +249,6 @@ jobs:
|
|||
working-directory: build
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -z "$CMAKE_BACKTRACE_URL" ] ; then
|
||||
# We're building a PR, default to the PR endpoint
|
||||
export CMAKE_BACKTRACE_URL="https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32"
|
||||
export CMAKE_BACKTRACE_TOKEN="server_pr_${{ github.event.number }}_${{ github.sha }}"
|
||||
fi
|
||||
|
||||
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release $CMAKE_EXTRA
|
||||
|
||||
|
@ -286,8 +303,7 @@ jobs:
|
|||
df -h
|
||||
|
||||
- name: Upload artifact to GitHub
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_PATTERN }}
|
||||
path: pkg-scripts/${{ env.ARTIFACT_PATTERN }}
|
||||
|
|
20
.github/workflows/master_build.yml
vendored
20
.github/workflows/master_build.yml
vendored
|
@ -1,6 +1,6 @@
|
|||
# Copyright 2013-2019 High Fidelity, Inc.
|
||||
# Copyright 2020-2022 Vircadia contributors
|
||||
# Copyright 2021-2022 Overte e.V.
|
||||
# Copyright 2021-2024 Overte e.V.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Master CI Build
|
||||
|
@ -26,6 +26,8 @@ env:
|
|||
UPLOAD_ENDPOINT: "https://fra1.digitaloceanspaces.com"
|
||||
CMAKE_BACKTRACE_URL: ${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}
|
||||
CMAKE_BACKTRACE_TOKEN: master_${{ github.event.number }}_${{ github.sha }}
|
||||
# Disable VCPKG caching to save time.
|
||||
VCPKG_FEATURE_FLAGS: -binarycaching
|
||||
|
||||
# OSX-specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
|
@ -120,7 +122,7 @@ jobs:
|
|||
rm -rf ~/overte-files
|
||||
rm -rf ~/.cache
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
fetch-depth: 1
|
||||
|
@ -242,14 +244,12 @@ jobs:
|
|||
working-directory: ${{runner.workspace}}/build
|
||||
run: cat ./_CPack_Packages/win64/NSIS/NSISOutput.log
|
||||
|
||||
- name: Upload artifact
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS') || matrix.build_type == 'android' # Automatic Linux packaging is not implemented
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.s3_access_key_id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.s3_secret_access_key }}
|
||||
run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py
|
||||
- name: Upload artifact to GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_PATTERN }}
|
||||
path: ./build/${{ env.ARTIFACT_PATTERN }}
|
||||
if-no-files-found: error
|
||||
|
||||
#- name: Archive symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
|
|
4
.github/workflows/master_deploy_apidocs.yml
vendored
4
.github/workflows/master_deploy_apidocs.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2022-2023 Overte e.V.
|
||||
# Copyright 2022-2024 Overte e.V.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Master API-docs CI Build and Deploy
|
||||
|
@ -14,7 +14,7 @@ jobs:
|
|||
|
||||
name: Build and deploy API-docs
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: tools/jsdoc
|
||||
|
|
4
.github/workflows/master_deploy_doxygen.yml
vendored
4
.github/workflows/master_deploy_doxygen.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2022-2023 Overte e.V.
|
||||
# Copyright 2022-2024 Overte e.V.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Master Doxygen CI Build and Deploy
|
||||
|
@ -14,7 +14,7 @@ jobs:
|
|||
|
||||
name: Build and deploy Doxygen documentation
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
|
17
.github/workflows/pr_build.yml
vendored
17
.github/workflows/pr_build.yml
vendored
|
@ -1,6 +1,6 @@
|
|||
# Copyright 2013-2019 High Fidelity, Inc.
|
||||
# Copyright 2020-2022 Vircadia contributors.
|
||||
# Copyright 2021-2022 Overte e.V.
|
||||
# Copyright 2021-2024 Overte e.V.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Pull Request CI Build
|
||||
|
@ -24,6 +24,8 @@ env:
|
|||
# We can't use secrets or actions here, so the actual value has to be hardcoded.
|
||||
CMAKE_BACKTRACE_URL: "https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32"
|
||||
CMAKE_BACKTRACE_TOKEN: PR_${{ github.event.number }}_${{ github.sha }}
|
||||
# Disable VCPKG caching to save time.
|
||||
VCPKG_FEATURE_FLAGS: -binarycaching
|
||||
|
||||
UPLOAD_BUCKET: overte-public
|
||||
UPLOAD_REGION: fra1
|
||||
|
@ -54,7 +56,12 @@ jobs:
|
|||
#- os: macOS-10.15
|
||||
# build_type: full
|
||||
- os: Ubuntu 20.04
|
||||
runner: linux_amd64
|
||||
# https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Meta-Labels
|
||||
# self_hosted makes the Hetzner auto-scaler put up the job.
|
||||
# type-cx52 is a Hetzner VPS server type. In this case cs52 is a server with 16-cores and 32GB of RAM.
|
||||
# image-x86-app-docker-ce is a Hetzner image.
|
||||
# https://github.com/testflows/TestFlows-GitHub-Hetzner-Runners/wiki/Specifying-The-Runner-Image
|
||||
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
|
||||
arch: amd64
|
||||
build_type: full
|
||||
apt-dependencies: pkg-config libxext-dev libdouble-conversion-dev libpcre2-16-0 libpulse0 libharfbuzz-dev libnss3 libnspr4 libxdamage1 libasound2 # add missing dependencies to docker image when convenient
|
||||
|
@ -65,7 +72,7 @@ jobs:
|
|||
# apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 python3-github python3-distro
|
||||
# Do not change the names of self-hosted runners without knowing what you are doing, as they correspond to labels that have to be set on the runner.
|
||||
- os: Ubuntu 22.04
|
||||
runner: linux_aarch64
|
||||
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
|
||||
arch: aarch64
|
||||
build_type: full
|
||||
image: docker.io/overte/overte-full-build:0.1.1-ubuntu-22.04-aarch64
|
||||
|
@ -165,7 +172,7 @@ jobs:
|
|||
rm -rf ~/overte-files
|
||||
rm -rf ~/.cache
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
fetch-depth: 1
|
||||
|
@ -335,7 +342,7 @@ jobs:
|
|||
|
||||
- name: Upload Artifact
|
||||
if: startsWith(matrix.os, 'Windows') || startsWith(matrix.os, 'macOS')
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_PATTERN }}
|
||||
path: ./build/${{ env.ARTIFACT_PATTERN }}
|
||||
|
|
212
.github/workflows/release_build.yml
vendored
Normal file
212
.github/workflows/release_build.yml
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
# Copyright 2013-2019 High Fidelity, Inc.
|
||||
# Copyright 2020-2022 Vircadia contributors
|
||||
# Copyright 2021-2024 Overte e.V.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Windows Release Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
# Release tags. E.g. 2024.06.1
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
|
||||
- "[0-9][0-9][0-9][0-9].[0-9][0-9].**"
|
||||
|
||||
env:
|
||||
APP_NAME: interface
|
||||
BUILD_TYPE: Release
|
||||
CI_BUILD: Github
|
||||
GIT_COMMIT: ${{ github.sha }}
|
||||
PRODUCTION_BUILD: true
|
||||
RELEASE_TYPE: PRODUCTION
|
||||
RELEASE_NUMBER: ${{ github.ref_name }}
|
||||
STABLE_BUILD: 1
|
||||
UPLOAD_BUCKET: overte-public
|
||||
UPLOAD_REGION: fra1
|
||||
UPLOAD_ENDPOINT: "https://fra1.digitaloceanspaces.com"
|
||||
CMAKE_BACKTRACE_URL: ${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}
|
||||
CMAKE_BACKTRACE_TOKEN: ${{ github.ref_name }}_Windows_${{ github.sha }}
|
||||
# Disable VCPKG caching to save time.
|
||||
VCPKG_FEATURE_FLAGS: -binarycaching
|
||||
|
||||
# WIN-specific variables
|
||||
PreferredToolArchitecture: X64
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2019
|
||||
build_type: full
|
||||
fail-fast: false
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- name: Configure build environment 1
|
||||
shell: bash
|
||||
id: buildenv1
|
||||
run: |
|
||||
|
||||
echo "GIT_COMMIT_SHORT=`echo ${{ github.sha }} | cut -c1-7`" >> $GITHUB_ENV
|
||||
|
||||
if [[ "${{ github.ref_name }}" == *"rc"* ]]; then # release candidate
|
||||
# The uploader already creates a subfolder for each RELEASE_NUMBER.
|
||||
echo "UPLOAD_PREFIX=build/overte/release-candidate/" >> $GITHUB_ENV
|
||||
else # release
|
||||
echo "UPLOAD_PREFIX=build/overte/release/" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
echo ::set-output name=github_sha_short::`echo $GIT_COMMIT | cut -c1-7`
|
||||
echo "JOB_NAME=${{matrix.os}}, ${{matrix.build_type}}" >> $GITHUB_ENV
|
||||
echo "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV
|
||||
|
||||
echo "PYTHON_EXEC=python" >> $GITHUB_ENV
|
||||
echo "ZIP_COMMAND=7z" >> $GITHUB_ENV
|
||||
echo "ZIP_ARGS=a" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=exe" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-A x64" >> $GITHUB_ENV
|
||||
echo "SYMBOL_REGEX=\(exe\|dll\|pdb\)" >> $GITHUB_ENV
|
||||
echo "SYMBOLS_ARCHIVE=$RELEASE_NUMBER-${{ github.sha }}-win-symbols.zip" >> $GITHUB_ENV
|
||||
# echo "HF_PFX_PASSPHRASE=${{secrets.pfx_key}}" >> $GITHUB_ENV
|
||||
# echo "HF_PFX_FILE=${{runner.workspace}}\build\codesign.pfx" >> $GITHUB_ENV
|
||||
|
||||
# Configuration is broken into two steps because you can't set an env var and also reference it in the same step
|
||||
- name: Configure build environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
echo "BUILD_NUMBER=$GIT_COMMIT_SHORT" >> $GITHUB_ENV
|
||||
echo "ARTIFACT_PATTERN=Overte-$RELEASE_NUMBER.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
echo "CLIENT_ONLY=FALSE" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Override NSIS
|
||||
shell: pwsh
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: choco install nsis --allow-downgrade --version=3.06.1
|
||||
|
||||
- name: Install Python modules
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
shell: bash
|
||||
run: $PYTHON_EXEC -m pip install boto3 PyGithub
|
||||
|
||||
- name: Create build environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory "${{runner.workspace}}/build"
|
||||
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY -DBYPASS_SIGNING:BOOLEAN=TRUE $CMAKE_EXTRA
|
||||
|
||||
- name: Build application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_TARGET_NAME $CMAKE_BUILD_EXTRA
|
||||
|
||||
- name: Build domain server
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server $CMAKE_BUILD_EXTRA
|
||||
|
||||
- name: Build assignment client
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client $CMAKE_BUILD_EXTRA
|
||||
|
||||
- name: Build console
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console $CMAKE_BUILD_EXTRA
|
||||
|
||||
- name: Build installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Retry code from https://unix.stackexchange.com/a/137639"
|
||||
function fail {
|
||||
echo $1 >&2
|
||||
exit 1
|
||||
}
|
||||
function retry {
|
||||
local n=1
|
||||
local max=5
|
||||
local delay=15
|
||||
while true; do
|
||||
"$@" && break || {
|
||||
if [[ $n -lt $max ]]; then
|
||||
((n++))
|
||||
echo "Command failed. Attempt $n/$max:"
|
||||
sleep $delay;
|
||||
else
|
||||
fail "The command has failed after $n attempts."
|
||||
fi
|
||||
}
|
||||
done
|
||||
}
|
||||
retry cmake --build . --config $BUILD_TYPE --target package $CMAKE_BUILD_EXTRA
|
||||
|
||||
#- name: Sign installer (Windows)
|
||||
# if: startsWith(matrix.os, 'windows')
|
||||
# shell: powershell
|
||||
# working-directory: C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64
|
||||
# run: .\signtool.exe sign /fd sha256 /f ${{runner.workspace}}\build\codesign.pfx /p ${{secrets.pfx_key}} /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${{runner.workspace}}\build\${env:ARTIFACT_PATTERN}
|
||||
|
||||
- name: Output system stats
|
||||
if: ${{ always() }}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Disk usage:"
|
||||
df -h
|
||||
|
||||
- name: Output installer logs
|
||||
if: failure() && startsWith(matrix.os, 'windows')
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cat ./_CPack_Packages/win64/NSIS/NSISOutput.log
|
||||
|
||||
- name: Upload artifact to S3
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.s3_access_key_id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.s3_secret_access_key }}
|
||||
run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py
|
||||
|
||||
- name: Upload artifact to GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_PATTERN }}
|
||||
path: ${{runner.workspace}}/build/${{ env.ARTIFACT_PATTERN }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Archive symbols
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
working-directory: ${{runner.workspace}}
|
||||
shell: bash
|
||||
run: |
|
||||
SYMBOLS_TEMP="symbols-temp"
|
||||
mkdir $SYMBOLS_TEMP
|
||||
find "./build" -regex ".*\.$SYMBOL_REGEX" -exec cp -r {} $SYMBOLS_TEMP \;
|
||||
cd $SYMBOLS_TEMP
|
||||
$ZIP_COMMAND $ZIP_ARGS ../$SYMBOLS_ARCHIVE .
|
||||
|
||||
- name: Upload debug symbols to GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.SYMBOLS_ARCHIVE }}
|
||||
path: ${{runner.workspace}}/${{ env.SYMBOLS_ARCHIVE }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Clear Working Directories
|
||||
if: contains(matrix.runner, 'linux_aarch64')
|
||||
shell: bash
|
||||
run: |
|
||||
rm -rf ./*
|
||||
rm -rf ~/overte-files
|
||||
rm -rf ~/.cache
|
3
BUILD.md
3
BUILD.md
|
@ -111,10 +111,7 @@ CMAKE_BACKTRACE_TOKEN
|
|||
|
||||
// The release version, e.g., 2021.3.2.
|
||||
RELEASE_NUMBER
|
||||
// The release name, e.g., Eos.
|
||||
RELEASE_NAME
|
||||
// The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b.
|
||||
|
||||
BUILD_NUMBER
|
||||
|
||||
// The type of release.
|
||||
|
|
|
@ -185,11 +185,11 @@ endif()
|
|||
|
||||
if(OVERTE_WARNINGS_AS_ERRORS)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR (CMAKE_CXX_COMPILER_ID MATCHES "" AND WIN32))
|
||||
set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} /WX")
|
||||
set(ENV{CFLAGS} "$ENV{CXXFLAGS} /WX")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX")
|
||||
set(CMAKE_CFLAGS "${CMAKE_CFLAGS} /WX")
|
||||
else()
|
||||
set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} -Werror")
|
||||
set(ENV{CFLAGS} "$ENV{CXXFLAGS} -Werror")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
set(CMAKE_CFLAGS "${CMAKE_CFLAGS} -Werror")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS
|
|||
1. Ensure you have all the prerequisites fulfilled from the [MacOS Build Guide](BUILD_OSX.md).
|
||||
2. Perform a clean CMake in your build folder. e.g.
|
||||
```bash
|
||||
BUILD_GLOBAL_SERVICES=STABLE USE_STABLE_GLOBAL_SERVICES=1 RELEASE_BUILD=PRODUCTION BUILD_NUMBER="Insert Build Identifier here e.g. short hash of your last Git commit" RELEASE_NAME="Insert Release Name Here" STABLE_BUILD=1 PRODUCTION_BUILD=1 RELEASE_NUMBER="Insert Release Version Here e.g. 1.1.0" RELEASE_TYPE=PRODUCTION cmake -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCLIENT_ONLY=1 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 ..
|
||||
BUILD_GLOBAL_SERVICES=STABLE USE_STABLE_GLOBAL_SERVICES=1 RELEASE_BUILD=PRODUCTION BUILD_NUMBER="Insert Build Identifier here e.g. short hash of your last Git commit" STABLE_BUILD=1 PRODUCTION_BUILD=1 RELEASE_NUMBER="Insert Release Version Here e.g. 1.1.0" RELEASE_TYPE=PRODUCTION cmake -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCLIENT_ONLY=1 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 ..
|
||||
```
|
||||
3. Pick a method to build and package your release.
|
||||
|
||||
|
|
|
@ -6,10 +6,6 @@ if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
|||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
if (USE_CCACHE OR "$ENV{USE_CCACHE}")
|
||||
configure_ccache()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
#
|
||||
# ConfigureCCache.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Created by Clement Brisset on 10/10/18.
|
||||
# Copyright 2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(configure_ccache)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
message(STATUS "Configuring ccache")
|
||||
|
||||
# Set up wrapper scripts
|
||||
set(C_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
set(CXX_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
|
||||
set(LAUNCH_C_IN "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/launch-c.in")
|
||||
set(LAUNCH_CXX_IN "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/launch-cxx.in")
|
||||
set(LAUNCH_C "${CMAKE_BINARY_DIR}/CMakeFiles/launch-c")
|
||||
set(LAUNCH_CXX "${CMAKE_BINARY_DIR}/CMakeFiles/launch-cxx")
|
||||
|
||||
configure_file(${LAUNCH_C_IN} ${LAUNCH_C})
|
||||
configure_file(${LAUNCH_CXX_IN} ${LAUNCH_CXX})
|
||||
execute_process(COMMAND chmod a+rx ${LAUNCH_C} ${LAUNCH_CXX})
|
||||
|
||||
if(CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
# Set Xcode project attributes to route compilation and linking
|
||||
# through our scripts
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CC ${LAUNCH_C})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CXX ${LAUNCH_CXX})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LD ${LAUNCH_C})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS ${LAUNCH_CXX})
|
||||
else()
|
||||
# Support Unix Makefiles and Ninja
|
||||
set(CMAKE_C_COMPILER_LAUNCHER ${LAUNCH_C})
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER ${LAUNCH_CXX})
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Could not find ccache")
|
||||
endif()
|
||||
endmacro()
|
|
@ -23,7 +23,6 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
|
||||
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
|
||||
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
|
||||
set_from_env(RELEASE_NAME RELEASE_NAME "")
|
||||
set_from_env(STABLE_BUILD STABLE_BUILD 0)
|
||||
|
||||
set_from_env(PRELOADED_STARTUP_LOCATION PRELOADED_STARTUP_LOCATION "")
|
||||
|
|
38
cmake/ports/artery-font-format/disable-checksum.patch
Normal file
38
cmake/ports/artery-font-format/disable-checksum.patch
Normal file
|
@ -0,0 +1,38 @@
|
|||
diff --git a/artery-font/serialization.hpp b/artery-font/serialization.hpp
|
||||
index 69263a8..6075eda 100644
|
||||
--- a/artery-font/serialization.hpp
|
||||
+++ b/artery-font/serialization.hpp
|
||||
@@ -109,15 +109,16 @@ template <ReadFunction READ, typename REAL, template <typename> class LIST, clas
|
||||
bool decode(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, void *userData) {
|
||||
uint32 totalLength = 0;
|
||||
uint32 prevLength = 0;
|
||||
- uint32 checksum = crc32Init();
|
||||
+ //uint32 checksum = crc32Init();
|
||||
byte dump[4];
|
||||
#define ARTERY_FONT_DECODE_READ(target, len) { \
|
||||
if (READ((target), (len), userData) != int(len)) \
|
||||
return false; \
|
||||
totalLength += (len); \
|
||||
- for (int _i = 0; _i < int(len); ++_i) \
|
||||
- checksum = crc32Update(checksum, reinterpret_cast<const byte *>(target)[_i]); \
|
||||
}
|
||||
+ // for (int _i = 0; _i < int(len); ++_i) \
|
||||
+ // checksum = crc32update(checksum, reinterpret_cast<const byte *>(target)[_i]); \
|
||||
+ //}
|
||||
#define ARTERY_FONT_DECODE_REALIGN() { \
|
||||
if (totalLength&0x03u) { \
|
||||
uint32 len = 0x04u-(totalLength&0x03u); \
|
||||
@@ -228,10 +229,10 @@ bool decode(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, void *userData) {
|
||||
ARTERY_FONT_DECODE_READ(&footer, sizeof(footer)-sizeof(footer.checksum));
|
||||
if (footer.magicNo != ARTERY_FONT_FOOTER_MAGIC_NO)
|
||||
return false;
|
||||
- uint32 finalChecksum = checksum;
|
||||
+ //uint32 finalChecksum = checksum;
|
||||
ARTERY_FONT_DECODE_READ(&footer.checksum, sizeof(footer.checksum));
|
||||
- if (footer.checksum != finalChecksum)
|
||||
- return false;
|
||||
+ //if (footer.checksum != finalChecksum)
|
||||
+ // return false;
|
||||
if (totalLength != footer.totalLength)
|
||||
return false;
|
||||
}
|
15
cmake/ports/artery-font-format/portfile.cmake
Normal file
15
cmake/ports/artery-font-format/portfile.cmake
Normal file
|
@ -0,0 +1,15 @@
|
|||
# header-only library
|
||||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO Chlumsky/artery-font-format
|
||||
REF 34134bde3cea35a93c2ae5703fa8d3d463793400
|
||||
SHA512 6b2fc0de9ca7b367c9b98f829ce6cee858f1252b12a49b6f1e89a5a2fdb109e20ef812f0b30495195ca0b177adae32d5e238fdc305724857ced098be2d29a6af
|
||||
HEAD_REF master
|
||||
PATCHES "disable-checksum.patch"
|
||||
)
|
||||
|
||||
file(COPY "${SOURCE_PATH}/artery-font" DESTINATION "${CURRENT_PACKAGES_DIR}/include")
|
||||
|
||||
# Handle copyright
|
||||
configure_file("${SOURCE_PATH}/LICENSE.txt" "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" COPYONLY)
|
7
cmake/ports/artery-font-format/vcpkg.json
Normal file
7
cmake/ports/artery-font-format/vcpkg.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "artery-font-format",
|
||||
"version": "1.0.1",
|
||||
"description": "Header-only C++ library that facilitates encoding and decoding of the Artery Atlas Font format",
|
||||
"homepage": "https://github.com/Chlumsky/artery-font-format",
|
||||
"license": "MIT"
|
||||
}
|
|
@ -5,4 +5,4 @@
|
|||
Source: hifi-deps
|
||||
Version: 0.1.5-github-actions
|
||||
Description: Collected dependencies for High Fidelity applications
|
||||
Build-Depends: bullet3, cgltf, draco, etc2comp, glad, glm, node, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android|!(linux&arm)), zlib
|
||||
Build-Depends: artery-font-format, bullet3, cgltf, draco, etc2comp, glad, glm, node, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android|!(linux&arm)), zlib
|
||||
|
|
|
@ -25,7 +25,6 @@ namespace BuildInfo {
|
|||
const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@";
|
||||
const QString ORGANIZATION_DOMAIN = "overte.org";
|
||||
const QString VERSION = "@BUILD_VERSION@";
|
||||
const QString RELEASE_NAME = "@RELEASE_NAME@";
|
||||
const QString BUILD_NUMBER = "@BUILD_NUMBER@";
|
||||
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
|
||||
const QString BUILD_TIME = "@BUILD_TIME@";
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Xcode generator doesn't include the compiler as the
|
||||
# first argument, Ninja and Makefiles do. Handle both cases.
|
||||
if [[ "$1" = "${CMAKE_C_COMPILER}" ]] ; then
|
||||
shift
|
||||
fi
|
||||
|
||||
export CCACHE_CPP2=true
|
||||
export CCACHE_HARDLINK=true
|
||||
export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches
|
||||
exec "${C_LAUNCHER}" "${CMAKE_C_COMPILER}" "$@"
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Xcode generator doesn't include the compiler as the
|
||||
# first argument, Ninja and Makefiles do. Handle both cases.
|
||||
if [[ "$1" = "${CMAKE_CXX_COMPILER}" ]] ; then
|
||||
shift
|
||||
fi
|
||||
|
||||
export CCACHE_CPP2=true
|
||||
export CCACHE_HARDLINK=true
|
||||
export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches
|
||||
exec "${CXX_LAUNCHER}" "${CMAKE_CXX_COMPILER}" "$@"
|
10
hifi_qt.py
10
hifi_qt.py
|
@ -157,7 +157,7 @@ endif()
|
|||
u_major = int( distro.major_version() or '0' )
|
||||
if distro.id() == 'ubuntu' or distro.id() == 'linuxmint':
|
||||
if (distro.id() == 'ubuntu' and u_major == 20) or distro.id() == 'linuxmint' and u_major == 20:
|
||||
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.10-2023.10.01-kde_d2122ee587cceb5b2f4130b7074f86db9aca570e-ubuntu-20.04-amd64.tar.xz'
|
||||
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371-ubuntu-20.04-amd64.tar.xz'
|
||||
elif (distro.id() == 'ubuntu' and u_major > 20) or (distro.id() == 'linuxmint' and u_major > 20):
|
||||
self.__no_qt_package_error()
|
||||
else:
|
||||
|
@ -170,9 +170,7 @@ endif()
|
|||
if distro.id() == 'ubuntu':
|
||||
u_major = int( distro.major_version() )
|
||||
|
||||
if u_major == 18:
|
||||
self.qtUrl = 'http://motofckr9k.ddns.net/vircadia_packages/qt5-install-5.15.2-ubuntu-18.04-aarch64_test.tar.xz'
|
||||
elif u_major == 20:
|
||||
if u_major == 20:
|
||||
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.9-2023.05.21-kde_fb3ec282151b1ee281a24f0545a40ac6438537c2-ubuntu-20.04-aarch64.tar.xz'
|
||||
elif u_major > 20:
|
||||
self.__no_qt_package_error()
|
||||
|
@ -182,9 +180,7 @@ endif()
|
|||
elif distro.id() == 'debian':
|
||||
u_major = int( distro.major_version() )
|
||||
|
||||
if u_major == 10:
|
||||
self.qtUrl = 'https://data.moto9000.moe/vircadia_packages/qt5-install-5.15.2-debian-10-aarch64.tar.xz'
|
||||
elif u_major > 10:
|
||||
if u_major > 10:
|
||||
self.__no_qt_package_error()
|
||||
else:
|
||||
self.__unsupported_error()
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
//
|
||||
// Created by Vlad Stelmahovsky on 15/5/2018.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -43,10 +42,6 @@ QString AboutUtil::getBuildVersion() const {
|
|||
return BuildInfo::VERSION;
|
||||
}
|
||||
|
||||
QString AboutUtil::getReleaseName() const {
|
||||
return BuildInfo::RELEASE_NAME;
|
||||
}
|
||||
|
||||
QString AboutUtil::getQtVersion() const {
|
||||
return qVersion();
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
* <em>Read-only.</em>
|
||||
* @property {string} buildDate - The build date of Interface that is currently running. <em>Read-only.</em>
|
||||
* @property {string} buildVersion - The build version of Interface that is currently running. <em>Read-only.</em>
|
||||
* @property {string} releaseName - The release codename of the version that Interface is currently running. <em>Read-only.</em>
|
||||
* @property {string} qtVersion - The Qt version used in Interface that is currently running. <em>Read-only.</em>
|
||||
* @property {string} qtWebEngineVersion - The Qt WebEngine version used in Interface that is currently running. <em>Read-only.</em>
|
||||
* @property {string} qtChromiumVersion - The Qt Chromium version used in Interface that is currently running. <em>Read-only.</em>
|
||||
|
@ -40,7 +39,6 @@
|
|||
* print("Interface platform: " + About.platform);
|
||||
* print("Interface build date: " + About.buildDate);
|
||||
* print("Interface version: " + About.buildVersion);
|
||||
* print("Interface release name: " + About.releaseName);
|
||||
* print("Qt version: " + About.qtVersion);
|
||||
*/
|
||||
|
||||
|
@ -71,7 +69,6 @@ class AboutUtil : public QObject {
|
|||
Q_PROPERTY(QString platform READ getPlatformName CONSTANT)
|
||||
Q_PROPERTY(QString buildDate READ getBuildDate CONSTANT)
|
||||
Q_PROPERTY(QString buildVersion READ getBuildVersion CONSTANT)
|
||||
Q_PROPERTY(QString releaseName READ getReleaseName CONSTANT)
|
||||
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
|
||||
Q_PROPERTY(QString qtWebEngineVersion READ getQtWebEngineVersion CONSTANT)
|
||||
Q_PROPERTY(QString qtChromiumVersion READ getQtChromiumVersion CONSTANT)
|
||||
|
@ -83,7 +80,6 @@ public:
|
|||
QString getPlatformName() const { return "Overte"; }
|
||||
QString getBuildDate() const;
|
||||
QString getBuildVersion() const;
|
||||
QString getReleaseName() const;
|
||||
QString getQtVersion() const;
|
||||
QString getQtWebEngineVersion() const;
|
||||
QString getQtChromiumVersion() const;
|
||||
|
|
|
@ -2480,7 +2480,7 @@ void Application::initialize(const QCommandLineParser &parser) {
|
|||
|
||||
// Setup the mouse ray pick and related operators
|
||||
{
|
||||
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::getPickEntities() | PickScriptingInterface::getPickLocalEntities()), 0.0f, true);
|
||||
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::getPickEntities() | PickScriptingInterface::getPickLocalEntities()), 0.0f, 0.0f, true);
|
||||
mouseRayPick->parentTransform = std::make_shared<MouseTransformNode>();
|
||||
mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
|
||||
auto mouseRayPickID = DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, mouseRayPick);
|
||||
|
@ -7251,10 +7251,6 @@ void Application::updateWindowTitle() const {
|
|||
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
||||
+ " " + applicationVersion();
|
||||
|
||||
if (BuildInfo::RELEASE_NAME != "") {
|
||||
buildVersion += " - " + BuildInfo::RELEASE_NAME;
|
||||
}
|
||||
|
||||
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
|
||||
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
|
||||
|
@ -7657,7 +7653,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint
|
|||
|
||||
{
|
||||
auto connection = std::make_shared<QMetaObject::Connection>();
|
||||
*connection = scriptManager->connect(scriptManager.get(), &ScriptManager::scriptEnding, [scriptManager, connection]() {
|
||||
*connection = scriptManager->connect(scriptManager.get(), &ScriptManager::scriptEnding, [this, scriptManager, connection]() {
|
||||
// Request removal of controller routes with callbacks to a given script engine
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
// scheduleScriptEndpointCleanup will have the last instance of shared pointer to script manager
|
||||
|
@ -8598,6 +8594,14 @@ SharedSoundPointer Application::getSampleSound() const {
|
|||
return _sampleSound;
|
||||
}
|
||||
|
||||
void Application::showVRKeyboardForHudUI(bool show) {
|
||||
if (show) {
|
||||
DependencyManager::get<Keyboard>()->setRaised(true, true);
|
||||
} else {
|
||||
DependencyManager::get<Keyboard>()->setRaised(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::loadLODToolsDialog() {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet(SYSTEM_TABLET));
|
||||
|
|
|
@ -431,6 +431,16 @@ public slots:
|
|||
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
|
||||
Q_INVOKABLE void loadAvatarBrowser() const;
|
||||
Q_INVOKABLE SharedSoundPointer getSampleSound() const;
|
||||
/**
|
||||
* @brief Shows/hides VR keyboard input for Overlay windows
|
||||
*
|
||||
* This is used by QML scripts to show and hide VR keyboard. Unlike JS API Keyboard.raised = true,
|
||||
* with showVRKeyboardForHudUI the input is passed to the active window on the overlay first.
|
||||
*
|
||||
* @param show
|
||||
* If set to true, then keyboard is shown, for false it's hidden.
|
||||
*/
|
||||
Q_INVOKABLE void showVRKeyboardForHudUI(bool show);
|
||||
|
||||
void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const;
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ void LODManager::setRenderTimes(float presentTime, float engineRunTime, float ba
|
|||
}
|
||||
|
||||
void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||
std::lock_guard<std::mutex> lock{ _automaticLODLock };
|
||||
std::lock_guard<std::mutex> lock(_automaticLODLock);
|
||||
|
||||
// The "render time" is the worse of:
|
||||
// - engineRunTime: Time spent in the render thread in the engine producing the gpu::Frame N
|
||||
|
@ -300,7 +300,7 @@ void LODManager::resetLODAdjust() {
|
|||
}
|
||||
|
||||
void LODManager::setAutomaticLODAdjust(bool value) {
|
||||
std::lock_guard<std::mutex> lock{ _automaticLODLock };
|
||||
std::lock_guard<std::mutex> lock(_automaticLODLock);
|
||||
_automaticLODAdjust = value;
|
||||
saveSettings();
|
||||
emit autoLODChanged();
|
||||
|
|
|
@ -5768,8 +5768,7 @@ void MyAvatar::FollowHelper::deactivate() {
|
|||
}
|
||||
|
||||
void MyAvatar::FollowHelper::deactivate(CharacterController::FollowType type) {
|
||||
int int_type = static_cast<int>(type);
|
||||
assert(int_type >= 0 && int_type < static_cast<int>(CharacterController::FollowType::Count));
|
||||
assert(type < CharacterController::FollowType::Count);
|
||||
_timeRemaining[(int)type] = 0.0f;
|
||||
}
|
||||
|
||||
|
@ -5777,16 +5776,14 @@ void MyAvatar::FollowHelper::deactivate(CharacterController::FollowType type) {
|
|||
// eg. activate(FollowType::Rotation, true) snaps the FollowHelper's rotation immediately
|
||||
// to the rotation of its _followDesiredBodyTransform.
|
||||
void MyAvatar::FollowHelper::activate(CharacterController::FollowType type, const bool snapFollow) {
|
||||
int int_type = static_cast<int>(type);
|
||||
assert(int_type >= 0 && int_type < static_cast<int>(CharacterController::FollowType::Count));
|
||||
assert(type < CharacterController::FollowType::Count);
|
||||
|
||||
// TODO: Perhaps, the follow time should be proportional to the displacement.
|
||||
_timeRemaining[(int)type] = snapFollow ? CharacterController::FOLLOW_TIME_IMMEDIATE_SNAP : FOLLOW_TIME;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::isActive(CharacterController::FollowType type) const {
|
||||
int int_type = static_cast<int>(type);
|
||||
assert(int_type >= 0 && int_type < static_cast<int>(CharacterController::FollowType::Count));
|
||||
assert(type < CharacterController::FollowType::Count);
|
||||
return _timeRemaining[(int)type] > 0.0f;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,19 @@ PickQuery::PickType LaserPointer::getType() const {
|
|||
}
|
||||
|
||||
void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) {
|
||||
//V8TODO pathProps are not a thing anymore
|
||||
auto renderState = std::static_pointer_cast<RenderState>(_renderStates[state]);
|
||||
if (renderState) {
|
||||
updateRenderState(renderState->getPathID(), pathProps);
|
||||
QVariant lineWidth = pathProps.toMap()["lineWidth"];
|
||||
QVariantMap pathPropsMap = pathProps.toMap();
|
||||
QVariant lineWidth = pathPropsMap["lineWidth"];
|
||||
if (lineWidth.isValid()) {
|
||||
renderState->setLineWidth(lineWidth.toFloat());
|
||||
}
|
||||
|
||||
if (pathPropsMap.contains("linePoints")) {
|
||||
QVariantList linePoints = pathPropsMap["linePoints"].toList();
|
||||
renderState->setNumPoints(linePoints.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,18 +138,18 @@ LaserPointer::RenderState::RenderState(const QUuid& startID, const QUuid& pathID
|
|||
StartEndRenderState(startID, endID), _pathID(pathID)
|
||||
{
|
||||
if (!getPathID().isNull()) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
{
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_IGNORE_PICK_INTERSECTION;
|
||||
_pathIgnorePicks = entityScriptingInterface->getEntityPropertiesInternal(getPathID(), desiredProperties, false).getIgnorePickIntersection();
|
||||
}
|
||||
{
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_STROKE_WIDTHS;
|
||||
auto widths = entityScriptingInterface->getEntityPropertiesInternal(getPathID(), desiredProperties, false).getStrokeWidths();
|
||||
_lineWidth = widths.length() == 0 ? PolyLineEntityItem::DEFAULT_LINE_WIDTH : widths[0];
|
||||
}
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_IGNORE_PICK_INTERSECTION;
|
||||
desiredProperties += PROP_LINE_POINTS;
|
||||
desiredProperties += PROP_STROKE_WIDTHS;
|
||||
auto properties = DependencyManager::get<EntityScriptingInterface>()->getEntityPropertiesInternal(getPathID(), desiredProperties, false);
|
||||
|
||||
auto widths = properties.getStrokeWidths();
|
||||
_lineWidth = widths.length() == 0 ? PolyLineEntityItem::DEFAULT_LINE_WIDTH : widths[0];
|
||||
|
||||
setNumPoints(properties.getLinePoints().length());
|
||||
|
||||
_pathIgnorePicks = properties.getIgnorePickIntersection();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,28 +176,52 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3&
|
|||
if (!getPathID().isNull()) {
|
||||
EntityItemProperties properties;
|
||||
QVector<glm::vec3> points;
|
||||
const size_t numPoints = getNumPoints();
|
||||
points.append(glm::vec3(0.0f));
|
||||
points.append(end - origin);
|
||||
const glm::vec3 endPoint = end - origin;
|
||||
if (numPoints > 2) {
|
||||
EntityPropertyFlags desiredProperties;
|
||||
desiredProperties += PROP_VISIBLE;
|
||||
auto oldProperties = DependencyManager::get<EntityScriptingInterface>()->getEntityPropertiesInternal(getPathID(), desiredProperties, false);
|
||||
|
||||
bool hasUnmodifiedEndPoint = false;
|
||||
glm::vec3 unmodifiedEndPoint;
|
||||
float directionLength = glm::length(endPoint);
|
||||
auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);
|
||||
if (rayPickResult && rayPickResult->pickVariant.contains("unmodifiedDirection")) {
|
||||
unmodifiedEndPoint = directionLength * vec3FromVariant(rayPickResult->pickVariant["unmodifiedDirection"]);
|
||||
hasUnmodifiedEndPoint = true;
|
||||
}
|
||||
|
||||
// Segment points are evenly spaced between origin and end
|
||||
for (size_t i = 1; i < numPoints - 1; i++) {
|
||||
const float frac = ((float)i / (numPoints - 1));
|
||||
if (!oldProperties.getVisible() || !_hasSetLinePoints || !hasUnmodifiedEndPoint) {
|
||||
points.append(frac * endPoint);
|
||||
} else {
|
||||
points.append(frac * directionLength * glm::normalize(glm::mix(unmodifiedEndPoint, endPoint, frac)));
|
||||
}
|
||||
}
|
||||
_hasSetLinePoints = true;
|
||||
}
|
||||
points.append(endPoint);
|
||||
properties.setPosition(origin);
|
||||
properties.setRotation(glm::quat(1.0f, 0.0f ,0.0f ,0.0f));
|
||||
properties.setRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
properties.setLinePoints(points);
|
||||
properties.setVisible(true);
|
||||
properties.setIgnorePickIntersection(doesPathIgnorePicks());
|
||||
QVector<glm::vec3> normals;
|
||||
normals.append(glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
normals.append(glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
normals.fill(glm::vec3(0.0f, 0.0f, 1.0f), (int)numPoints);
|
||||
properties.setNormals(normals);
|
||||
QVector<float> widths;
|
||||
float width = getLineWidth() * parentScale;
|
||||
widths.append(width);
|
||||
widths.append(width);
|
||||
widths.fill(width, (int)numPoints);
|
||||
properties.setStrokeWidths(widths);
|
||||
DependencyManager::get<EntityScriptingInterface>()->editEntity(getPathID(), properties);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<StartEndRenderState> LaserPointer::buildRenderState(const QVariantMap& propMap, const QList<EntityItemProperties> &entityProperties) {
|
||||
// FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers
|
||||
QUuid startID;
|
||||
if (propMap["startPropertyIndex"].isValid()) {
|
||||
int startPropertyIndex = propMap["startPropertyIndex"].toInt();
|
||||
|
@ -205,11 +234,11 @@ std::shared_ptr<StartEndRenderState> LaserPointer::buildRenderState(const QVaria
|
|||
|
||||
QUuid pathID;
|
||||
if (propMap["pathPropertyIndex"].isValid()) {
|
||||
// laser paths must be PolyLine
|
||||
int pathPropertyIndex = propMap["pathPropertyIndex"].toInt();
|
||||
if (pathPropertyIndex >= 0 && pathPropertyIndex < entityProperties.length()) {
|
||||
//pathMap["type"].toString() == "PolyLine"
|
||||
EntityItemProperties pathProperties(entityProperties[pathPropertyIndex]);
|
||||
// laser paths must be PolyLine
|
||||
pathProperties.setType(EntityTypes::EntityType::PolyLine);
|
||||
pathProperties.getGrab().setGrabbable(false);
|
||||
pathID = DependencyManager::get<EntityScriptingInterface>()->addEntityInternal(pathProperties, entity::HostType::LOCAL);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include "PathPointer.h"
|
||||
|
||||
#include<EntityItemProperties.h>
|
||||
#include <EntityItemProperties.h>
|
||||
|
||||
class LaserPointer : public PathPointer {
|
||||
using Parent = PathPointer;
|
||||
|
@ -24,21 +24,29 @@ public:
|
|||
RenderState(const QUuid& startID, const QUuid& pathID, const QUuid& endID);
|
||||
|
||||
const QUuid& getPathID() const { return _pathID; }
|
||||
const bool& doesPathIgnorePicks() const { return _pathIgnorePicks; }
|
||||
|
||||
void setLineWidth(float width) { _lineWidth = width; }
|
||||
float getLineWidth() const { return _lineWidth; }
|
||||
|
||||
void setNumPoints(size_t numPoints) { _numPoints = std::max(numPoints, (size_t)2); }
|
||||
size_t getNumPoints() const { return _numPoints; }
|
||||
|
||||
const bool& doesPathIgnorePicks() const { return _pathIgnorePicks; }
|
||||
|
||||
void cleanup() override;
|
||||
void disable() override;
|
||||
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
|
||||
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override;
|
||||
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd,
|
||||
bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance,
|
||||
const PickResultPointer& pickResult) override;
|
||||
|
||||
private:
|
||||
QUuid _pathID;
|
||||
|
||||
bool _pathIgnorePicks;
|
||||
float _lineWidth;
|
||||
float _lineWidth { 0.0f };
|
||||
size_t _numPoints { 0 };
|
||||
bool _pathIgnorePicks { false };
|
||||
|
||||
bool _hasSetLinePoints { false };
|
||||
};
|
||||
|
||||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
|
||||
|
@ -67,7 +75,6 @@ protected:
|
|||
|
||||
private:
|
||||
static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LaserPointer_h
|
||||
|
|
|
@ -207,9 +207,11 @@ void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float al
|
|||
}
|
||||
}
|
||||
|
||||
void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
|
||||
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
|
||||
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
|
||||
void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd,
|
||||
bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance,
|
||||
const PickResultPointer& pickResult) {
|
||||
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength,
|
||||
distance, pickResult);
|
||||
auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult);
|
||||
if (parabolaPickResult && render::Item::isValidID(_pathID)) {
|
||||
render::Transaction transaction;
|
||||
|
|
|
@ -144,13 +144,11 @@ void PathPointer::updateVisuals(const PickResultPointer& pickResult) {
|
|||
auto renderState = _renderStates.find(_currentRenderState);
|
||||
auto defaultRenderState = _defaultRenderStates.find(_currentRenderState);
|
||||
float parentScale = 1.0f;
|
||||
//if (_scaleWithParent) {
|
||||
if (_enabled && _scaleWithParent) {
|
||||
glm::vec3 dimensions = DependencyManager::get<PickManager>()->getParentTransform(_pickUID).getScale();
|
||||
parentScale = glm::max(glm::max(dimensions.x, dimensions.y), dimensions.z);
|
||||
}
|
||||
|
||||
//if (!_currentRenderState.empty() && renderState != _renderStates.end() &&
|
||||
if (_enabled && !_currentRenderState.empty() && renderState != _renderStates.end() &&
|
||||
(type != IntersectionType::NONE || _pathLength > 0.0f)) {
|
||||
glm::vec3 origin = getPickOrigin(pickResult);
|
||||
|
@ -162,14 +160,14 @@ void PathPointer::updateVisuals(const PickResultPointer& pickResult) {
|
|||
defaultRenderState->second.second->disable();
|
||||
}
|
||||
} else if (_enabled && !_currentRenderState.empty() && defaultRenderState != _defaultRenderStates.end()) {
|
||||
//} else if (!_currentRenderState.empty() && defaultRenderState != _defaultRenderStates.end()) {
|
||||
if (renderState != _renderStates.end() && renderState->second->isEnabled()) {
|
||||
renderState->second->disable();
|
||||
}
|
||||
glm::vec3 origin = getPickOrigin(pickResult);
|
||||
glm::vec3 end = getPickEnd(pickResult, defaultRenderState->second.first);
|
||||
defaultRenderState->second.second->update(origin, end, Vectors::UP, parentScale, _distanceScaleEnd, _centerEndY,
|
||||
_faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first, pickResult);
|
||||
_faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first,
|
||||
pickResult);
|
||||
} else if (!_currentRenderState.empty()) {
|
||||
if (renderState != _renderStates.end() && renderState->second->isEnabled()) {
|
||||
renderState->second->disable();
|
||||
|
@ -205,12 +203,12 @@ void PathPointer::editRenderState(const std::string& state, const QVariant& star
|
|||
}
|
||||
|
||||
void PathPointer::updateRenderState(const QUuid& id, const QVariant& props) {
|
||||
// FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers
|
||||
if (!id.isNull() && props.isValid()) {
|
||||
QVariantMap propMap = props.toMap();
|
||||
propMap.remove("visible");
|
||||
qApp->getOverlays().editOverlay(id, propMap);
|
||||
}
|
||||
// FIXME: updating render state props no longer works since removing 3D Overlays
|
||||
//if (!id.isNull() && props.isValid()) {
|
||||
// QVariantMap propMap = props.toMap();
|
||||
// propMap.remove("visible");
|
||||
// qApp->getOverlays().editOverlay(id, propMap);
|
||||
//}
|
||||
}
|
||||
|
||||
Pointer::PickedObject PathPointer::getHoveredObject(const PickResultPointer& pickResult) {
|
||||
|
@ -300,8 +298,9 @@ void StartEndRenderState::disable() {
|
|||
_enabled = false;
|
||||
}
|
||||
|
||||
void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
|
||||
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
|
||||
void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd,
|
||||
bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance,
|
||||
const PickResultPointer& pickResult) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
if (!getStartID().isNull()) {
|
||||
EntityItemProperties properties;
|
||||
|
|
|
@ -43,16 +43,17 @@ public:
|
|||
|
||||
virtual void cleanup();
|
||||
virtual void disable();
|
||||
virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
|
||||
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult);
|
||||
virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd,
|
||||
bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance,
|
||||
const PickResultPointer& pickResult);
|
||||
|
||||
bool isEnabled() const { return _enabled; }
|
||||
|
||||
protected:
|
||||
QUuid _startID;
|
||||
QUuid _endID;
|
||||
bool _startIgnorePicks;
|
||||
bool _endIgnorePicks;
|
||||
bool _startIgnorePicks { false };
|
||||
bool _endIgnorePicks { false };
|
||||
|
||||
glm::vec3 _startDim;
|
||||
glm::vec3 _endDim;
|
||||
|
|
|
@ -121,6 +121,7 @@ PickFilter getPickFilter(unsigned int filter) {
|
|||
* @property {Vec3} [dirOffset] - Synonym for <code>direction</code>.
|
||||
* @property {Quat} [orientation] - Alternative property for specifying <code>direction</code>. The value is applied to the
|
||||
* default <code>direction</code> value.
|
||||
* @property {number} [delay=0.0] - The delay, in seconds, to apply to the ray direction.
|
||||
* @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or
|
||||
* {@link Picks.getPickScriptParameters}. A ray pick's type is {@link PickType.Ray}.
|
||||
* @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale
|
||||
|
@ -173,7 +174,14 @@ std::shared_ptr<PickQuery> PickScriptingInterface::buildRayPick(const QVariantMa
|
|||
direction = vec3FromVariant(propMap["dirOffset"]);
|
||||
}
|
||||
|
||||
auto rayPick = std::make_shared<RayPick>(position, direction, filter, maxDistance, enabled);
|
||||
float delayHalf = 0.0f;
|
||||
if (propMap["delay"].isValid()) {
|
||||
// We want to be within 0.1% of the target in <delay> seconds
|
||||
// https://twitter.com/FreyaHolmer/status/1757836988495847568
|
||||
delayHalf = -std::max(propMap["delay"].toFloat(), 0.0f) / log2(0.001);
|
||||
}
|
||||
|
||||
auto rayPick = std::make_shared<RayPick>(position, direction, filter, maxDistance, delayHalf, enabled);
|
||||
setParentTransform(rayPick, propMap);
|
||||
|
||||
return rayPick;
|
||||
|
@ -455,6 +463,10 @@ void PickScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValue
|
|||
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
void PickScriptingInterface::setDelay(unsigned int uid, float delay) {
|
||||
DependencyManager::get<PickManager>()->setDelay(uid, delay);
|
||||
}
|
||||
|
||||
bool PickScriptingInterface::isLeftHand(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isLeftHand(uid);
|
||||
}
|
||||
|
@ -497,6 +509,15 @@ void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
|
|||
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
|
||||
}
|
||||
|
||||
float PickScriptingInterface::getHandLaserDelay() const {
|
||||
return _handLaserDelaySetting.get();
|
||||
}
|
||||
|
||||
void PickScriptingInterface::setHandLaserDelay(float delay) {
|
||||
_handLaserDelaySetting.set(delay);
|
||||
emit handLaserDelayChanged(delay);
|
||||
}
|
||||
|
||||
void PickScriptingInterface::setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap) {
|
||||
QUuid parentUuid;
|
||||
int parentJointIndex = 0;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <PhysicsEngine.h>
|
||||
#include <Pick.h>
|
||||
#include <PickFilter.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
class ScriptEngine;
|
||||
class ScriptValue;
|
||||
|
@ -72,6 +73,7 @@ class ScriptValue;
|
|||
* @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD surface. <em>Read-only.</em>
|
||||
*
|
||||
* @property {number} perFrameTimeBudget - The maximum time, in microseconds, to spend per frame updating pick results.
|
||||
* @property {number} handLaserDelay - The delay, in seconds, applied to the hand lasers to smooth their movement.
|
||||
*/
|
||||
|
||||
class PickScriptingInterface : public QObject, public Dependency {
|
||||
|
@ -105,6 +107,7 @@ class PickScriptingInterface : public QObject, public Dependency {
|
|||
Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ getIntersectedAvatar CONSTANT)
|
||||
Q_PROPERTY(unsigned int INTERSECTED_HUD READ getIntersectedHud CONSTANT)
|
||||
Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget)
|
||||
Q_PROPERTY(float handLaserDelay READ getHandLaserDelay WRITE setHandLaserDelay)
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
@ -263,6 +266,15 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValue& includeItems);
|
||||
|
||||
/*@jsdoc
|
||||
* Sets the delay of a Ray pick.
|
||||
* <p><strong>Note:</strong> Not used by other pick types.</p>
|
||||
* @function Picks.setDelay
|
||||
* @param {number} id - The ID of the pointer.
|
||||
* @param {number} delay - The desired delay in seconds.
|
||||
*/
|
||||
Q_INVOKABLE void setDelay(unsigned int uid, float delay);
|
||||
|
||||
/*@jsdoc
|
||||
* Checks if a pick is associated with the left hand: a ray or parabola pick with <code>joint</code> property set to
|
||||
* <code>"_CONTROLLER_LEFTHAND"</code> or <code>"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"</code>, or a stylus pick with
|
||||
|
@ -295,6 +307,9 @@ public:
|
|||
unsigned int getPerFrameTimeBudget() const;
|
||||
void setPerFrameTimeBudget(unsigned int numUsecs);
|
||||
|
||||
float getHandLaserDelay() const;
|
||||
void setHandLaserDelay(float delay);
|
||||
|
||||
public slots:
|
||||
|
||||
static constexpr unsigned int getPickBypassIgnore() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_BYPASS_IGNORE); }
|
||||
|
@ -461,6 +476,9 @@ public slots:
|
|||
*/
|
||||
static constexpr unsigned int getIntersectedHud() { return IntersectionType::HUD; }
|
||||
|
||||
signals:
|
||||
void handLaserDelayChanged(float delay);
|
||||
|
||||
protected:
|
||||
static std::shared_ptr<PickQuery> buildRayPick(const QVariantMap& properties);
|
||||
static std::shared_ptr<PickQuery> buildStylusPick(const QVariantMap& properties);
|
||||
|
@ -468,6 +486,9 @@ protected:
|
|||
static std::shared_ptr<PickQuery> buildParabolaPick(const QVariantMap& properties);
|
||||
|
||||
static void setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap);
|
||||
|
||||
private:
|
||||
Setting::Handle<float> _handLaserDelaySetting { "handLaserDelay", 0.35f };
|
||||
};
|
||||
|
||||
#endif // hifi_PickScriptingInterface_h
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <ScriptManager.h>
|
||||
|
||||
STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager){
|
||||
qDebug() << "STATIC_SCRIPT_TYPES_INITIALIZER PointerScriptingInterface";
|
||||
auto scriptEngine = manager->engine().get();
|
||||
scriptRegisterMetaType<RayPointerProperties, rayPointerPropertiesToScriptValue, rayPointerPropertiesFromScriptValue>(scriptEngine);
|
||||
scriptRegisterMetaType<StylusPointerProperties, stylusPointerPropertiesToScriptValue, stylusPointerPropertiesFromScriptValue>(scriptEngine);
|
||||
|
@ -92,7 +91,6 @@ unsigned int PointerScriptingInterface::createParabolaPointer(ParabolaPointerPro
|
|||
return createPointerInternal(PickQuery::PickType::Parabola, properties);
|
||||
}
|
||||
|
||||
|
||||
bool PointerScriptingInterface::isPointerEnabled(unsigned int uid) const {
|
||||
return DependencyManager::get<PointerManager>()->isPointerEnabled(uid);
|
||||
}
|
||||
|
@ -178,45 +176,45 @@ std::shared_ptr<Pointer> PointerScriptingInterface::buildStylus(const PointerPro
|
|||
* Properties that define the visual appearance of a ray pointer when the pointer is intersecting something.
|
||||
* @typedef {object} Pointers.RayPointerRenderState
|
||||
* @property {string} name - When creating using {@link Pointers.createPointer}, the name of the render state.
|
||||
* @property {Overlays.OverlayProperties|Uuid} [start]
|
||||
* @property {Entities.EntityProperties|Uuid} [start]
|
||||
* <p>When creating or editing using {@link Pointers.createPointer} or {@link Pointers.editRenderState}, the properties of
|
||||
* an overlay to render at the start of the ray pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the overlay rendered at the start of the ray;
|
||||
* <code>null</code> if there is no overlay.
|
||||
* an entity to render at the start of the ray pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the entity rendered at the start of the ray;
|
||||
* <code>null</code> if there is no entity.
|
||||
*
|
||||
* @property {Overlays.OverlayProperties|Uuid} [path]
|
||||
* @property {Entities.EntityProperties|Uuid} [path]
|
||||
* <p>When creating or editing using {@link Pointers.createPointer} or {@link Pointers.editRenderState}, the properties of
|
||||
* the overlay rendered for the path of the ray pointer. The <code>type</code> property must be specified and be
|
||||
* <code>"line3d"</code>.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the overlay rendered for the path of the ray;
|
||||
* <code>null</code> if there is no overlay.
|
||||
* the entity rendered for the path of the ray pointer. The <code>type</code> property must be specified and be
|
||||
* <code>"PolyLine"</code>.</p> Specify <code>linePoints</code> to control how many segments make up the path.
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the entity rendered for the path of the ray;
|
||||
* <code>null</code> if there is no entity.
|
||||
*
|
||||
* @property {Overlays.OverlayProperties|Uuid} [end]
|
||||
* @property {Entities.EntityProperties|Uuid} [end]
|
||||
* <p>When creating or editing using {@link Pointers.createPointer} or {@link Pointers.editRenderState}, the properties of
|
||||
* an overlay to render at the end of the ray pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the overlay rendered at the end of the ray;
|
||||
* <code>null</code> if there is no overlay.
|
||||
* an entity to render at the end of the ray pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the entity rendered at the end of the ray;
|
||||
* <code>null</code> if there is no entity.
|
||||
*/
|
||||
/*@jsdoc
|
||||
* The properties of a ray pointer. These include the properties from the underlying ray pick that the pointer uses.
|
||||
* @typedef {object} Pointers.RayPointerProperties
|
||||
* @property {boolean} [faceAvatar=false] - <code>true</code> if the overlay rendered at the end of the ray rotates about the
|
||||
* @property {boolean} [faceAvatar=false] - <code>true</code> if the entity rendered at the end of the ray rotates about the
|
||||
* world y-axis to always face the avatar; <code>false</code> if it maintains its world orientation.
|
||||
* @property {boolean} [centerEndY=true] - <code>true</code> if the overlay rendered at the end of the ray is centered on
|
||||
* the ray end; <code>false</code> if the overlay is positioned against the surface if <code>followNormal</code> is
|
||||
* @property {boolean} [centerEndY=true] - <code>true</code> if the entity rendered at the end of the ray is centered on
|
||||
* the ray end; <code>false</code> if the entity is positioned against the surface if <code>followNormal</code> is
|
||||
* <code>true</code>, or above the ray end if <code>followNormal</code> is <code>false</code>.
|
||||
* @property {boolean} [lockEnd=false] - <code>true</code> if the end of the ray is locked to the center of the object at
|
||||
* which the ray is pointing; <code>false</code> if the end of the ray is at the intersected surface.
|
||||
* @property {boolean} [distanceScaleEnd=false] - <code>true</code> if the dimensions of the overlay at the end of the ray
|
||||
* @property {boolean} [distanceScaleEnd=false] - <code>true</code> if the dimensions of the entity at the end of the ray
|
||||
* scale linearly with distance; <code>false</code> if they aren't.
|
||||
* @property {boolean} [scaleWithParent=false] - <code>true</code> if the width of the ray's path and the size of the
|
||||
* start and end overlays scale linearly with the pointer parent's scale; <code>false</code> if they don't scale.
|
||||
* start and end entities scale linearly with the pointer parent's scale; <code>false</code> if they don't scale.
|
||||
* @property {boolean} [scaleWithAvatar=false] - A synonym for <code>scalewithParent</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} [followNormal=false] - <code>true</code> if the overlay rendered at the end of the ray rotates to
|
||||
* @property {boolean} [followNormal=false] - <code>true</code> if the entity rendered at the end of the ray rotates to
|
||||
* follow the normal of the surface if one is intersected; <code>false</code> if it doesn't.
|
||||
* @property {number} [followNormalStrength=0.0] - How quickly the overlay rendered at the end of the ray rotates to follow
|
||||
* the normal of an intersected surface. If <code>0</code> or <code>1</code>, the overlay rotation follows instantaneously;
|
||||
* @property {number} [followNormalStrength=0.0] - How quickly the entity rendered at the end of the ray rotates to follow
|
||||
* the normal of an intersected surface. If <code>0</code> or <code>1</code>, the entity rotation follows instantaneously;
|
||||
* for other values, the larger the value the more quickly the rotation follows.
|
||||
* @property {Pointers.RayPointerRenderState[]|Object.<string, Pointers.RayPointerRenderState>} [renderStates]
|
||||
* <p>A set of visual states that can be switched among using {@link Pointers.setRenderState}. These define the visual
|
||||
|
@ -236,7 +234,7 @@ std::shared_ptr<Pointer> PointerScriptingInterface::buildStylus(const PointerPro
|
|||
* @property {boolean} [hover=false] - <code>true</code> if the pointer generates {@link Entities} hover events,
|
||||
* <code>false</code> if it doesn't.
|
||||
* @property {Pointers.Trigger[]} [triggers=[]] - A list of ways that a {@link Controller} action or function should trigger
|
||||
* events on the entity or overlay currently intersected.
|
||||
* events on the object currently intersected.
|
||||
* @property {PickType} pointerType - The type of pointer returned from {@link Pointers.getPointerProperties} or
|
||||
* {@link Pointers.getPointerScriptParameters}. A laser pointer's type is {@link PickType(0)|PickType.Ray}.
|
||||
* @property {number} [pickID] - The ID of the pick created alongside this pointer, returned from
|
||||
|
@ -378,41 +376,41 @@ std::shared_ptr<Pointer> PointerScriptingInterface::buildLaserPointer(const Poin
|
|||
* Properties that define the visual appearance of a parabola pointer when the pointer is intersecting something.
|
||||
* @typedef {object} Pointers.ParabolaPointerRenderState
|
||||
* @property {string} name - When creating using {@link Pointers.createPointer}, the name of the render state.
|
||||
* @property {Overlays.OverlayProperties|Uuid} [start]
|
||||
* @property {Entities.EntityProperties|Uuid} [start]
|
||||
* <p>When creating or editing using {@link Pointers.createPointer} or {@link Pointers.editRenderState}, the properties of
|
||||
* an overlay to render at the start of the parabola pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the overlay rendered at the start of the
|
||||
* parabola; <code>null</code> if there is no overlay.
|
||||
* an entity to render at the start of the parabola pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the entity rendered at the start of the
|
||||
* parabola; <code>null</code> if there is no entity.
|
||||
* @property {Pointers.ParabolaPointerPath|Uuid} [path]
|
||||
* <p>When creating or editing using {@link Pointers.createPointer} or {@link Pointers.editRenderState}, the properties of
|
||||
* the rendered path of the parabola pointer.</p>
|
||||
* <p>This property is not provided when getting using {@link Pointers.getPointerProperties}.
|
||||
* @property {Overlays.OverlayProperties|Uuid} [end]
|
||||
* @property {Entities.EntityProperties|Uuid} [end]
|
||||
* <p>When creating or editing using {@link Pointers.createPointer} or {@link Pointers.editRenderState}, the properties of
|
||||
* an overlay to render at the end of the ray pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the overlay rendered at the end of the parabola;
|
||||
* <code>null</code> if there is no overlay.
|
||||
* an entity to render at the end of the ray pointer. The <code>type</code> property must be specified.</p>
|
||||
* <p>When getting using {@link Pointers.getPointerProperties}, the ID of the entity rendered at the end of the parabola;
|
||||
* <code>null</code> if there is no entity.
|
||||
*/
|
||||
/*@jsdoc
|
||||
* The properties of a parabola pointer. These include the properties from the underlying parabola pick that the pointer uses.
|
||||
* @typedef {object} Pointers.ParabolaPointerProperties
|
||||
* @property {boolean} [faceAvatar=false] - <code>true</code> if the overlay rendered at the end of the ray rotates about the
|
||||
* @property {boolean} [faceAvatar=false] - <code>true</code> if the entity rendered at the end of the ray rotates about the
|
||||
* world y-axis to always face the avatar; <code>false</code> if it maintains its world orientation.
|
||||
* @property {boolean} [centerEndY=true] - <code>true</code> if the overlay rendered at the end of the ray is centered on
|
||||
* the ray end; <code>false</code> if the overlay is positioned against the surface if <code>followNormal</code> is
|
||||
* @property {boolean} [centerEndY=true] - <code>true</code> if the entity rendered at the end of the ray is centered on
|
||||
* the ray end; <code>false</code> if the entity is positioned against the surface if <code>followNormal</code> is
|
||||
* <code>true</code>, or above the ray end if <code>followNormal</code> is <code>false</code>.
|
||||
* @property {boolean} [lockEnd=false] - <code>true</code> if the end of the ray is locked to the center of the object at
|
||||
* which the ray is pointing; <code>false</code> if the end of the ray is at the intersected surface.
|
||||
* @property {boolean} [distanceScaleEnd=false] - <code>true</code> if the dimensions of the overlay at the end of the ray
|
||||
* @property {boolean} [distanceScaleEnd=false] - <code>true</code> if the dimensions of the entity at the end of the ray
|
||||
* scale linearly with distance; <code>false</code> if they aren't.
|
||||
* @property {boolean} [scaleWithParent=false] - <code>true</code> if the width of the ray's path and the size of the
|
||||
* start and end overlays scale linearly with the pointer parent's scale; <code>false</code> if they don't scale.
|
||||
* start and end entities scale linearly with the pointer parent's scale; <code>false</code> if they don't scale.
|
||||
* @property {boolean} [scaleWithAvatar=false] - A synonym for <code>scalewithParent</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} [followNormal=false] - <code>true</code> if the overlay rendered at the end of the ray rotates to
|
||||
* @property {boolean} [followNormal=false] - <code>true</code> if the entity rendered at the end of the ray rotates to
|
||||
* follow the normal of the surface if one is intersected; <code>false</code> if it doesn't.
|
||||
* @property {number} [followNormalStrength=0.0] - How quickly the overlay rendered at the end of the ray rotates to follow
|
||||
* the normal of an intersected surface. If <code>0</code> or <code>1</code>, the overlay rotation follows instantaneously;
|
||||
* @property {number} [followNormalStrength=0.0] - How quickly the entity rendered at the end of the ray rotates to follow
|
||||
* the normal of an intersected surface. If <code>0</code> or <code>1</code>, the entity rotation follows instantaneously;
|
||||
* for other values, the larger the value the more quickly the rotation follows.
|
||||
* @property {Pointers.ParabolaPointerRenderState[]|Object.<string, Pointers.ParabolaPointerRenderState>} [renderStates]
|
||||
* <p>A set of visual states that can be switched among using {@link Pointers.setRenderState}. These define the visual
|
||||
|
@ -432,7 +430,7 @@ std::shared_ptr<Pointer> PointerScriptingInterface::buildLaserPointer(const Poin
|
|||
* @property {boolean} [hover=false] - <code>true</code> if the pointer generates {@link Entities} hover events,
|
||||
* <code>false</code> if it doesn't.
|
||||
* @property {Pointers.Trigger[]} [triggers=[]] - A list of ways that a {@link Controller} action or function should trigger
|
||||
* events on the entity or overlay currently intersected.
|
||||
* events on the object currently intersected.
|
||||
* @property {PickType} pointerType - The type of pointer returned from {@link Pointers.getPointerProperties} or
|
||||
* {@link Pointers.getPointerScriptParameters}. A parabola pointer's type is {@link PickType(0)|PickType.Parabola}.
|
||||
* @property {number} [pickID] - The ID of the pick created alongside this pointer, returned from
|
||||
|
@ -622,7 +620,6 @@ bool rayPointerPropertiesFromScriptValue(const ScriptValue& value, RayPointerPro
|
|||
out.properties[*renderStatesName].setValue(renderStates);
|
||||
}
|
||||
}
|
||||
qDebug() << "rayPointerPropertiesFromScriptValue" << out.properties;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -675,7 +672,6 @@ bool stylusPointerPropertiesFromScriptValue(const ScriptValue& value, StylusPoin
|
|||
out.properties[*renderStatesName].setValue(renderStates);
|
||||
}
|
||||
}
|
||||
qDebug() << "stylusPointerPropertiesFromScriptValue" << out.properties;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -714,7 +710,6 @@ bool parabolaPointerPropertiesFromScriptValue(const ScriptValue& value, Parabola
|
|||
pathProperties.copyFromScriptValue(path, false);
|
||||
stateMap.insert("pathPropertyIndex", QVariant(out.entityProperties.length()));
|
||||
out.entityProperties.append(pathProperties);
|
||||
qDebug() << "parabolaPointerPropertiesFromScriptValue : added path entity";
|
||||
}
|
||||
|
||||
if (stateMap["end"].isValid()) {
|
||||
|
@ -723,7 +718,6 @@ bool parabolaPointerPropertiesFromScriptValue(const ScriptValue& value, Parabola
|
|||
endProperties.copyFromScriptValue(end, false);
|
||||
stateMap.insert("endPropertyIndex", QVariant(out.entityProperties.length()));
|
||||
out.entityProperties.append(endProperties);
|
||||
qDebug() << "parabolaPointerPropertiesFromScriptValue : added end entity";
|
||||
}
|
||||
renderStates[i].setValue(stateMap);
|
||||
}
|
||||
|
@ -731,6 +725,5 @@ bool parabolaPointerPropertiesFromScriptValue(const ScriptValue& value, Parabola
|
|||
out.properties[*renderStatesName].setValue(renderStates);
|
||||
}
|
||||
}
|
||||
qDebug() << "parabolaPointerPropertiesFromScriptValue" << out.properties;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ public:
|
|||
* ignorePickIntersection: true
|
||||
* };
|
||||
* var intersectedPath = {
|
||||
* type: "line3d",
|
||||
* type: "PolyLine",
|
||||
* color: { red: 0, green: 255, blue: 0 },
|
||||
* };
|
||||
* var searchEnd = {
|
||||
|
@ -114,7 +114,7 @@ public:
|
|||
* ignorePickIntersection: true
|
||||
* };
|
||||
* var searchPath = {
|
||||
* type: "line3d",
|
||||
* type: "PolyLine",
|
||||
* color: { red: 255, green: 0, blue: 0 },
|
||||
* };
|
||||
*
|
||||
|
@ -224,8 +224,8 @@ public:
|
|||
* @param {number} id - The ID of the pointer.
|
||||
* @param {string} renderState - The name of the render state to edit.
|
||||
* @param {Pointers.RayPointerRenderState|Pointers.ParabolaPointerRenderState} properties - The new properties for the
|
||||
* render state. Only the overlay properties to change need be specified.
|
||||
* @example <caption>Change the dimensions of a ray pointer's intersecting end overlay.</caption>
|
||||
* render state. Only the properties to change need be specified.
|
||||
* @example <caption>Change the dimensions of a ray pointer's intersecting end entity.</caption>
|
||||
* var intersectEnd = {
|
||||
* type: "sphere",
|
||||
* dimensions: { x: 0.2, y: 0.2, z: 0.2 },
|
||||
|
@ -234,7 +234,7 @@ public:
|
|||
* ignorePickIntersection: true
|
||||
* };
|
||||
* var intersectedPath = {
|
||||
* type: "line3d",
|
||||
* type: "PolyLine",
|
||||
* color: { red: 0, green: 255, blue: 0 },
|
||||
* };
|
||||
* var searchEnd = {
|
||||
|
@ -245,7 +245,7 @@ public:
|
|||
* ignorePickIntersection: true
|
||||
* };
|
||||
* var searchPath = {
|
||||
* type: "line3d",
|
||||
* type: "PolyLine",
|
||||
* color: { red: 255, green: 0, blue: 0 },
|
||||
* };
|
||||
*
|
||||
|
@ -310,7 +310,7 @@ public:
|
|||
* ignorePickIntersection: true
|
||||
* };
|
||||
* var intersectedPath = {
|
||||
* type: "line3d",
|
||||
* type: "PolyLine",
|
||||
* color: { red: 0, green: 255, blue: 0 },
|
||||
* };
|
||||
* var searchEnd = {
|
||||
|
@ -321,7 +321,7 @@ public:
|
|||
* ignorePickIntersection: true
|
||||
* };
|
||||
* var searchPath = {
|
||||
* type: "line3d",
|
||||
* type: "PolyLine",
|
||||
* color: { red: 255, green: 0, blue: 0 },
|
||||
* };
|
||||
*
|
||||
|
@ -423,6 +423,14 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); }
|
||||
|
||||
/*@jsdoc
|
||||
* Sets the delay of a Ray pointer.
|
||||
* <p><strong>Note:</strong> Not used by stylus or parabola pointers.</p>
|
||||
* @function Pointers.setDelay
|
||||
* @param {number} id - The ID of the pointer.
|
||||
* @param {number} delay - The desired delay in seconds.
|
||||
*/
|
||||
Q_INVOKABLE void setDelay(unsigned int uid, float delay) const { DependencyManager::get<PointerManager>()->setDelay(uid, delay); }
|
||||
|
||||
/*@jsdoc
|
||||
* Checks if a pointer is associated with the left hand: a ray or parabola pointer with <code>joint</code> property set to
|
||||
|
|
|
@ -19,10 +19,43 @@ PickRay RayPick::getMathematicalPick() const {
|
|||
return _mathPick;
|
||||
}
|
||||
|
||||
float delayHalf = 0.0f;
|
||||
withReadLock([&] {
|
||||
delayHalf = _delayHalf;
|
||||
});
|
||||
const bool hasDelay = _prevUpdate != 0 && delayHalf > 0.0f && !isNaN(_prevDirection.x);
|
||||
const float now = secTimestampNow();
|
||||
const float dt = now - _prevUpdate;
|
||||
float alpha = 0.0f;
|
||||
if (hasDelay) {
|
||||
// This equation gives a framerate-independent lerp for a moving target
|
||||
// https://twitter.com/FreyaHolmer/status/1757836988495847568
|
||||
alpha = 1 - exp2(-dt / delayHalf);
|
||||
}
|
||||
|
||||
Transform currentParentTransform = parentTransform->getTransform();
|
||||
glm::vec3 origin = currentParentTransform.transform(_mathPick.origin);
|
||||
glm::vec3 direction = glm::normalize(currentParentTransform.transformDirection(_mathPick.direction));
|
||||
return PickRay(origin, direction);
|
||||
|
||||
glm::vec3 direction;
|
||||
glm::vec3 newDirection = glm::normalize(currentParentTransform.transformDirection(_mathPick.direction));
|
||||
if (hasDelay) {
|
||||
PickResultPointer result = getPrevPickResult();
|
||||
if (!result || !result->doesIntersect()) {
|
||||
direction = glm::normalize(glm::mix(_prevDirection, newDirection, alpha));
|
||||
} else {
|
||||
auto rayResult = std::static_pointer_cast<RayPickResult>(result);
|
||||
glm::vec3 oldDirection = glm::normalize(rayResult->intersection - origin);
|
||||
direction = glm::normalize(glm::mix(oldDirection, newDirection, alpha));
|
||||
}
|
||||
} else {
|
||||
direction = newDirection;
|
||||
}
|
||||
|
||||
_prevUpdate = now;
|
||||
if (!isNaN(direction.x)) {
|
||||
_prevDirection = direction;
|
||||
}
|
||||
return PickRay(origin, direction, newDirection);
|
||||
}
|
||||
|
||||
PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
|
||||
|
@ -65,6 +98,14 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) {
|
|||
return std::make_shared<RayPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick);
|
||||
}
|
||||
|
||||
void RayPick::setDelay(float delay) {
|
||||
withWriteLock([&] {
|
||||
// We want to be within 0.1% of the target in <delay> seconds
|
||||
// https://twitter.com/FreyaHolmer/status/1757836988495847568
|
||||
_delayHalf = -std::max(delay, 0.0f) / log2(0.001);
|
||||
});
|
||||
}
|
||||
|
||||
Transform RayPick::getResultTransform() const {
|
||||
PickResultPointer result = getPrevPickResult();
|
||||
if (!result) {
|
||||
|
@ -89,8 +130,6 @@ glm::vec3 RayPick::intersectRayWithEntityXYPlane(const QUuid& entityID, const gl
|
|||
return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint());
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::vec2 RayPick::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint, bool unNormalized) {
|
||||
glm::quat invRot = glm::inverse(rotation);
|
||||
glm::vec3 localPos = invRot * (worldPos - position);
|
||||
|
|
|
@ -84,9 +84,8 @@ public:
|
|||
class RayPick : public Pick<PickRay> {
|
||||
|
||||
public:
|
||||
RayPick(glm::vec3 position, glm::vec3 direction, const PickFilter& filter, float maxDistance, bool enabled) :
|
||||
Pick(PickRay(position, direction), filter, maxDistance, enabled) {
|
||||
}
|
||||
RayPick(glm::vec3 position, glm::vec3 direction, const PickFilter& filter, float maxDistance, float delayHalf, bool enabled) :
|
||||
Pick(PickRay(position, direction), filter, maxDistance, enabled), _delayHalf(delayHalf) {}
|
||||
|
||||
PickType getType() const override { return PickType::Ray; }
|
||||
|
||||
|
@ -98,6 +97,8 @@ public:
|
|||
PickResultPointer getHUDIntersection(const PickRay& pick) override;
|
||||
Transform getResultTransform() const override;
|
||||
|
||||
void setDelay(float delay) override;
|
||||
|
||||
// These are helper functions for projecting and intersecting rays
|
||||
static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction);
|
||||
static glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized = true);
|
||||
|
@ -107,6 +108,10 @@ public:
|
|||
|
||||
private:
|
||||
static glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration);
|
||||
|
||||
float _delayHalf { 0.0f };
|
||||
mutable float _prevUpdate { 0.0f };
|
||||
mutable glm::vec3 _prevDirection { NAN };
|
||||
};
|
||||
|
||||
#endif // hifi_RayPick_h
|
||||
|
|
|
@ -48,29 +48,6 @@ PickQuery::PickType StylusPointer::getType() const {
|
|||
}
|
||||
|
||||
QUuid StylusPointer::buildStylus(const QVariantMap& properties) {
|
||||
// FIXME: we have to keep using the Overlays interface here, because existing scripts use overlay properties to define pointers
|
||||
/*QVariantMap propertiesMap;
|
||||
|
||||
QString modelUrl = DEFAULT_STYLUS_MODEL_URL;
|
||||
|
||||
if (properties["model"].isValid()) {
|
||||
QVariantMap modelData = properties["model"].toMap();
|
||||
|
||||
if (modelData["url"].isValid()) {
|
||||
modelUrl = modelData["url"].toString();
|
||||
}
|
||||
}
|
||||
// TODO: make these configurable per pointer
|
||||
propertiesMap["name"] = "stylus";
|
||||
propertiesMap["url"] = modelUrl;
|
||||
propertiesMap["loadPriority"] = 10.0f;
|
||||
propertiesMap["solid"] = true;
|
||||
propertiesMap["visible"] = false;
|
||||
propertiesMap["ignorePickIntersection"] = true;
|
||||
propertiesMap["drawInFront"] = false;
|
||||
|
||||
return qApp->getOverlays().addOverlay("model", propertiesMap);*/
|
||||
|
||||
EntityItemProperties entityProperties;
|
||||
QString modelURL = DEFAULT_STYLUS_MODEL_URL;
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ WindowScriptingInterface::~WindowScriptingInterface() {
|
|||
}
|
||||
|
||||
ScriptValue WindowScriptingInterface::hasFocus() {
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
return engine()->newValue(qApp->hasFocus());
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ void WindowScriptingInterface::alert(const QString& message) {
|
|||
/// \param const QString& message message to display
|
||||
/// \return ScriptValue `true` if 'Yes' was clicked, `false` otherwise
|
||||
ScriptValue WindowScriptingInterface::confirm(const QString& message) {
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
return engine()->newValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)));
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ ScriptValue WindowScriptingInterface::confirm(const QString& message) {
|
|||
/// \return ScriptValue string text value in text box if the dialog was accepted, `null` otherwise.
|
||||
ScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) {
|
||||
QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText);
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto sResult = engine()->newValue(result);
|
||||
if (sResult.equals(engine()->newValue(""))) {
|
||||
return engine()->nullValue();
|
||||
|
@ -234,7 +234,7 @@ ScriptValue WindowScriptingInterface::browseDir(const QString& title, const QStr
|
|||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
|
||||
}
|
||||
|
||||
|
@ -279,7 +279,7 @@ ScriptValue WindowScriptingInterface::browse(const QString& title, const QString
|
|||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,7 @@ ScriptValue WindowScriptingInterface::save(const QString& title, const QString&
|
|||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
|
||||
}
|
||||
|
||||
|
@ -378,7 +378,7 @@ ScriptValue WindowScriptingInterface::browseAssets(const QString& title, const Q
|
|||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
return result.isEmpty() ? engine()->nullValue() : engine()->newValue(result);
|
||||
}
|
||||
|
||||
|
|
|
@ -303,10 +303,13 @@ bool Keyboard::isRaised() const {
|
|||
return resultWithReadLock<bool>([&] { return _raised; });
|
||||
}
|
||||
|
||||
void Keyboard::setRaised(bool raised) {
|
||||
void Keyboard::setRaised(bool raised, bool inputToHudUI) {
|
||||
|
||||
bool isRaised;
|
||||
withReadLock([&] { isRaised = _raised; });
|
||||
|
||||
_inputToHudUI = inputToHudUI;
|
||||
|
||||
if (isRaised != raised) {
|
||||
raiseKeyboardAnchor(raised);
|
||||
raiseKeyboard(raised);
|
||||
|
@ -585,8 +588,13 @@ void Keyboard::handleTriggerBegin(const QUuid& id, const PointerEvent& event) {
|
|||
|
||||
QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString);
|
||||
QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString);
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent);
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent);
|
||||
if (_inputToHudUI) {
|
||||
QCoreApplication::postEvent(qApp->getPrimaryWidget(), pressEvent);
|
||||
QCoreApplication::postEvent(qApp->getPrimaryWidget(), releaseEvent);
|
||||
} else {
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent);
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent);
|
||||
}
|
||||
|
||||
if (!getPreferMalletsOverLasers()) {
|
||||
key.startTimer(KEY_PRESS_TIMEOUT_MS);
|
||||
|
|
|
@ -93,7 +93,7 @@ public:
|
|||
void createKeyboard();
|
||||
void registerKeyboardHighlighting();
|
||||
bool isRaised() const;
|
||||
void setRaised(bool raised);
|
||||
void setRaised(bool raised, bool inputToHudUI = false);
|
||||
void setResetKeyboardPositionOnRaise(bool reset);
|
||||
bool isPassword() const;
|
||||
void setPassword(bool password);
|
||||
|
@ -190,6 +190,8 @@ private:
|
|||
QSet<QUuid> _itemsToIgnore;
|
||||
std::vector<QHash<QUuid, Key>> _keyboardLayers;
|
||||
|
||||
// Send keyboard events to hud UI if true
|
||||
std::atomic<bool> _inputToHudUI { false };
|
||||
bool _created { false };
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <plugins/PluginManager.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
#include <raypick/PickScriptingInterface.h>
|
||||
#include "scripting/RenderScriptingInterface.h"
|
||||
#include "Application.h"
|
||||
#include "DialogsManager.h"
|
||||
|
@ -207,6 +208,23 @@ void setupPreferences() {
|
|||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []() -> bool { return qApp->getPreferAvatarFingerOverStylus(); };
|
||||
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []() -> float { return DependencyManager::get<PickScriptingInterface>()->getHandLaserDelay(); };
|
||||
auto setter = [](float value) { DependencyManager::get<PickScriptingInterface>()->setHandLaserDelay(value); };
|
||||
auto delaySlider = new SpinnerSliderPreference(UI_CATEGORY, "Laser Delay (seconds)", getter, setter);
|
||||
delaySlider->setMin(0.0f);
|
||||
delaySlider->setMax(0.7f);
|
||||
delaySlider->setStep(0.05f);
|
||||
delaySlider->setDecimals(2.0f);
|
||||
preferences->addPreference(delaySlider);
|
||||
}
|
||||
|
||||
static const QString VIEW_CATEGORY{ "View" };
|
||||
{
|
||||
auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); };
|
||||
|
@ -226,12 +244,6 @@ void setupPreferences() {
|
|||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); };
|
||||
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
|
||||
}
|
||||
|
||||
// Snapshots
|
||||
static const QString SNAPSHOTS { "Snapshots" };
|
||||
{
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <Mmsystem.h>
|
||||
#include <mmdeviceapi.h>
|
||||
|
|
|
@ -2566,7 +2566,7 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) {
|
|||
void AttachmentDataObject::setModelURL(const QString& modelURL) {
|
||||
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.modelURL = modelURL;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
|
@ -2577,7 +2577,7 @@ QString AttachmentDataObject::getModelURL() const {
|
|||
void AttachmentDataObject::setJointName(const QString& jointName) {
|
||||
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.jointName = jointName;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
|
@ -2588,7 +2588,7 @@ QString AttachmentDataObject::getJointName() const {
|
|||
void AttachmentDataObject::setTranslation(const glm::vec3& translation) {
|
||||
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.translation = translation;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
|
@ -2599,7 +2599,7 @@ glm::vec3 AttachmentDataObject::getTranslation() const {
|
|||
void AttachmentDataObject::setRotation(const glm::quat& rotation) {
|
||||
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.rotation = rotation;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
|
@ -2610,7 +2610,7 @@ glm::quat AttachmentDataObject::getRotation() const {
|
|||
void AttachmentDataObject::setScale(float scale) {
|
||||
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.scale = scale;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
|
@ -2621,7 +2621,7 @@ float AttachmentDataObject::getScale() const {
|
|||
void AttachmentDataObject::setIsSoft(bool isSoft) {
|
||||
AttachmentData data = scriptvalue_cast<AttachmentData>(thisObject());
|
||||
data.isSoft = isSoft;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
thisObject() = engine()->toScriptValue(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -1329,7 +1329,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {boolean} unlit=false - <code>true</code> if the entity is unaffected by lighting, <code>false</code> if it is lit
|
||||
* by the key light and local lights.
|
||||
* @property {string} font="" - The font to render the text with. It can be one of the following: <code>"Courier"</code>,
|
||||
* <code>"Inconsolata"</code>, <code>"Roboto"</code>, <code>"Timeless"</code>, or a path to a .sdff file.
|
||||
* <code>"Inconsolata"</code>, <code>"Roboto"</code>, <code>"Timeless"</code>, or a path to a PNG MTSDF .arfont file generated
|
||||
* by the msdf-atlas-gen tool (https://github.com/Chlumsky/msdf-atlas-gen).
|
||||
* @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text.
|
||||
* @property {Color} textEffectColor=255,255,255 - The color of the effect.
|
||||
* @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range <code>0.0</code> – <code>0.5</code>.
|
||||
|
|
|
@ -69,7 +69,6 @@ void EntityScriptServerLogClient::handleEntityServerScriptLogPacket(QSharedPoint
|
|||
QString messageText = QString::fromUtf8(message->readAll());
|
||||
QJsonParseError error;
|
||||
QJsonDocument document = QJsonDocument::fromJson(messageText.toUtf8(), &error);
|
||||
emit receivedNewLogLines(messageText);
|
||||
if(document.isNull()) {
|
||||
qWarning() << "EntityScriptServerLogClient::handleEntityServerScriptLogPacket: Cannot parse JSON: " << error.errorString()
|
||||
<< " Contents: " << messageText;
|
||||
|
@ -98,21 +97,37 @@ void EntityScriptServerLogClient::handleEntityServerScriptLogPacket(QSharedPoint
|
|||
case ScriptMessage::Severity::SEVERITY_INFO:
|
||||
emit scriptEngines->infoEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(),
|
||||
scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true);
|
||||
emit receivedNewLogLines("[ INFO {" + scriptMessage.getEntityID().toString() + "} "
|
||||
+ scriptMessage.getFileName() + ":"
|
||||
+ QString::number(scriptMessage.getLineNumber()) + "] "
|
||||
+ scriptMessage.getMessage());
|
||||
break;
|
||||
|
||||
case ScriptMessage::Severity::SEVERITY_PRINT:
|
||||
emit scriptEngines->printedEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(),
|
||||
scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true);
|
||||
emit receivedNewLogLines("[ WARNING {" + scriptMessage.getEntityID().toString() + "} "
|
||||
+ scriptMessage.getFileName() + ":"
|
||||
+ QString::number(scriptMessage.getLineNumber()) + "] "
|
||||
+ scriptMessage.getMessage());
|
||||
break;
|
||||
|
||||
case ScriptMessage::Severity::SEVERITY_WARNING:
|
||||
emit scriptEngines->warningEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(),
|
||||
scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true);
|
||||
emit receivedNewLogLines("[ WARNING {" + scriptMessage.getEntityID().toString() + "} "
|
||||
+ scriptMessage.getFileName() + ":"
|
||||
+ QString::number(scriptMessage.getLineNumber()) + "] "
|
||||
+ scriptMessage.getMessage());
|
||||
break;
|
||||
|
||||
case ScriptMessage::Severity::SEVERITY_ERROR:
|
||||
emit scriptEngines->errorEntityMessage(scriptMessage.getMessage(), scriptMessage.getFileName(),
|
||||
scriptMessage.getLineNumber(), scriptMessage.getEntityID(), true);
|
||||
emit receivedNewLogLines("[ ERROR {" + scriptMessage.getEntityID().toString() + "} "
|
||||
+ scriptMessage.getFileName() + ":"
|
||||
+ QString::number(scriptMessage.getLineNumber()) + "] "
|
||||
+ scriptMessage.getMessage());
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
#include <list>
|
||||
#include <functional>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "glad/glad.h"
|
||||
Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45")
|
||||
|
||||
using namespace gpu;
|
||||
|
@ -21,9 +23,27 @@ using namespace gpu::gl45;
|
|||
GLint GL45Backend::MAX_COMBINED_SHADER_STORAGE_BLOCKS{ 0 };
|
||||
GLint GL45Backend::MAX_UNIFORM_LOCATIONS{ 0 };
|
||||
|
||||
#ifdef GLAD_DEBUG
|
||||
static void post_call_callback_gl(const char *name, void *funcptr, int len_args, ...) {
|
||||
(void)funcptr;
|
||||
(void)len_args;
|
||||
|
||||
GLenum error_code = glad_glGetError();
|
||||
if (error_code != GL_NO_ERROR) {
|
||||
qCWarning(gpugl45logging) << "OpenGL error" << error_code << "in" << name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void staticInit() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
#ifdef GLAD_DEBUG
|
||||
// This sets the post call callback to a logging function. By default it prints on
|
||||
// stderr and skips our log. It only exists in debug builds.
|
||||
glad_set_post_callback(&post_call_callback_gl);
|
||||
#endif
|
||||
glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &GL45Backend::MAX_COMBINED_SHADER_STORAGE_BLOCKS);
|
||||
glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &GL45Backend::MAX_UNIFORM_LOCATIONS);
|
||||
});
|
||||
|
@ -82,7 +102,7 @@ void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) {
|
|||
uint32 startIndex = batch._params[paramOffset + 0]._uint;
|
||||
|
||||
GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType];
|
||||
|
||||
|
||||
auto typeByteSize = TYPE_SIZE[_input._indexBufferType];
|
||||
GLvoid* indexBufferByteOffset = reinterpret_cast<GLvoid*>(startIndex * typeByteSize + _input._indexBufferOffset);
|
||||
|
||||
|
@ -148,7 +168,7 @@ void GL45Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset
|
|||
GLenum glType = gl::ELEMENT_TYPE_TO_GL[_input._indexBufferType];
|
||||
auto typeByteSize = TYPE_SIZE[_input._indexBufferType];
|
||||
GLvoid* indexBufferByteOffset = reinterpret_cast<GLvoid*>(startIndex * typeByteSize + _input._indexBufferOffset);
|
||||
|
||||
|
||||
if (isStereo()) {
|
||||
GLint trueNumInstances = 2 * numInstances;
|
||||
|
||||
|
|
|
@ -2,3 +2,7 @@ set(TARGET_NAME graphics-scripting)
|
|||
setup_hifi_library()
|
||||
link_hifi_libraries(shared networking graphics model-serializers image material-networking model-networking script-engine)
|
||||
include_hifi_library_headers(gpu)
|
||||
|
||||
if (WIN32)
|
||||
add_compile_definitions(_USE_MATH_DEFINES)
|
||||
endif()
|
|
@ -45,7 +45,7 @@ void Mesh::setVertexFormatAndStream(const gpu::Stream::FormatPointer& vf, const
|
|||
|
||||
// We require meshes to have a color attribute. If they don't, we default to white.
|
||||
if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) {
|
||||
int channelNum = _vertexStream.getNumBuffers();
|
||||
gpu::Stream::Slot channelNum = (gpu::Stream::Slot)_vertexStream.getNumBuffers();
|
||||
_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE);
|
||||
_vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <qfile.h>
|
||||
#include <qfileinfo.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
@ -42,6 +44,18 @@
|
|||
|
||||
#include "FBXSerializer.h"
|
||||
|
||||
float atof_locale_independent(char* str) {
|
||||
//TODO: Once we have C++17 we can use std::from_chars
|
||||
std::istringstream streamToParse(str);
|
||||
streamToParse.imbue(std::locale("C"));
|
||||
float value;
|
||||
if (!(streamToParse >> value)) {
|
||||
qDebug(modelformat) << "cgltf: Cannot parse float from string: " << str;
|
||||
return 0.0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
#define GLTF_GET_INDICIES(accCount) int index1 = (indices[n + 0] * accCount); int index2 = (indices[n + 1] * accCount); int index3 = (indices[n + 2] * accCount);
|
||||
|
||||
#define GLTF_APPEND_ARRAY_1(newArray, oldArray) GLTF_GET_INDICIES(1) \
|
||||
|
@ -148,8 +162,8 @@ bool findNodeInPointerArray(const cgltf_node *nodePointer, cgltf_node **nodes, s
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename T> bool findPointerInArray(const T *pointer, const T *array, size_t arraySize, int &index) {
|
||||
for (int i = 0; i < arraySize; i++) {
|
||||
template<typename T> bool findPointerInArray(const T *pointer, const T *array, size_t arraySize, size_t &index) {
|
||||
for (size_t i = 0; i < arraySize; i++) {
|
||||
if (&array[i] == pointer) {
|
||||
index = i;
|
||||
return true;
|
||||
|
@ -187,13 +201,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
|
|||
auto &node = _data->nodes[index];
|
||||
for(size_t childIndexInParent = 0; childIndexInParent < node.children_count; childIndexInParent++) {
|
||||
cgltf_node *child = node.children[childIndexInParent];
|
||||
int childIndex = 0;
|
||||
size_t childIndex = 0;
|
||||
if (!findPointerInArray(child, _data->nodes, _data->nodes_count, childIndex)) {
|
||||
qDebug(modelformat) << "findPointerInArray failed for model: " << _url;
|
||||
hfmModel.loadErrorCount++;
|
||||
return false;
|
||||
}
|
||||
parents[childIndex] = index;
|
||||
parents[(int)childIndex] = index;
|
||||
}
|
||||
sortedNodes.push_back(index);
|
||||
}
|
||||
|
@ -918,14 +932,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
|
|||
continue;
|
||||
}
|
||||
|
||||
int jointIndex = 0;
|
||||
size_t jointIndex = 0;
|
||||
if (!findPointerInArray(node.skin->joints[clusterJoints[c]], _data->nodes, _data->nodes_count, jointIndex)) {
|
||||
qCWarning(modelformat) << "Cannot find the joint " << node.skin->joints[clusterJoints[c]]->name <<" in joint array";
|
||||
hfmModel.loadErrorCount++;
|
||||
continue;
|
||||
}
|
||||
mesh.clusterIndices[prevMeshClusterIndexCount + c] =
|
||||
originalToNewNodeIndexMap[jointIndex];
|
||||
originalToNewNodeIndexMap[(int)jointIndex];
|
||||
}
|
||||
|
||||
// normalize and compress to 16-bits
|
||||
|
@ -961,14 +975,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
|
|||
}
|
||||
}
|
||||
|
||||
int materialIndex = 0;
|
||||
size_t materialIndex = 0;
|
||||
if (primitive.material != nullptr && !findPointerInArray(primitive.material, _data->materials, _data->materials_count, materialIndex)) {
|
||||
qCWarning(modelformat) << "GLTFSerializer::buildGeometry: Invalid material pointer";
|
||||
hfmModel.loadErrorCount++;
|
||||
return false;
|
||||
}
|
||||
if (primitive.material != nullptr) {
|
||||
part.materialID = materialIDs[materialIndex];
|
||||
part.materialID = materialIDs[(int)materialIndex];
|
||||
}
|
||||
mesh.parts.push_back(part);
|
||||
|
||||
|
@ -1306,7 +1320,7 @@ HFMTexture GLTFSerializer::getHFMTexture(const cgltf_texture *texture) {
|
|||
size_t offset = bufferView->offset;
|
||||
int length = (int)bufferView->size;
|
||||
|
||||
int imageIndex = 0;
|
||||
size_t imageIndex = 0;
|
||||
if (!findPointerInArray(image, _data->images, _data->images_count, imageIndex)) {
|
||||
// This should never happen. It would mean a bug in cgltf library.
|
||||
qDebug(modelformat) << "GLTFSerializer::getHFMTexture: can't find texture in the array";
|
||||
|
@ -1346,7 +1360,7 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& m
|
|||
}
|
||||
if (mToonExtension["shadeMultiplyTexture"].isObject()) {
|
||||
QJsonObject object = mToonExtension["shadeMultiplyTexture"].toObject();
|
||||
if (object["index"].isDouble() && object["index"].toInt() < _data->textures_count) {
|
||||
if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) {
|
||||
hfmMat.shadeTexture = getHFMTexture(&_data->textures[object["index"].toInt()]);
|
||||
}
|
||||
}
|
||||
|
@ -1355,7 +1369,7 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& m
|
|||
}
|
||||
if (mToonExtension["shadingShiftTexture"].isObject()) {
|
||||
QJsonObject object = mToonExtension["shadingShiftTexture"].toObject();
|
||||
if (object["index"].isDouble() && object["index"].toInt() < _data->textures_count) {
|
||||
if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) {
|
||||
hfmMat.shadingShiftTexture = getHFMTexture(&_data->textures[object["index"].toInt()]);
|
||||
}
|
||||
}
|
||||
|
@ -1370,7 +1384,7 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& m
|
|||
}
|
||||
if (mToonExtension["matcapTexture"].isObject()) {
|
||||
QJsonObject object = mToonExtension["matcapTexture"].toObject();
|
||||
if (object["index"].isDouble() && object["index"].toInt() < _data->textures_count) {
|
||||
if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) {
|
||||
hfmMat.matcapTexture = getHFMTexture(&_data->textures[object["index"].toInt()]);
|
||||
}
|
||||
}
|
||||
|
@ -1388,7 +1402,7 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& m
|
|||
}
|
||||
if (mToonExtension["rimMultiplyTexture"].isObject()) {
|
||||
QJsonObject object = mToonExtension["rimMultiplyTexture"].toObject();
|
||||
if (object["index"].isDouble() && object["index"].toInt() < _data->textures_count) {
|
||||
if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) {
|
||||
hfmMat.rimTexture = getHFMTexture(&_data->textures[object["index"].toInt()]);
|
||||
}
|
||||
}
|
||||
|
@ -1417,7 +1431,7 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const cgltf_material& m
|
|||
}
|
||||
if (mToonExtension["uvAnimationMaskTexture"].isObject()) {
|
||||
QJsonObject object = mToonExtension["uvAnimationMaskTexture"].toObject();
|
||||
if (object["index"].isDouble() && object["index"].toInt() < _data->textures_count) {
|
||||
if (object["index"].isDouble() && object["index"].toInt() < (int)_data->textures_count) {
|
||||
hfmMat.uvAnimationTexture = getHFMTexture(&_data->textures[object["index"].toInt()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,24 +14,12 @@
|
|||
#ifndef hifi_GLTFSerializer_h
|
||||
#define hifi_GLTFSerializer_h
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <memory.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
#include <hfm/HFMSerializer.h>
|
||||
|
||||
static float atof_locale_independent(char *str) {
|
||||
//TODO: Once we have C++17 we can use std::from_chars
|
||||
std::istringstream streamToParse(str);
|
||||
streamToParse.imbue(std::locale("C"));
|
||||
float value;
|
||||
if(!(streamToParse >> value)) {
|
||||
qDebug(modelformat) << "cgltf: Cannot parse float from string: " << str;
|
||||
return 0.0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
float atof_locale_independent(char* str);
|
||||
|
||||
#define CGLTF_ATOF(str) atof_locale_independent(str)
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class CharacterController : public btCharacterControllerInterface {
|
|||
|
||||
public:
|
||||
enum class FollowType : uint8_t {
|
||||
Rotation,
|
||||
Rotation = 0,
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Count
|
||||
|
|
|
@ -170,6 +170,8 @@ public:
|
|||
void setIgnoreItems(const QVector<QUuid>& items);
|
||||
void setIncludeItems(const QVector<QUuid>& items);
|
||||
|
||||
virtual void setDelay(float delay) {}
|
||||
|
||||
virtual QVariantMap toVariantMap() const {
|
||||
QVariantMap properties;
|
||||
|
||||
|
|
|
@ -129,6 +129,13 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
|
|||
}
|
||||
}
|
||||
|
||||
void PickManager::setDelay(unsigned int uid, float delay) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
pick->setDelay(delay);
|
||||
}
|
||||
}
|
||||
|
||||
Transform PickManager::getParentTransform(unsigned int uid) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
|
||||
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
|
||||
void setDelay(unsigned int uid, float delay) const;
|
||||
|
||||
Transform getParentTransform(unsigned int uid) const;
|
||||
Transform getResultTransform(unsigned int uid) const;
|
||||
|
|
|
@ -74,6 +74,10 @@ void Pointer::setIncludeItems(const QVector<QUuid>& includeItems) const {
|
|||
DependencyManager::get<PickManager>()->setIncludeItems(_pickUID, includeItems);
|
||||
}
|
||||
|
||||
void Pointer::setDelay(float delay) const {
|
||||
DependencyManager::get<PickManager>()->setDelay(_pickUID, delay);
|
||||
}
|
||||
|
||||
bool Pointer::isLeftHand() const {
|
||||
return DependencyManager::get<PickManager>()->isLeftHand(_pickUID);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
virtual void setPrecisionPicking(bool precisionPicking);
|
||||
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
|
||||
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;
|
||||
virtual void setDelay(float delay) const;
|
||||
|
||||
bool isLeftHand() const;
|
||||
bool isRightHand() const;
|
||||
|
|
|
@ -157,6 +157,13 @@ void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, boo
|
|||
}
|
||||
}
|
||||
|
||||
void PointerManager::setDelay(unsigned int uid, float delay) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setDelay(delay);
|
||||
}
|
||||
}
|
||||
|
||||
bool PointerManager::isLeftHand(unsigned int uid) {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
|
||||
void setLength(unsigned int uid, float length) const;
|
||||
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const;
|
||||
void setDelay(unsigned int uid, float delay) const;
|
||||
|
||||
void update();
|
||||
|
||||
|
|
|
@ -38,34 +38,36 @@ using namespace recording;
|
|||
static const QString HFR_EXTENSION = "hfr";
|
||||
|
||||
RecordingScriptingInterface::RecordingScriptingInterface() {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player = DependencyManager::get<Deck>();
|
||||
_recorder = DependencyManager::get<Recorder>();
|
||||
}
|
||||
|
||||
bool RecordingScriptingInterface::isPlaying() const {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
return _player->isPlaying();
|
||||
}
|
||||
|
||||
bool RecordingScriptingInterface::isPaused() const {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
return _player->isPaused();
|
||||
}
|
||||
|
||||
float RecordingScriptingInterface::playerElapsed() const {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
return _player->position();
|
||||
}
|
||||
|
||||
float RecordingScriptingInterface::playerLength() const {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
return _player->length();
|
||||
}
|
||||
|
||||
void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, const ScriptValue& callback) {
|
||||
Locker(_mutex);
|
||||
_player->queueClip(clipLoader->getClip());
|
||||
{
|
||||
Locker lock(_mutex);
|
||||
_player->queueClip(clipLoader->getClip());
|
||||
}
|
||||
|
||||
if (callback.isFunction()) {
|
||||
auto engine = callback.engine();
|
||||
|
@ -75,8 +77,6 @@ void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader,
|
|||
}
|
||||
|
||||
void RecordingScriptingInterface::loadRecording(const QString& url, const ScriptValue& callback) {
|
||||
Locker(_mutex);
|
||||
|
||||
auto clipLoader = DependencyManager::get<recording::ClipCache>()->getClipLoader(url);
|
||||
|
||||
if (clipLoader->isLoaded()) {
|
||||
|
@ -85,6 +85,8 @@ void RecordingScriptingInterface::loadRecording(const QString& url, const Script
|
|||
return;
|
||||
}
|
||||
|
||||
Locker lock(_mutex);
|
||||
|
||||
// hold a strong pointer to the loading clip so that it has a chance to load
|
||||
_clipLoaders.insert(clipLoader);
|
||||
|
||||
|
@ -134,12 +136,12 @@ void RecordingScriptingInterface::startPlaying() {
|
|||
return;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player->play();
|
||||
}
|
||||
|
||||
void RecordingScriptingInterface::setPlayerVolume(float volume) {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player->setVolume(std::min(std::max(volume, 0.0f), 1.0f));
|
||||
}
|
||||
|
||||
|
@ -153,7 +155,7 @@ void RecordingScriptingInterface::setPlayerTime(float time) {
|
|||
return;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player->seek(time);
|
||||
}
|
||||
|
||||
|
@ -162,7 +164,7 @@ void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurren
|
|||
}
|
||||
|
||||
void RecordingScriptingInterface::setPlayerLoop(bool loop) {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player->loop(loop);
|
||||
}
|
||||
|
||||
|
@ -183,7 +185,7 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode
|
|||
}
|
||||
|
||||
void RecordingScriptingInterface::pausePlayer() {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player->pause();
|
||||
}
|
||||
|
||||
|
@ -193,7 +195,7 @@ void RecordingScriptingInterface::stopPlaying() {
|
|||
return;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_player->stop();
|
||||
}
|
||||
|
||||
|
@ -211,7 +213,7 @@ void RecordingScriptingInterface::startRecording() {
|
|||
return;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_recorder->start();
|
||||
}
|
||||
|
||||
|
@ -221,7 +223,7 @@ void RecordingScriptingInterface::stopRecording() {
|
|||
return;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
_recorder->stop();
|
||||
_lastClip = _recorder->getClip();
|
||||
_lastClip->seek(0);
|
||||
|
@ -236,7 +238,7 @@ QString RecordingScriptingInterface::getDefaultRecordingSaveDirectory() {
|
|||
}
|
||||
|
||||
void RecordingScriptingInterface::saveRecording(const QString& filename) {
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
if (!_lastClip) {
|
||||
qWarning() << "There is no recording to save";
|
||||
return;
|
||||
|
@ -251,7 +253,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(const ScriptValue& getCli
|
|||
return false;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
if (!_lastClip) {
|
||||
qWarning() << "There is no recording to save";
|
||||
return false;
|
||||
|
@ -293,7 +295,7 @@ void RecordingScriptingInterface::loadLastRecording() {
|
|||
return;
|
||||
}
|
||||
|
||||
Locker(_mutex);
|
||||
Locker lock(_mutex);
|
||||
|
||||
if (!_lastClip) {
|
||||
qCDebug(scriptengine) << "There is no recording to load";
|
||||
|
|
|
@ -357,7 +357,7 @@ protected:
|
|||
using Locker = std::unique_lock<Mutex>;
|
||||
using Flag = std::atomic<bool>;
|
||||
|
||||
Mutex _mutex;
|
||||
mutable Mutex _mutex;
|
||||
|
||||
QSharedPointer<recording::Deck> _player;
|
||||
QSharedPointer<recording::Recorder> _recorder;
|
||||
|
|
93
libraries/render-utils/res/fonts/CourierPrime-OFL.txt
Normal file
93
libraries/render-utils/res/fonts/CourierPrime-OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright 2015 The Courier Prime Project Authors (https://github.com/quoteunquoteapps/CourierPrime).
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
libraries/render-utils/res/fonts/CourierPrime.arfont
Normal file
BIN
libraries/render-utils/res/fonts/CourierPrime.arfont
Normal file
Binary file not shown.
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
|
||||
SIL Open Font License v1.10
|
||||
|
||||
This license can also be found at this permalink: http://www.fontsquirrel.com/license/courier-prime
|
||||
|
||||
Copyright (c) 2013, Quote-Unquote Apps (http://quoteunquoteapps.com),
|
||||
with Reserved Font Name Courier Prime.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||
|
||||
—————————————————————————————-
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
—————————————————————————————-
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||
|
||||
“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
|
||||
|
||||
“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||
|
||||
“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||
|
||||
“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
*/
|
Binary file not shown.
93
libraries/render-utils/res/fonts/Inconsolata-OFL.txt
Normal file
93
libraries/render-utils/res/fonts/Inconsolata-OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright 2006 The Inconsolata Project Authors
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
libraries/render-utils/res/fonts/InconsolataMedium.arfont
Normal file
BIN
libraries/render-utils/res/fonts/InconsolataMedium.arfont
Normal file
Binary file not shown.
Binary file not shown.
202
libraries/render-utils/res/fonts/Roboto-LICENSE.txt
Normal file
202
libraries/render-utils/res/fonts/Roboto-LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
BIN
libraries/render-utils/res/fonts/Roboto.arfont
Normal file
BIN
libraries/render-utils/res/fonts/Roboto.arfont
Normal file
Binary file not shown.
Binary file not shown.
BIN
libraries/render-utils/res/fonts/Timeless.arfont
Normal file
BIN
libraries/render-utils/res/fonts/Timeless.arfont
Normal file
Binary file not shown.
Binary file not shown.
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource>
|
||||
<file>CourierPrime.sdff</file>
|
||||
<file>InconsolataMedium.sdff</file>
|
||||
<file>Roboto.sdff</file>
|
||||
<file>Timeless.sdff</file>
|
||||
<file>CourierPrime.arfont</file>
|
||||
<file>InconsolataMedium.arfont</file>
|
||||
<file>Roboto.arfont</file>
|
||||
<file>Timeless.arfont</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -1802,8 +1802,8 @@ public:
|
|||
};
|
||||
|
||||
static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) {
|
||||
float len = glm::compMax(glm::abs(unpacked.positionOffset));
|
||||
glm::vec3 normalizedPos(unpacked.positionOffset);
|
||||
float len = max(abs(unpacked.positionOffsetX), max(abs(unpacked.positionOffsetY), abs(unpacked.positionOffsetZ)));
|
||||
glm::vec3 normalizedPos(unpacked.positionOffsetX, unpacked.positionOffsetY, unpacked.positionOffsetZ);
|
||||
if (len > 0.0f) {
|
||||
normalizedPos /= len;
|
||||
} else {
|
||||
|
@ -1813,8 +1813,8 @@ static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uve
|
|||
packed = glm::uvec4(
|
||||
glm::floatBitsToUint(len),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(normalizedPos, 0.0f)),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(unpacked.normalOffset, 0.0f)),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(unpacked.tangentOffset, 0.0f))
|
||||
glm_packSnorm3x10_1x2(glm::vec4(unpacked.normalOffsetX, unpacked.normalOffsetY, unpacked.normalOffsetZ, 0.0f)),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(unpacked.tangentOffsetX, unpacked.tangentOffsetY, unpacked.tangentOffsetZ, 0.0f))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1922,10 +1922,19 @@ void Blender::run() {
|
|||
int index = blendshape.indices.at(j);
|
||||
|
||||
auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index];
|
||||
currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient;
|
||||
currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient;
|
||||
glm::vec3 blendshapePosition = blendshape.vertices.at(j) * vertexCoefficient;
|
||||
currentBlendshapeOffset.positionOffsetX += blendshapePosition.x;
|
||||
currentBlendshapeOffset.positionOffsetY += blendshapePosition.y;
|
||||
currentBlendshapeOffset.positionOffsetZ += blendshapePosition.z;
|
||||
glm::vec3 blendshapeNormal = blendshape.normals.at(j) * normalCoefficient;
|
||||
currentBlendshapeOffset.normalOffsetX += blendshapeNormal.x;
|
||||
currentBlendshapeOffset.normalOffsetY += blendshapeNormal.y;
|
||||
currentBlendshapeOffset.normalOffsetZ += blendshapeNormal.z;
|
||||
if (j < blendshape.tangents.size()) {
|
||||
currentBlendshapeOffset.tangentOffset += blendshape.tangents.at(j) * normalCoefficient;
|
||||
glm::vec3 blendshapeTangent = blendshape.tangents.at(j) * normalCoefficient;
|
||||
currentBlendshapeOffset.tangentOffsetX += blendshapeTangent.x;
|
||||
currentBlendshapeOffset.tangentOffsetY += blendshapeTangent.y;
|
||||
currentBlendshapeOffset.tangentOffsetZ += blendshapeTangent.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<@include sdf_text3D.slh@>
|
||||
<$declareEvalSDFSuperSampled()$>
|
||||
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec2 _positionMS;
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
|
||||
<@endif@>
|
||||
|
@ -42,7 +43,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
|
|||
layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reusing the fade texcoord locations here
|
||||
|
||||
void main() {
|
||||
vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds);
|
||||
vec4 color = evalSDFSuperSampled(_texCoord0, _positionMS, _glyphBounds);
|
||||
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
color.a *= params.color.a;
|
||||
|
|
|
@ -17,13 +17,16 @@
|
|||
LAYOUT(binding=0) uniform sampler2D fontTexture;
|
||||
|
||||
struct TextParams {
|
||||
vec4 bounds;
|
||||
vec4 color;
|
||||
|
||||
vec3 effectColor;
|
||||
float effectThickness;
|
||||
vec2 unitRange;
|
||||
|
||||
int effect;
|
||||
vec3 spare;
|
||||
float effectThickness;
|
||||
|
||||
vec3 effectColor;
|
||||
float spare;
|
||||
};
|
||||
|
||||
LAYOUT(binding=0) uniform textParamsBuffer {
|
||||
|
@ -37,51 +40,75 @@ LAYOUT(binding=0) uniform textParamsBuffer {
|
|||
const float interiorCutoff = 0.5;
|
||||
const float taaBias = pow(2.0, TAA_TEXTURE_LOD_BIAS);
|
||||
|
||||
vec4 evalSDF(vec2 texCoord, vec4 glyphBounds) {
|
||||
// MSDF logic from: https://github.com/Chlumsky/msdfgen?tab=readme-ov-file#using-a-multi-channel-distance-field
|
||||
float median(float r, float g, float b) {
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
float screenPxRange(vec2 texCoord) {
|
||||
vec2 screenTexSize = vec2(1.0) / fwidth(texCoord);
|
||||
return max(0.5 * dot(params.unitRange, screenTexSize), 1.0);
|
||||
}
|
||||
|
||||
vec2 evalSDF(vec2 texCoord) {
|
||||
vec4 msdf = textureLod(fontTexture, texCoord, TAA_TEXTURE_LOD_BIAS);
|
||||
float sdf = median(msdf.r, msdf.g, msdf.b);
|
||||
float screenPxDistance = screenPxRange(texCoord) * (sdf - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
return vec2(opacity, msdf.a);
|
||||
}
|
||||
|
||||
vec4 evalSDFColor(vec2 texCoord, vec4 glyphBounds) {
|
||||
vec3 color = params.color.rgb;
|
||||
float sdf = textureLod(fontTexture, texCoord, TAA_TEXTURE_LOD_BIAS).g;
|
||||
vec2 sdf = evalSDF(texCoord);
|
||||
|
||||
// Outline
|
||||
if (params.effect == 1 || params.effect == 2) {
|
||||
float outline = float(sdf < interiorCutoff);
|
||||
float outline = float(sdf.x < interiorCutoff);
|
||||
color = mix(color, params.effectColor, outline);
|
||||
|
||||
// with or without fill
|
||||
sdf = mix(sdf, 0.0, float(params.effect == 1) * (1.0 - outline));
|
||||
sdf.x = mix(sdf.y, 0.0, float(params.effect == 1) * (1.0 - outline));
|
||||
|
||||
const float EPSILON = 0.00001;
|
||||
sdf += mix(0.0, params.effectThickness - EPSILON, outline);
|
||||
sdf.x += mix(0.0, params.effectThickness - EPSILON, outline);
|
||||
} else if (params.effect == 3) { // Shadow
|
||||
// don't sample from outside of our glyph bounds
|
||||
sdf *= mix(1.0, 0.0, float(clamp(texCoord, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != texCoord));
|
||||
sdf.x *= mix(1.0, 0.0, float(clamp(texCoord, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != texCoord));
|
||||
|
||||
if (sdf < interiorCutoff) {
|
||||
if (sdf.x < interiorCutoff) {
|
||||
color = params.effectColor;
|
||||
const float DOUBLE_MAX_OFFSET_PIXELS = 20.0; // must match value in Font.cpp
|
||||
// FIXME: TAA_TEXTURE_LOD_BIAS doesn't have any effect because we're only generating one mip, so here we need to use 0, but it should
|
||||
// really match the LOD that we use in the textureLod call below
|
||||
vec2 textureOffset = vec2(params.effectThickness * DOUBLE_MAX_OFFSET_PIXELS) / vec2(textureSize(fontTexture, 0/*int(TAA_TEXTURE_LOD_BIAS)*/));
|
||||
vec2 shadowTexCoords = texCoord - textureOffset;
|
||||
sdf = textureLod(fontTexture, shadowTexCoords, TAA_TEXTURE_LOD_BIAS).g;
|
||||
sdf.x = evalSDF(shadowTexCoords).x;
|
||||
|
||||
// don't sample from outside of our glyph bounds
|
||||
sdf *= mix(1.0, 0.0, float(clamp(shadowTexCoords, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != shadowTexCoords));
|
||||
sdf.x *= mix(1.0, 0.0, float(clamp(shadowTexCoords, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != shadowTexCoords));
|
||||
}
|
||||
}
|
||||
|
||||
return vec4(color, sdf);
|
||||
return vec4(color, sdf.x);
|
||||
}
|
||||
|
||||
vec4 evalSDFSuperSampled(vec2 texCoord, vec4 glyphBounds) {
|
||||
vec4 evalSDFSuperSampled(vec2 texCoord, vec2 positionMS, vec4 glyphBounds) {
|
||||
// Clip to edges. Note: We don't need to check the top edge.
|
||||
if (positionMS.x < params.bounds.x || positionMS.x > (params.bounds.x + params.bounds.z) ||
|
||||
positionMS.y < params.bounds.y - params.bounds.w) {
|
||||
return vec4(0.0);
|
||||
}
|
||||
|
||||
vec2 dxTexCoord = dFdx(texCoord) * 0.5 * taaBias;
|
||||
vec2 dyTexCoord = dFdy(texCoord) * 0.5 * taaBias;
|
||||
|
||||
// Perform 4x supersampling for anisotropic filtering
|
||||
vec4 color;
|
||||
color = evalSDF(texCoord, glyphBounds);
|
||||
color += evalSDF(texCoord + dxTexCoord, glyphBounds);
|
||||
color += evalSDF(texCoord + dyTexCoord, glyphBounds);
|
||||
color += evalSDF(texCoord + dxTexCoord + dyTexCoord, glyphBounds);
|
||||
color = evalSDFColor(texCoord, glyphBounds);
|
||||
color += evalSDFColor(texCoord + dxTexCoord, glyphBounds);
|
||||
color += evalSDFColor(texCoord + dyTexCoord, glyphBounds);
|
||||
color += evalSDFColor(texCoord + dxTexCoord + dyTexCoord, glyphBounds);
|
||||
color *= 0.25;
|
||||
|
||||
return vec4(color.rgb, step(interiorCutoff, color.a));
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
<@include sdf_text3D.slh@>
|
||||
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_MS) out vec2 _positionMS;
|
||||
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES;
|
||||
<@endif@>
|
||||
|
@ -27,6 +28,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
|
|||
layout(location=RENDER_UTILS_ATTR_FADE1) flat out vec4 _glyphBounds; // we're reusing the fade texcoord locations here
|
||||
|
||||
void main() {
|
||||
_positionMS = inPosition.xy;
|
||||
_texCoord01 = vec4(inTexCoord0.st, 0.0, 0.0);
|
||||
_glyphBounds = inTexCoord1;
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QNetworkReply>
|
||||
#include <QThreadStorage>
|
||||
|
||||
#include "artery-font/artery-font.h"
|
||||
#include "artery-font/std-artery-font.h"
|
||||
|
||||
#include <ColorUtils.h>
|
||||
|
||||
|
@ -77,12 +81,150 @@ struct QuadBuilder {
|
|||
}
|
||||
};
|
||||
|
||||
Font::Pointer Font::load(QIODevice& fontFile) {
|
||||
Pointer font = std::make_shared<Font>();
|
||||
Font::Pointer Font::load(const QString& family, QIODevice& fontFile) {
|
||||
Pointer font = std::make_shared<Font>(family);
|
||||
font->read(fontFile);
|
||||
return font;
|
||||
}
|
||||
|
||||
void Font::handleFontNetworkReply() {
|
||||
auto requestReply = qobject_cast<QNetworkReply*>(sender());
|
||||
Q_ASSERT(requestReply != nullptr);
|
||||
|
||||
if (requestReply->error() == QNetworkReply::NoError) {
|
||||
read(*requestReply);
|
||||
} else {
|
||||
qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
QThreadStorage<size_t> _readOffset;
|
||||
QThreadStorage<size_t> _readMax;
|
||||
int readHelper(void* dst, int length, void* data) {
|
||||
if (_readOffset.localData() + length > _readMax.localData()) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(dst, (char *)data + _readOffset.localData(), length);
|
||||
_readOffset.setLocalData(_readOffset.localData() + length);
|
||||
return length;
|
||||
};
|
||||
|
||||
void Font::read(QIODevice& in) {
|
||||
_loaded = false;
|
||||
|
||||
QByteArray data = in.readAll();
|
||||
_readOffset.setLocalData(0);
|
||||
_readMax.setLocalData(data.length());
|
||||
artery_font::StdArteryFont<float> arteryFont;
|
||||
bool success = artery_font::decode<&readHelper, float, artery_font::StdList, artery_font::StdByteArray, artery_font::StdString>(arteryFont, (void *)data.data());
|
||||
|
||||
if (!success) {
|
||||
qDebug() << "Font" << _family << "failed to decode.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (arteryFont.variants.length() == 0) {
|
||||
qDebug() << "Font" << _family << "has 0 variants.";
|
||||
return;
|
||||
}
|
||||
|
||||
_distanceRange = glm::vec2(arteryFont.variants[0].metrics.distanceRange);
|
||||
_fontSize = arteryFont.variants[0].metrics.ascender + fabs(arteryFont.variants[0].metrics.descender);
|
||||
_leading = arteryFont.variants[0].metrics.lineHeight;
|
||||
_spaceWidth = 0.5f * arteryFont.variants[0].metrics.emSize; // We use half the emSize as a first guess for _spaceWidth
|
||||
|
||||
if (arteryFont.variants[0].glyphs.length() == 0) {
|
||||
qDebug() << "Font" << _family << "has 0 glyphs.";
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Glyph> glyphs;
|
||||
glyphs.reserve(arteryFont.variants[0].glyphs.length());
|
||||
for (int i = 0; i < arteryFont.variants[0].glyphs.length(); i++) {
|
||||
auto& g = arteryFont.variants[0].glyphs[i];
|
||||
|
||||
Glyph glyph;
|
||||
glyph.c = g.codepoint;
|
||||
glyph.texOffset = glm::vec2(g.imageBounds.l, g.imageBounds.b);
|
||||
glyph.texSize = glm::vec2(g.imageBounds.r, g.imageBounds.t) - glyph.texOffset;
|
||||
glyph.offset = glm::vec2(g.planeBounds.l, g.planeBounds.b);
|
||||
glyph.size = glm::vec2(g.planeBounds.r, g.planeBounds.t) - glyph.offset;
|
||||
glyph.d = g.advance.h;
|
||||
glyphs.push_back(glyph);
|
||||
|
||||
// If we find the space character, we save its size in _spaceWidth for later
|
||||
if (glyph.c == ' ') {
|
||||
_spaceWidth = glyph.d;
|
||||
}
|
||||
}
|
||||
|
||||
if (arteryFont.images.length() == 0) {
|
||||
qDebug() << "Font" << _family << "has 0 images.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (arteryFont.images[0].imageType != artery_font::ImageType::IMAGE_MTSDF) {
|
||||
qDebug() << "Font" << _family << "has the wrong image type. Expected MTSDF (7), got" << arteryFont.images[0].imageType;
|
||||
return;
|
||||
}
|
||||
|
||||
if (arteryFont.images[0].encoding != artery_font::ImageEncoding::IMAGE_PNG) {
|
||||
qDebug() << "Font" << _family << "has the wrong encoding. Expected PNG (8), got" << arteryFont.images[0].encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
if (arteryFont.images[0].pixelFormat != artery_font::PixelFormat::PIXEL_UNSIGNED8) {
|
||||
qDebug() << "Font" << _family << "has the wrong pixel format. Expected unsigned char (8), got" << arteryFont.images[0].pixelFormat;
|
||||
return;
|
||||
}
|
||||
|
||||
if (arteryFont.images[0].width == 0 || arteryFont.images[0].height == 0) {
|
||||
qDebug() << "Font" << _family << "has image with width or height of 0. Width:" << arteryFont.images[0].width << ", height:"<< arteryFont.images[0].height;
|
||||
return;
|
||||
}
|
||||
|
||||
// read image data
|
||||
QImage image;
|
||||
if (!image.loadFromData((const unsigned char*)arteryFont.images[0].data, arteryFont.images[0].data.length(), "PNG")) {
|
||||
qDebug() << "Failed to read image for font" << _family;
|
||||
return;
|
||||
}
|
||||
|
||||
_glyphs.clear();
|
||||
glm::vec2 imageSize = toGlm(image.size());
|
||||
_distanceRange /= imageSize;
|
||||
foreach(Glyph g, glyphs) {
|
||||
// Adjust the pixel texture coordinates into UV coordinates,
|
||||
g.texSize /= imageSize;
|
||||
g.texOffset /= imageSize;
|
||||
// Y flip
|
||||
g.texOffset.y = 1.0f - (g.texOffset.y + g.texSize.y);
|
||||
g.offset.y = -(1.0f - (g.offset.y + g.size.y));
|
||||
// store in the character to glyph hash
|
||||
_glyphs[g.c] = g;
|
||||
};
|
||||
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
if (image.hasAlphaChannel()) {
|
||||
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA);
|
||||
}
|
||||
// FIXME: We're forcing this to use only one mip, and then manually doing anisotropic filtering in the shader,
|
||||
// and also calling textureLod. Shouldn't this just use anisotropic filtering and auto-generate mips?
|
||||
// We should also use smoothstep for anti-aliasing, as explained here: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
|
||||
_texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
|
||||
_texture->setStoredMipFormat(formatMip);
|
||||
_texture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
|
||||
_texture->setImportant(true);
|
||||
|
||||
_loaded = true;
|
||||
_needsParamsUpdate = true;
|
||||
}
|
||||
|
||||
static QHash<QString, Font::Pointer> LOADED_FONTS;
|
||||
|
||||
Font::Pointer Font::load(const QString& family) {
|
||||
|
@ -91,16 +233,15 @@ Font::Pointer Font::load(const QString& family) {
|
|||
QString loadFilename;
|
||||
|
||||
if (family == ROBOTO_FONT_FAMILY) {
|
||||
loadFilename = ":/Roboto.sdff";
|
||||
loadFilename = ":/Roboto.arfont";
|
||||
} else if (family == INCONSOLATA_FONT_FAMILY) {
|
||||
loadFilename = ":/InconsolataMedium.sdff";
|
||||
loadFilename = ":/InconsolataMedium.arfont";
|
||||
} else if (family == COURIER_FONT_FAMILY) {
|
||||
loadFilename = ":/CourierPrime.sdff";
|
||||
loadFilename = ":/CourierPrime.arfont";
|
||||
} else if (family == TIMELESS_FONT_FAMILY) {
|
||||
loadFilename = ":/Timeless.sdff";
|
||||
loadFilename = ":/Timeless.arfont";
|
||||
} else if (family.startsWith("http")) {
|
||||
auto loadingFont = std::make_shared<Font>();
|
||||
loadingFont->setLoaded(false);
|
||||
auto loadingFont = std::make_shared<Font>(family);
|
||||
LOADED_FONTS[family] = loadingFont;
|
||||
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
@ -114,7 +255,7 @@ Font::Pointer Font::load(const QString& family) {
|
|||
connect(networkReply, &QNetworkReply::finished, loadingFont.get(), &Font::handleFontNetworkReply);
|
||||
} else if (!LOADED_FONTS.contains(ROBOTO_FONT_FAMILY)) {
|
||||
// Unrecognized font and we haven't loaded Roboto yet
|
||||
loadFilename = ":/Roboto.sdff";
|
||||
loadFilename = ":/Roboto.arfont";
|
||||
} else {
|
||||
// Unrecognized font but we've already loaded Roboto
|
||||
LOADED_FONTS[family] = LOADED_FONTS[ROBOTO_FONT_FAMILY];
|
||||
|
@ -126,25 +267,13 @@ Font::Pointer Font::load(const QString& family) {
|
|||
|
||||
qCDebug(renderutils) << "Loaded font" << loadFilename << "from Qt Resource System.";
|
||||
|
||||
LOADED_FONTS[family] = load(fontFile);
|
||||
LOADED_FONTS[family] = load(family, fontFile);
|
||||
}
|
||||
}
|
||||
return LOADED_FONTS[family];
|
||||
}
|
||||
|
||||
void Font::handleFontNetworkReply() {
|
||||
auto requestReply = qobject_cast<QNetworkReply*>(sender());
|
||||
Q_ASSERT(requestReply != nullptr);
|
||||
|
||||
if (requestReply->error() == QNetworkReply::NoError) {
|
||||
setLoaded(true);
|
||||
read(*requestReply);
|
||||
} else {
|
||||
qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
Font::Font() {
|
||||
Font::Font(const QString& family) : _family(family) {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, []{
|
||||
Q_INIT_RESOURCE(fonts);
|
||||
|
@ -197,82 +326,6 @@ glm::vec2 Font::computeExtent(const QString& str) const {
|
|||
return extent;
|
||||
}
|
||||
|
||||
void Font::read(QIODevice& in) {
|
||||
uint8_t header[4];
|
||||
readStream(in, header);
|
||||
if (memcmp(header, "SDFF", 4)) {
|
||||
qDebug() << "Bad SDFF file";
|
||||
_loaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t version;
|
||||
readStream(in, version);
|
||||
|
||||
// read font name
|
||||
_family = "";
|
||||
if (version > 0x0001) {
|
||||
char c;
|
||||
readStream(in, c);
|
||||
while (c) {
|
||||
_family += c;
|
||||
readStream(in, c);
|
||||
}
|
||||
}
|
||||
|
||||
// read font data
|
||||
readStream(in, _leading);
|
||||
readStream(in, _ascent);
|
||||
readStream(in, _descent);
|
||||
readStream(in, _spaceWidth);
|
||||
_fontSize = _ascent + _descent;
|
||||
|
||||
// Read character count
|
||||
uint16_t count;
|
||||
readStream(in, count);
|
||||
// read metrics data for each character
|
||||
QVector<Glyph> glyphs(count);
|
||||
// std::for_each instead of Qt foreach because we need non-const references
|
||||
std::for_each(glyphs.begin(), glyphs.end(), [&](Glyph& g) {
|
||||
g.read(in);
|
||||
});
|
||||
|
||||
// read image data
|
||||
QImage image;
|
||||
if (!image.loadFromData(in.readAll(), "PNG")) {
|
||||
qDebug() << "Failed to read SDFF image";
|
||||
_loaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_glyphs.clear();
|
||||
glm::vec2 imageSize = toGlm(image.size());
|
||||
foreach(Glyph g, glyphs) {
|
||||
// Adjust the pixel texture coordinates into UV coordinates,
|
||||
g.texSize /= imageSize;
|
||||
g.texOffset /= imageSize;
|
||||
// store in the character to glyph hash
|
||||
_glyphs[g.c] = g;
|
||||
};
|
||||
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
if (image.hasAlphaChannel()) {
|
||||
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA);
|
||||
}
|
||||
// FIXME: We're forcing this to use only one mip, and then manually doing anisotropic filtering in the shader,
|
||||
// and also calling textureLod. Shouldn't this just use anisotropic filtering and auto-generate mips?
|
||||
// We should also use smoothstep for anti-aliasing, as explained here: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
|
||||
_texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
|
||||
_texture->setStoredMipFormat(formatMip);
|
||||
_texture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
|
||||
_texture->setImportant(true);
|
||||
}
|
||||
|
||||
void Font::setupGPU() {
|
||||
if (_pipelines.empty()) {
|
||||
using namespace shader::render_utils::program;
|
||||
|
@ -334,13 +387,21 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
drawInfo.bounds = bounds;
|
||||
drawInfo.origin = origin;
|
||||
|
||||
float enlargedBoundsX = bounds.x - 0.5f * DOUBLE_MAX_OFFSET_PIXELS * float(enlargeForShadows);
|
||||
float rightEdge = origin.x + enlargedBoundsX;
|
||||
float rightEdge = origin.x + bounds.x;
|
||||
|
||||
// Top left of text
|
||||
bool firstTokenOfLine = true;
|
||||
glm::vec2 advance = origin;
|
||||
std::vector<std::pair<Glyph, vec2>> glyphsAndCorners;
|
||||
foreach(const QString& token, tokenizeForWrapping(str)) {
|
||||
const QStringList tokens = tokenizeForWrapping(str);
|
||||
for (int i = 0; i < tokens.length(); i++) {
|
||||
const QString& token = tokens[i];
|
||||
|
||||
if ((bounds.y != -1) && (advance.y < origin.y - bounds.y)) {
|
||||
// We are out of the y bound, stop drawing
|
||||
break;
|
||||
}
|
||||
|
||||
bool isNewLine = (token == QString('\n'));
|
||||
bool forceNewLine = false;
|
||||
|
||||
|
@ -349,43 +410,50 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
// We are out of the x bound, force new line
|
||||
forceNewLine = true;
|
||||
}
|
||||
if (isNewLine || forceNewLine) {
|
||||
|
||||
if (isNewLine || (forceNewLine && !firstTokenOfLine)) {
|
||||
if (forceNewLine && !firstTokenOfLine) {
|
||||
// We want to try this token again on the new line
|
||||
i--;
|
||||
}
|
||||
|
||||
// Character return, move the advance to a new line
|
||||
advance = glm::vec2(origin.x, advance.y - _leading);
|
||||
|
||||
if (isNewLine) {
|
||||
// No need to draw anything, go directly to next token
|
||||
continue;
|
||||
} else if (computeExtent(token).x > enlargedBoundsX) {
|
||||
// token will never fit, stop drawing
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((bounds.y != -1) && (advance.y - _fontSize < origin.y - bounds.y)) {
|
||||
// We are out of the y bound, stop drawing
|
||||
break;
|
||||
firstTokenOfLine = true;
|
||||
// No need to draw anything, go directly to next token
|
||||
continue;
|
||||
}
|
||||
|
||||
// Draw the token
|
||||
if (!isNewLine) {
|
||||
for (auto c : token) {
|
||||
auto glyph = _glyphs[c];
|
||||
|
||||
glyphsAndCorners.emplace_back(glyph, advance - glm::vec2(0.0f, _ascent));
|
||||
|
||||
// Advance by glyph size
|
||||
advance.x += glyph.d;
|
||||
for (const QChar& c : token) {
|
||||
if (advance.x > rightEdge) {
|
||||
break;
|
||||
}
|
||||
const Glyph& glyph = _glyphs[c];
|
||||
|
||||
glyphsAndCorners.emplace_back(glyph, advance);
|
||||
|
||||
// Advance by glyph size
|
||||
advance.x += glyph.d;
|
||||
}
|
||||
|
||||
if (forceNewLine && firstTokenOfLine) {
|
||||
// If the first word of a line didn't fit, we draw as many characters as we could, now go to the next line
|
||||
// Character return, move the advance to a new line
|
||||
advance = glm::vec2(origin.x, advance.y - _leading);
|
||||
firstTokenOfLine = true;
|
||||
} else {
|
||||
// Add space after all non return tokens
|
||||
advance.x += _spaceWidth;
|
||||
// Our token fits in the x direction! Any subsequent tokens won't be the first for this line.
|
||||
firstTokenOfLine = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<QuadBuilder> quadBuilders;
|
||||
quadBuilders.reserve(glyphsAndCorners.size());
|
||||
{
|
||||
int i = glyphsAndCorners.size() - 1;
|
||||
int i = (int)glyphsAndCorners.size() - 1;
|
||||
while (i >= 0) {
|
||||
auto nextGlyphAndCorner = glyphsAndCorners[i];
|
||||
float rightSpacing = rightEdge - (nextGlyphAndCorner.second.x + nextGlyphAndCorner.first.d);
|
||||
|
@ -393,7 +461,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
alignment, rightSpacing));
|
||||
i--;
|
||||
while (i >= 0) {
|
||||
auto prevGlyphAndCorner = glyphsAndCorners[i];
|
||||
const auto& prevGlyphAndCorner = glyphsAndCorners[i];
|
||||
// We're to the right of the last character we checked, which means we're on a previous line, so we need to
|
||||
// recalculate the spacing, so we exit this loop
|
||||
if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner.second.x) {
|
||||
|
@ -410,7 +478,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
|
|||
}
|
||||
|
||||
// The quadBuilders is backwards now because we looped over the glyphs backwards to adjust their alignment
|
||||
for (int i = quadBuilders.size() - 1; i >= 0; i--) {
|
||||
for (int i = (int)quadBuilders.size() - 1; i >= 0; i--) {
|
||||
quint16 verticesOffset = numVertices;
|
||||
drawInfo.verticesBuffer->append(quadBuilders[i]);
|
||||
numVertices += VERTICES_PER_QUAD;
|
||||
|
@ -454,8 +522,10 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
|
|||
int textEffect = (int)effect;
|
||||
const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT;
|
||||
|
||||
const bool boundsChanged = bounds != drawInfo.bounds || origin != drawInfo.origin;
|
||||
|
||||
// If we're switching to or from shadow effect mode, we need to rebuild the vertices
|
||||
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin || alignment != _alignment ||
|
||||
if (str != drawInfo.string || boundsChanged || alignment != _alignment ||
|
||||
(drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) ||
|
||||
(textEffect == SHADOW_EFFECT && scale != _scale)) {
|
||||
_scale = scale;
|
||||
|
@ -465,8 +535,9 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
|
|||
|
||||
setupGPU();
|
||||
|
||||
if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor ||
|
||||
drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) {
|
||||
if (!drawInfo.paramsBuffer || boundsChanged || _needsParamsUpdate || drawInfo.params.color != color ||
|
||||
drawInfo.params.effectColor != effectColor || drawInfo.params.effectThickness != effectThickness ||
|
||||
drawInfo.params.effect != textEffect) {
|
||||
drawInfo.params.color = color;
|
||||
drawInfo.params.effectColor = effectColor;
|
||||
drawInfo.params.effectThickness = effectThickness;
|
||||
|
@ -474,14 +545,18 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
|
|||
|
||||
// need the gamma corrected color here
|
||||
DrawParams gpuDrawParams;
|
||||
gpuDrawParams.bounds = glm::vec4(origin, bounds);
|
||||
gpuDrawParams.color = ColorUtils::sRGBToLinearVec4(drawInfo.params.color);
|
||||
gpuDrawParams.effectColor = ColorUtils::sRGBToLinearVec3(drawInfo.params.effectColor);
|
||||
gpuDrawParams.effectThickness = drawInfo.params.effectThickness;
|
||||
gpuDrawParams.unitRange = _distanceRange;
|
||||
gpuDrawParams.effect = drawInfo.params.effect;
|
||||
gpuDrawParams.effectThickness = drawInfo.params.effectThickness;
|
||||
gpuDrawParams.effectColor = ColorUtils::sRGBToLinearVec3(drawInfo.params.effectColor);
|
||||
if (!drawInfo.paramsBuffer) {
|
||||
drawInfo.paramsBuffer = std::make_shared<gpu::Buffer>(sizeof(DrawParams), nullptr);
|
||||
}
|
||||
drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams);
|
||||
|
||||
_needsParamsUpdate = false;
|
||||
}
|
||||
|
||||
batch.setPipeline(_pipelines[std::make_tuple(color.a < 1.0f, unlit, forward)]);
|
||||
|
|
|
@ -24,22 +24,25 @@ class Font : public QObject {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<Font>;
|
||||
|
||||
Font();
|
||||
Font(const QString& family);
|
||||
|
||||
void read(QIODevice& path);
|
||||
|
||||
struct DrawParams {
|
||||
vec4 color { 0 };
|
||||
vec4 bounds { 0.0f };
|
||||
vec4 color { 0.0f };
|
||||
|
||||
vec3 effectColor { 0 };
|
||||
float effectThickness { 0 };
|
||||
vec2 unitRange { 1.0f };
|
||||
|
||||
int effect { 0 };
|
||||
float effectThickness { 0.0f };
|
||||
|
||||
vec3 effectColor { 0.0f };
|
||||
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
vec3 _spare;
|
||||
float _spare;
|
||||
};
|
||||
|
||||
struct DrawInfo {
|
||||
|
@ -65,13 +68,12 @@ public:
|
|||
static Pointer load(const QString& family);
|
||||
|
||||
bool isLoaded() const { return _loaded; }
|
||||
void setLoaded(bool loaded) { _loaded = loaded; }
|
||||
|
||||
public slots:
|
||||
void handleFontNetworkReply();
|
||||
|
||||
private:
|
||||
static Pointer load(QIODevice& fontFile);
|
||||
static Pointer load(const QString& family, QIODevice& fontFile);
|
||||
QStringList tokenizeForWrapping(const QString& str) const;
|
||||
QStringList splitLines(const QString& str) const;
|
||||
glm::vec2 computeTokenExtent(const QString& str) const;
|
||||
|
@ -91,16 +93,16 @@ private:
|
|||
|
||||
// Font characteristics
|
||||
QString _family;
|
||||
glm::vec2 _distanceRange { 1.0f };
|
||||
float _fontSize { 0.0f };
|
||||
float _leading { 0.0f };
|
||||
float _ascent { 0.0f };
|
||||
float _descent { 0.0f };
|
||||
float _spaceWidth { 0.0f };
|
||||
|
||||
float _scale { 0.0f };
|
||||
TextAlignment _alignment;
|
||||
TextAlignment _alignment { TextAlignment::LEFT };
|
||||
|
||||
bool _loaded { true };
|
||||
bool _loaded { false };
|
||||
bool _needsParamsUpdate { false };
|
||||
|
||||
gpu::TexturePointer _texture;
|
||||
gpu::BufferStreamPointer _stream;
|
||||
|
|
|
@ -9,15 +9,3 @@ QRectF Glyph::bounds() const {
|
|||
QRectF Glyph::textureBounds() const {
|
||||
return glmToRect(texOffset, texSize);
|
||||
}
|
||||
|
||||
void Glyph::read(QIODevice& in) {
|
||||
uint16_t charcode;
|
||||
readStream(in, charcode);
|
||||
c = charcode;
|
||||
readStream(in, texOffset);
|
||||
readStream(in, size);
|
||||
readStream(in, offset);
|
||||
readStream(in, d);
|
||||
// texSize is divided by the image size later
|
||||
texSize = size;
|
||||
}
|
||||
|
|
|
@ -26,13 +26,10 @@ struct Glyph {
|
|||
vec2 size;
|
||||
vec2 offset;
|
||||
float d; // xadvance - adjusts character positioning
|
||||
size_t indexOffset;
|
||||
|
||||
// We adjust bounds because offset is the bottom left corner of the font but the top left corner of a QRect
|
||||
QRectF bounds() const;
|
||||
QRectF textureBounds() const;
|
||||
|
||||
void read(QIODevice& in);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -139,12 +139,12 @@ void IDsToBounds::run(const RenderContextPointer& renderContext, const ItemIDs&
|
|||
for (auto id : inItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (item.exist()) {
|
||||
outItems.emplace_back(ItemBound{ id, item.getBound(renderContext->args) });
|
||||
outItems.emplace_back(ItemBound(id, item.getBound(renderContext->args)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto id : inItems) {
|
||||
outItems.emplace_back(ItemBound{ id });
|
||||
outItems.emplace_back(ItemBound(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,9 +339,9 @@ class ItemBound {
|
|||
ItemBound(ItemID id) : id(id) { }
|
||||
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
|
||||
|
||||
ItemID id;
|
||||
ItemID id { 0 };
|
||||
AABox bound;
|
||||
uint32_t padding;
|
||||
uint32_t padding { 0 };
|
||||
};
|
||||
|
||||
// many Item Bounds in a vector
|
||||
|
|
|
@ -74,7 +74,7 @@ void AssetScriptingInterface::uploadData(QString data, const ScriptValue& callba
|
|||
auto upload = DependencyManager::get<AssetClient>()->createUpload(dataByteArray);
|
||||
|
||||
Promise deferred = makePromise(__FUNCTION__);
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto scriptEngine = engine();
|
||||
deferred->ready([=](QString error, QVariantMap result) {
|
||||
auto url = result.value("url").toString();
|
||||
|
@ -98,7 +98,7 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, const Scrip
|
|||
auto handler = jsBindCallback(thisObject(), callback);
|
||||
auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash);
|
||||
Promise deferred = makePromise(__FUNCTION__);
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto scriptEngine = engine();
|
||||
deferred->ready([=](QString error, QVariantMap result) {
|
||||
jsCallback(handler, scriptEngine->newValue(error), result);
|
||||
|
@ -136,7 +136,7 @@ void AssetScriptingInterface::downloadData(QString urlString, const ScriptValue&
|
|||
auto assetRequest = assetClient->createRequest(hash);
|
||||
|
||||
Promise deferred = makePromise(__FUNCTION__);
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto scriptEngine = engine();
|
||||
deferred->ready([=](QString error, QVariantMap result) {
|
||||
// FIXME: to remain backwards-compatible the signature here is "callback(data, n/a)"
|
||||
|
@ -200,7 +200,7 @@ void AssetScriptingInterface::getMapping(QString asset, const ScriptValue& callb
|
|||
JS_VERIFY(AssetUtils::isValidFilePath(path), "invalid ATP file path: " + asset + "(path:"+path+")");
|
||||
JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function");
|
||||
Promise promise = getAssetInfo(path);
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto scriptEngine = engine();
|
||||
promise->ready([=](QString error, QVariantMap result) {
|
||||
jsCallback(handler, scriptEngine->newValue(error), scriptEngine->newValue(result.value("hash").toString()));
|
||||
|
@ -234,7 +234,7 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal
|
|||
if (!jsVerify(handler.isValid(), "jsPromiseReady -- invalid callback handler")) {
|
||||
return nullptr;
|
||||
}
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto scriptEngine = engine();
|
||||
return promise->ready([this, handler, scriptEngine](QString error, QVariantMap result) {
|
||||
jsCallback(handler, scriptEngine->newValue(error), result);
|
||||
|
@ -244,7 +244,6 @@ Promise AssetScriptingInterface::jsPromiseReady(Promise promise, const ScriptVal
|
|||
void AssetScriptingInterface::jsCallback(const ScriptValue& handler,
|
||||
const ScriptValue& error, const ScriptValue& result) {
|
||||
Q_ASSERT(thread() == QThread::currentThread());
|
||||
Q_ASSERT(engine);
|
||||
//V8TODO: which kind of script context guard needs to be used here?
|
||||
ScriptContextGuard scriptContextGuard(_scriptManager->engine()->currentContext());
|
||||
auto errorValue = !error.toBool() ? engine()->nullValue() : error;
|
||||
|
@ -546,7 +545,7 @@ void AssetScriptingInterface::loadFromCache(const ScriptValue& options, const Sc
|
|||
}
|
||||
|
||||
bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) {
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
auto scriptManager = engine()->manager();
|
||||
if (!scriptManager) {
|
||||
return false;
|
||||
|
|
|
@ -82,14 +82,14 @@ ScriptValue ConsoleScriptingInterface::exception(ScriptContext* context, ScriptE
|
|||
void ConsoleScriptingInterface::time(QString labelName) {
|
||||
_timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC());
|
||||
QString message = QString("%1: Timer started").arg(labelName);
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
scriptManager->scriptPrintedMessage(message, context()->currentFileName(), context()->currentLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleScriptingInterface::timeEnd(QString labelName) {
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
if (!_timerDetails.contains(labelName)) {
|
||||
scriptManager->scriptErrorMessage("No such label found " + labelName, context()->currentFileName(), context()->currentLineNumber());
|
||||
|
@ -138,7 +138,7 @@ ScriptValue ConsoleScriptingInterface::assertion(ScriptContext* context, ScriptE
|
|||
}
|
||||
|
||||
void ConsoleScriptingInterface::trace() {
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
ScriptEnginePointer scriptEngine = engine();
|
||||
if (ScriptManager* scriptManager = scriptEngine->manager()) {
|
||||
scriptManager->scriptPrintedMessage
|
||||
|
@ -148,7 +148,7 @@ void ConsoleScriptingInterface::trace() {
|
|||
}
|
||||
|
||||
void ConsoleScriptingInterface::clear() {
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
scriptManager->clearDebugLogWindow();
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const
|
|||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
message = message.arg(glm::to_string(out).c_str());
|
||||
qCDebug(scriptengine) << message;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
scriptManager->print(message);
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) {
|
|||
message = message.arg(glm::to_string(glm::dquat(q)).c_str());
|
||||
}
|
||||
qCDebug(scriptengine) << message;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
scriptManager->print(message);
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ signals:
|
|||
* Triggered when a client side entity script prints a message to the program log via {@link print}, {@link Script.print},
|
||||
* {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or
|
||||
* {@link console.timeEnd}.
|
||||
* @function Script.printedMessage
|
||||
* @function Script.printedEntityMessage
|
||||
* @param {string} message - The message.
|
||||
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
||||
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
||||
|
@ -280,7 +280,7 @@ signals:
|
|||
/*@jsdoc
|
||||
* Triggered when a client side entity script generates an error, {@link console.error} or {@link console.exception} is called, or
|
||||
* {@link console.assert} is called and fails.
|
||||
* @function Script.errorMessage
|
||||
* @function Script.errorEntityMessage
|
||||
* @param {string} message - The error message.
|
||||
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
||||
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
||||
|
@ -292,7 +292,7 @@ signals:
|
|||
|
||||
/*@jsdoc
|
||||
* Triggered when a client side entity script generates a warning or {@link console.warn} is called.
|
||||
* @function Script.warningMessage
|
||||
* @function Script.warningEntityMessage
|
||||
* @param {string} message - The warning message.
|
||||
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
||||
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
||||
|
@ -304,7 +304,7 @@ signals:
|
|||
|
||||
/*@jsdoc
|
||||
* Triggered when a client side entity script generates an information message or {@link console.info} is called.
|
||||
* @function Script.infoMessage
|
||||
* @function Script.infoEntityMessage
|
||||
* @param {string} message - The information message.
|
||||
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
||||
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
||||
|
|
|
@ -478,10 +478,10 @@ void ScriptManager::waitTillDoneRunning(bool shutdown) {
|
|||
}
|
||||
}
|
||||
#else
|
||||
auto startedWaiting = usecTimestampNow();
|
||||
//auto startedWaiting = usecTimestampNow();
|
||||
while (!_isDoneRunning) {
|
||||
// If the final evaluation takes too long, then tell the script engine to stop running
|
||||
auto elapsedUsecs = usecTimestampNow() - startedWaiting;
|
||||
//auto elapsedUsecs = usecTimestampNow() - startedWaiting;
|
||||
// TODO: This part was very unsafe and was causing crashes all the time.
|
||||
// I disabled it for now until we find a safer solution.
|
||||
// With it disabled now we get clean shutdowns and restarts.
|
||||
|
@ -589,6 +589,10 @@ void ScriptManager::scriptErrorMessage(const QString& message, const QString& fi
|
|||
emit errorMessage(message, getFilename());
|
||||
if (!currentEntityIdentifier.isInvalidID()) {
|
||||
emit errorEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript());
|
||||
} else {
|
||||
if (isEntityServerScript()) {
|
||||
emit errorEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,6 +601,10 @@ void ScriptManager::scriptWarningMessage(const QString& message, const QString&
|
|||
emit warningMessage(message, getFilename());
|
||||
if (!currentEntityIdentifier.isInvalidID()) {
|
||||
emit warningEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript());
|
||||
} else {
|
||||
if (isEntityServerScript()) {
|
||||
emit warningEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -605,6 +613,10 @@ void ScriptManager::scriptInfoMessage(const QString& message, const QString& fil
|
|||
emit infoMessage(message, getFilename());
|
||||
if (!currentEntityIdentifier.isInvalidID()) {
|
||||
emit infoEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript());
|
||||
} else {
|
||||
if (isEntityServerScript()) {
|
||||
emit infoEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,6 +625,12 @@ void ScriptManager::scriptPrintedMessage(const QString& message, const QString&
|
|||
emit printedMessage(message, getFilename());
|
||||
if (!currentEntityIdentifier.isInvalidID()) {
|
||||
emit printedEntityMessage(message, fileName, lineNumber, currentEntityIdentifier, isEntityServerScript());
|
||||
} else {
|
||||
if (isEntityServerScript()) {
|
||||
// TODO: Some callbacks like for example websockets one right now
|
||||
// don't set currentEntityIdentifier and there doesn't seem to be easy way to add it currently
|
||||
emit printedEntityMessage(message, fileName, lineNumber, EntityItemID(), isEntityServerScript());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2036,7 +2054,7 @@ void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString
|
|||
std::weak_ptr<ScriptManager> weakRef(shared_from_this());
|
||||
scriptCache->getScriptContents(entityScript,
|
||||
[this, weakRef, entityScript, entityID](const QString& url, const QString& contents, bool isURL, bool success, const QString& status) {
|
||||
std::shared_ptr<ScriptManager> strongRef(weakRef);
|
||||
std::shared_ptr<ScriptManager> strongRef = weakRef.lock();
|
||||
if (!strongRef) {
|
||||
qCWarning(scriptengine) << "loadEntityScript.contentAvailable -- ScriptManager was deleted during getScriptContents!!";
|
||||
return;
|
||||
|
|
|
@ -45,7 +45,7 @@ void ScriptUUID::print(const QString& label, const QUuid& id) {
|
|||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
message = message.arg(id.toString());
|
||||
qCDebug(scriptengine) << message;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
scriptManager->print(message);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ void Vec3::print(const QString& label, const glm::vec3& v) {
|
|||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
message = message.arg(glm::to_string(glm::dvec3(v)).c_str());
|
||||
qCDebug(scriptengine) << message;
|
||||
Q_ASSERT(engine);
|
||||
Q_ASSERT(engine());
|
||||
if (ScriptManager* scriptManager = engine()->manager()) {
|
||||
scriptManager->print(message);
|
||||
}
|
||||
|
|
|
@ -33,8 +33,6 @@ class ScriptEngine;
|
|||
* <p>Create using <code>new WebSocket(...)</code> in Interface, client entity, avatar, and server entity scripts, or the
|
||||
* {@link WebSocketServer} class in server entity and assignment client scripts.
|
||||
*
|
||||
* <p><strong>Note:</strong> Does not support secure, <code>wss:</code> protocol.</p>
|
||||
*
|
||||
* @class WebSocket
|
||||
* @param {string|WebSocket} urlOrWebSocket - The URL to connect to or an existing {@link WebSocket} to reuse the connection of.
|
||||
*
|
||||
|
|
|
@ -56,7 +56,7 @@ bool qBytearrayFromScriptValue(const ScriptValue& object, QByteArray &qByteArray
|
|||
return false;
|
||||
}
|
||||
v8::Local<v8::ArrayBuffer> arrayBuffer = v8::Local<v8::ArrayBuffer>::Cast(v8Value);
|
||||
qByteArray.resize(arrayBuffer->ByteLength());
|
||||
qByteArray.resize((int)arrayBuffer->ByteLength());
|
||||
memcpy(qByteArray.data(), arrayBuffer->Data(), arrayBuffer->ByteLength());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -539,7 +539,7 @@ void ScriptEngineV8::storeGlobalObjectContents() {
|
|||
v8::Local<v8::Object> globalMemberObjects = v8::Object::New(_v8Isolate);
|
||||
|
||||
auto globalMemberNames = context->Global()->GetPropertyNames(context).ToLocalChecked();
|
||||
for (size_t i = 0; i < globalMemberNames->Length(); i++) {
|
||||
for (uint32_t i = 0; i < globalMemberNames->Length(); i++) {
|
||||
auto name = globalMemberNames->Get(context, i).ToLocalChecked();
|
||||
if(!globalMemberObjects->Set(context, name, context->Global()->Get(context, name).ToLocalChecked()).FromMaybe(false)) {
|
||||
Q_ASSERT(false);
|
||||
|
@ -645,7 +645,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
|
|||
// Since V8 cannot use arbitrary object as global object, objects from main global need to be copied to closure's global object
|
||||
auto globalObjectContents = _globalObjectContents.Get(_v8Isolate);
|
||||
auto globalMemberNames = globalObjectContents->GetPropertyNames(globalObjectContents->CreationContext()).ToLocalChecked();
|
||||
for (size_t i = 0; i < globalMemberNames->Length(); i++) {
|
||||
for (uint32_t i = 0; i < globalMemberNames->Length(); i++) {
|
||||
auto name = globalMemberNames->Get(closureContext, i).ToLocalChecked();
|
||||
if(!closureContext->Global()->Set(closureContext, name, globalObjectContents->Get(globalObjectContents->CreationContext(), name).ToLocalChecked()).FromMaybe(false)) {
|
||||
Q_ASSERT(false);
|
||||
|
@ -656,7 +656,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
|
|||
// Objects from closure need to be copied to global object too
|
||||
// V8TODO: I'm not sure which context to use with Get
|
||||
auto closureMemberNames = closureObject->GetPropertyNames(closureContext).ToLocalChecked();
|
||||
for (size_t i = 0; i < closureMemberNames->Length(); i++) {
|
||||
for (uint32_t i = 0; i < closureMemberNames->Length(); i++) {
|
||||
auto name = closureMemberNames->Get(closureContext, i).ToLocalChecked();
|
||||
if(!closureContext->Global()->Set(closureContext, name, closureObject->Get(closureContext, name).ToLocalChecked()).FromMaybe(false)) {
|
||||
Q_ASSERT(false);
|
||||
|
@ -717,7 +717,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
|
|||
|
||||
auto requireMemberNames =
|
||||
oldRequireObject->GetPropertyNames(oldRequireObject->CreationContext()).ToLocalChecked();
|
||||
for (size_t i = 0; i < requireMemberNames->Length(); i++) {
|
||||
for (uint32_t i = 0; i < requireMemberNames->Length(); i++) {
|
||||
auto name = requireMemberNames->Get(closureContext, i).ToLocalChecked();
|
||||
v8::Local<v8::Value> oldObject;
|
||||
if (!oldRequireObject->Get(oldRequireObject->CreationContext(), name).ToLocal(&oldObject)) {
|
||||
|
|
|
@ -1309,7 +1309,8 @@ int ScriptSignalV8Proxy::qt_metacall(QMetaObject::Call call, int id, void** argu
|
|||
}
|
||||
|
||||
v8::TryCatch tryCatch(isolate);
|
||||
callback->Call(functionContext, v8This, numArgs, args);
|
||||
auto maybeResult = callback->Call(functionContext, v8This, numArgs, args);
|
||||
Q_UNUSED(maybeResult); // Signals don't have return values
|
||||
if (tryCatch.HasCaught()) {
|
||||
QString errorMessage(QString("Signal proxy ") + fullName() + " connection call failed: \""
|
||||
+ _engine->formatErrorMessageFromTryCatch(tryCatch)
|
||||
|
|
|
@ -119,9 +119,9 @@ struct BlendshapeOffsetPacked {
|
|||
};
|
||||
|
||||
struct BlendshapeOffsetUnpacked {
|
||||
glm::vec3 positionOffset;
|
||||
glm::vec3 normalOffset;
|
||||
glm::vec3 tangentOffset;
|
||||
float positionOffsetX, positionOffsetY, positionOffsetZ;
|
||||
float normalOffsetX, normalOffsetY, normalOffsetZ;
|
||||
float tangentOffsetX, tangentOffsetY, tangentOffsetZ;
|
||||
};
|
||||
|
||||
using BlendshapeOffset = BlendshapeOffsetPacked;
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
// The following struct is not compliant with the HF coding standard, but uses underscores to match the classes
|
||||
|
|
|
@ -205,25 +205,33 @@ public:
|
|||
* @typedef {object} PickRay
|
||||
* @property {Vec3} origin - The starting position of the ray.
|
||||
* @property {Vec3} direction - The direction that the ray travels.
|
||||
* @property {Vec3} unmodifiedDirection - The direction that the ray would travel, if not for applied effects like delays.
|
||||
*/
|
||||
class PickRay : public MathPick {
|
||||
public:
|
||||
PickRay() : origin(NAN), direction(NAN) { }
|
||||
PickRay(const QVariantMap& pickVariant) : origin(vec3FromVariant(pickVariant["origin"])), direction(vec3FromVariant(pickVariant["direction"])) {}
|
||||
PickRay(const glm::vec3& origin, const glm::vec3 direction) : origin(origin), direction(direction) {}
|
||||
PickRay() : origin(NAN), direction(NAN), unmodifiedDirection(NAN) { }
|
||||
PickRay(const QVariantMap& pickVariant) :
|
||||
origin(vec3FromVariant(pickVariant["origin"])), direction(vec3FromVariant(pickVariant["direction"])),
|
||||
unmodifiedDirection(vec3FromVariant(pickVariant["unmodifiedDirection"])) {}
|
||||
PickRay(const glm::vec3& origin, const glm::vec3& direction) :
|
||||
origin(origin), direction(direction), unmodifiedDirection(direction) {}
|
||||
PickRay(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& unmodifiedDirection) :
|
||||
origin(origin), direction(direction), unmodifiedDirection(unmodifiedDirection) {}
|
||||
glm::vec3 origin;
|
||||
glm::vec3 direction;
|
||||
glm::vec3 unmodifiedDirection;
|
||||
|
||||
operator bool() const override {
|
||||
return !(glm::any(glm::isnan(origin)) || glm::any(glm::isnan(direction)));
|
||||
return !(glm::any(glm::isnan(origin)) || glm::any(glm::isnan(direction)) || glm::any(glm::isnan(unmodifiedDirection)));
|
||||
}
|
||||
bool operator==(const PickRay& other) const {
|
||||
return (origin == other.origin && direction == other.direction);
|
||||
return (origin == other.origin && direction == other.direction && unmodifiedDirection == other.unmodifiedDirection);
|
||||
}
|
||||
QVariantMap toVariantMap() const override {
|
||||
QVariantMap pickRay;
|
||||
pickRay["origin"] = vec3toVariant(origin);
|
||||
pickRay["direction"] = vec3toVariant(direction);
|
||||
pickRay["unmodifiedDirection"] = vec3toVariant(unmodifiedDirection);
|
||||
return pickRay;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ const int NUM_FRUSTUM_CORNERS = 8;
|
|||
const int NUM_FRUSTUM_PLANES = 6;
|
||||
|
||||
const float DEFAULT_CENTER_SPHERE_RADIUS = 3.0f;
|
||||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f;
|
||||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 55.0f;
|
||||
const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f;
|
||||
const float DEFAULT_NEAR_CLIP = 0.08f;
|
||||
const float DEFAULT_FAR_CLIP = 16384.0f;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue