mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 23:55:24 +02:00
Merge branch 'master' into fix/interface-console
# Conflicts: # README.md
This commit is contained in:
commit
71cee734ec
119 changed files with 2185 additions and 2002 deletions
56
.github/workflows/master_build.yml
vendored
56
.github/workflows/master_build.yml
vendored
|
@ -3,8 +3,7 @@ name: Master CI Build
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- gha-master-ci
|
||||
# FIXME: Change target branch to "master" before merging into "master" branch.
|
||||
- master
|
||||
|
||||
env:
|
||||
APP_NAME: interface
|
||||
|
@ -48,50 +47,51 @@ jobs:
|
|||
shell: bash
|
||||
id: buildenv1
|
||||
run: |
|
||||
echo ::set-env name=UPLOAD_PREFIX::master
|
||||
echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7`
|
||||
echo ::set-env name=JOB_NAME::"build (${{matrix.os}}, ${{matrix.build_type}})"
|
||||
echo "UPLOAD_PREFIX=master" >> $GITHUB_ENV
|
||||
echo ::set-output name=github_sha_short::`echo $GIT_COMMIT | cut -c1-7`
|
||||
echo "JOB_NAME=build (${{matrix.os}}, ${{matrix.build_type}})" >> $GITHUB_ENV
|
||||
# Linux build variables
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-"* ]]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::tgz
|
||||
echo ::set-env name=CMAKE_BUILD_EXTRA::"-- -j3"
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)"
|
||||
echo "PYTHON_EXEC=python3" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=tgz" >> $GITHUB_ENV
|
||||
echo "CMAKE_BUILD_EXTRA=-- -j3" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=ZIP_COMMAND::zip
|
||||
echo ::set-env name=ZIP_ARGS::-r
|
||||
echo ::set-env name=INSTALLER_EXT::dmg
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
echo "PYTHON_EXEC=python3" >> $GITHUB_ENV
|
||||
echo "ZIP_COMMAND=zip" >> $GITHUB_ENV
|
||||
echo "ZIP_ARGS=-r" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=dmg" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode" >> $GITHUB_ENV
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python
|
||||
echo ::set-env name=ZIP_COMMAND::7z
|
||||
echo ::set-env name=ZIP_ARGS::a
|
||||
echo ::set-env name=INSTALLER_EXT::exe
|
||||
echo ::set-env name=CMAKE_EXTRA::"-A x64"
|
||||
echo "::set-env name=SYMBOL_REGEX::\(exe\|dll\|pdb\)"
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-win-symbols.zip"
|
||||
# echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}}
|
||||
# echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx"
|
||||
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=${BUILD_NUMBER}-${{ matrix.build_type }}-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
|
||||
fi
|
||||
# Configuration is broken into two steps because you can't set an env var and also reference it in the same step
|
||||
- name: Configure build environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo ::set-env name=ARTIFACT_PATTERN::Vircadia-Alpha-*.$INSTALLER_EXT
|
||||
echo "ARTIFACT_PATTERN=Vircadia-Alpha-*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
# Build type variables
|
||||
echo "GIT_COMMIT_SHORT=${{ steps.buildenv1.outputs.github_sha_short }}" >> $GITHUB_ENV
|
||||
if [ "${{ matrix.build_type }}" = "full" ]; then
|
||||
echo ::set-env name=CLIENT_ONLY::FALSE
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
echo "CLIENT_ONLY=FALSE" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-Alpha-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
else
|
||||
echo ::set-env name=CLIENT_ONLY::TRUE
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-Interface-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
echo "CLIENT_ONLY=TRUE" >> $GITHUB_ENV
|
||||
echo "INSTALLER=Vircadia-Alpha-Interface-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Clear working directory
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
|
|
80
.github/workflows/pr_build.yml
vendored
80
.github/workflows/pr_build.yml
vendored
|
@ -14,7 +14,9 @@ env:
|
|||
RELEASE_TYPE: PR
|
||||
RELEASE_NUMBER: ${{ github.event.number }}
|
||||
VERSION_CODE: ${{ github.event.number }}
|
||||
|
||||
# Sentry Crash Reporting
|
||||
CMAKE_BACKTRACE_URL: ${{ secrets.MINIDUMP_TOKEN }}
|
||||
CMAKE_BACKTRACE_TOKEN: PR_${{ github.event.number }}_${{ github.sha }}
|
||||
|
||||
# OSX specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
|
@ -22,19 +24,24 @@ env:
|
|||
|
||||
# WIN32 specific variables
|
||||
PreferredToolArchitecture: X64
|
||||
GIT_COMMIT_SHORT: ${{ github.sha }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "build (${{matrix.os}}, ${{matrix.build_type}})"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macOS-latest, ubuntu-18.04]
|
||||
build_type: [full]
|
||||
include:
|
||||
- os: windows-latest
|
||||
build_type: full
|
||||
- os: macOS-latest
|
||||
build_type: full
|
||||
- os: ubuntu-18.04
|
||||
build_type: full
|
||||
apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0
|
||||
- os: ubuntu-18.04
|
||||
build_type: android
|
||||
apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0
|
||||
fail-fast: false
|
||||
runs-on: ${{matrix.os}}
|
||||
if: github.event.action != 'labeled' || github.event.label.name == 'rebuild'
|
||||
|
@ -43,36 +50,48 @@ jobs:
|
|||
shell: bash
|
||||
id: buildenv1
|
||||
run: |
|
||||
echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7`
|
||||
echo ::set-env name=JOB_NAME::"build (${{matrix.os}}, ${{matrix.build_type}})"
|
||||
echo ::set-output name=github_sha_short::`echo $GIT_COMMIT | cut -c1-7`
|
||||
echo "JOB_NAME=build (${{matrix.os}}, ${{matrix.build_type}})" >> $GITHUB_ENV
|
||||
|
||||
# Linux build variables
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-"* ]]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::*
|
||||
echo ::set-env name=CMAKE_BUILD_EXTRA::"-- -j3"
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)"
|
||||
echo "PYTHON_EXEC=python3" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=*" >> $GITHUB_ENV
|
||||
echo "CMAKE_BUILD_EXTRA=-- -j3" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::dmg
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
echo "PYTHON_EXEC=python3" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=dmg" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode" >> $GITHUB_ENV
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python
|
||||
echo ::set-env name=INSTALLER_EXT::exe
|
||||
echo ::set-env name=CMAKE_EXTRA::"-A x64"
|
||||
echo "PYTHON_EXEC=python" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=exe" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-A x64" >> $GITHUB_ENV
|
||||
fi
|
||||
# Android + Quest build variables
|
||||
if [[ "${{ matrix.build_type }}" == "android" ]]; then
|
||||
HIFI_ANDROID_PRECOMPILED="${{runner.workspace}}/dependencies"
|
||||
echo "HIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED" >> $GITHUB_ENV
|
||||
mkdir $HIFI_ANDROID_PRECOMPILED
|
||||
echo "INSTALLER_EXT=apk" >> $GITHUB_ENV
|
||||
fi
|
||||
# Configuration is broken into two steps because you can't set an env var and also reference it in the same step
|
||||
- name: Configure Build Environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo ::set-env name=ARTIFACT_PATTERN::Vircadia-Alpha-PR${{ github.event.number }}-*.$INSTALLER_EXT
|
||||
# Build type variables
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
echo "GIT_COMMIT_SHORT=${{ steps.buildenv1.outputs.github_sha_short }}" >> $GITHUB_ENV
|
||||
if [[ "${{ matrix.build_type }}" != "android" ]]; then
|
||||
echo "ARTIFACT_PATTERN=Vircadia-Alpha-PR${{ github.event.number }}-*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
# Build type variables
|
||||
echo "INSTALLER=Vircadia-Alpha-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
else
|
||||
echo "ARTIFACT_PATTERN=*.$INSTALLER_EXT" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Clear Working Directory
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
shell: bash
|
||||
|
@ -102,22 +121,27 @@ jobs:
|
|||
shell: bash
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release $CMAKE_EXTRA
|
||||
- name: Build Application
|
||||
if: matrix.build_type == 'full' || matrix.build_type == 'client'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME $CMAKE_BUILD_EXTRA
|
||||
- name: Build Domain Server
|
||||
if: matrix.build_type == 'full'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server $CMAKE_BUILD_EXTRA
|
||||
- name: Build Assignment Client
|
||||
if: matrix.build_type == 'full'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client $CMAKE_BUILD_EXTRA
|
||||
- name: Build Console
|
||||
if: matrix.build_type == 'full'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console $CMAKE_BUILD_EXTRA
|
||||
- name: Build Installer
|
||||
if: matrix.build_type != 'android'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
|
@ -144,6 +168,18 @@ jobs:
|
|||
done
|
||||
}
|
||||
retry cmake --build . --config $BUILD_TYPE --target package $CMAKE_BUILD_EXTRA
|
||||
- name: Build for Android + Quest
|
||||
if: matrix.build_type == 'android'
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/vircadia
|
||||
run: |
|
||||
echo "Pre-cache the vcpkg managed dependencies"
|
||||
$PYTHON_EXEC prebuild.py --build-root ${{runner.workspace}}/build --android interface
|
||||
cd android
|
||||
# Pre-cache the gradle dependencies
|
||||
./gradlew -m tasks -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED
|
||||
# Build!
|
||||
./build_android.sh
|
||||
- name: Output system stats
|
||||
if: ${{ always() }}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
@ -161,4 +197,8 @@ jobs:
|
|||
working-directory: ${{runner.workspace}}/build
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: $PYTHON_EXEC "$GITHUB_WORKSPACE/tools/ci-scripts/upload_to_publish_server.py"
|
||||
run: |
|
||||
if [[ "${{ matrix.build_type }}" == "android" ]]; then
|
||||
cd $GITHUB_WORKSPACE/android
|
||||
fi
|
||||
$PYTHON_EXEC "$GITHUB_WORKSPACE/tools/ci-scripts/upload_to_publish_server.py"
|
|
@ -66,7 +66,7 @@ The above code to suppress modules is not necessary, but will speed up the build
|
|||
|
||||
### Clone the repository
|
||||
|
||||
`git clone https://github.com/kasenvr/project-athena.git`
|
||||
`git clone https://github.com/vircadia/vircadia.git`
|
||||
|
||||
## Building & Running
|
||||
|
||||
|
@ -119,4 +119,4 @@ Some things you can try if you want to do a clean build
|
|||
* In Android Studio, click _File > Invalidate Caches / Restart_ and select _Invalidate and Restart_
|
||||
|
||||
If you see lots of "couldn't acquire lock" errors,
|
||||
* Open Task Manager and close any running Clang / Gradle processes
|
||||
* Open Task Manager and close any running Clang / Gradle processes
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# Build Linux
|
||||
|
||||
*Last Updated on April 11, 2020*
|
||||
*Last Updated on December 1, 2020*
|
||||
|
||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
|
||||
|
||||
You can use the [Vircadia Builder](https://github.com/vircadia/vircadia-builder) to build on Linux more easily. Alternatively, you can follow the manual steps below.
|
||||
|
||||
## Ubuntu 16.04/18.04 specific build guide
|
||||
### Ubuntu 16.04 only
|
||||
Add the following line to *.bash_profile*
|
||||
|
@ -76,7 +78,7 @@ sudo apt-get install nodejs
|
|||
|
||||
Clone this repository:
|
||||
```bash
|
||||
git clone https://github.com/kasenvr/project-athena.git
|
||||
git clone https://github.com/vircadia/vircadia.git
|
||||
```
|
||||
|
||||
To compile a DEV version checkout the branch you need. To get a list of all tags:
|
||||
|
@ -86,7 +88,7 @@ git fetch -a
|
|||
|
||||
Then checkout the main branch with:
|
||||
```bash
|
||||
git checkout kasen/core
|
||||
git checkout master
|
||||
```
|
||||
|
||||
### Using a custom Qt build
|
||||
|
@ -103,7 +105,7 @@ Qt must be installed in `$HIFI_QT_BASE/$VIRCADIA_USE_QT_VERSION/qt5-install`.
|
|||
|
||||
Create the build directory:
|
||||
```bash
|
||||
cd project-athena
|
||||
cd vircadia
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
|
|
|
@ -107,7 +107,7 @@ Note: You can also run Interface by launching it from command line or File Explo
|
|||
For any problems after Step #6, first try this:
|
||||
* Delete your locally cloned copy of the Vircadia repository
|
||||
* Restart your computer
|
||||
* Redownload the [repository](https://github.com/kasenvr/project-athena)
|
||||
* Redownload the [repository](https://github.com/vircadia/vircadia)
|
||||
* Restart directions from Step #6
|
||||
|
||||
#### CMake gives you the same error message repeatedly after the build fails
|
||||
|
|
|
@ -41,6 +41,12 @@ endif()
|
|||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" "${EXTERNAL_BUILD_ASSETS}")
|
||||
MESSAGE(STATUS "EXTERNAL_BUILD_ASSETS: ${EXTERNAL_BUILD_ASSETS}")
|
||||
|
||||
set(GLES_OPTION "$ENV{USE_GLES}")
|
||||
|
||||
# Will affect VCPKG dependencies
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_env/USE_GLES.txt" "${GLES_OPTION}")
|
||||
MESSAGE(STATUS "GLES_OPTION: ${GLES_OPTION}")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros/TargetPython.cmake")
|
||||
target_python()
|
||||
|
||||
|
@ -129,7 +135,6 @@ set(BUILD_TESTS_OPTION OFF)
|
|||
set(BUILD_MANUAL_TESTS_OPTION ${BUILD_TESTS_OPTION})
|
||||
set(BUILD_TOOLS_OPTION ON)
|
||||
set(BUILD_INSTALLER_OPTION ON)
|
||||
set(GLES_OPTION OFF)
|
||||
set(DISABLE_QML_OPTION OFF)
|
||||
set(DOWNLOAD_SERVERLESS_CONTENT_OPTION OFF)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
The project embraces distributed development and if you'd like to help, it would be greatly appreciated. Just open a pull request with the revisions.
|
||||
|
||||
Contributing
|
||||
|
@ -6,7 +7,7 @@ Contributing
|
|||
2. Clone your fork of the repository locally
|
||||
|
||||
```
|
||||
git clone git://github.com/USERNAME/project-athena.git
|
||||
git clone git://github.com/USERNAME/vircadia.git
|
||||
```
|
||||
3. Create a new branch
|
||||
|
||||
|
@ -20,7 +21,7 @@ Contributing
|
|||
6. Update your branch
|
||||
|
||||
```
|
||||
git remote add upstream https://github.com/kasenvr/project-athena
|
||||
git remote add upstream https://github.com/vircadia/vircadia
|
||||
git pull upstream master
|
||||
```
|
||||
|
||||
|
@ -35,13 +36,22 @@ Contributing
|
|||
|
||||
*You can follow [GitHub's guide](https://help.github.com/articles/creating-a-pull-request) to find out how to create a pull request.*
|
||||
|
||||
Tips for Pull Requests
|
||||
===
|
||||
To make the QA process go as smoothly as possible.
|
||||
|
||||
1. Have a basic description in your pull request.
|
||||
2. Write a basic test plan if you are altering or adding features.
|
||||
3. If a new API is added, try to make sure that some level of basic documentation on how you can utilize it is included.
|
||||
4. If an added API or feature requires an external service, try to document or link to instructions on how to create a basic working setup.
|
||||
|
||||
Reporting Bugs
|
||||
===
|
||||
1. Always update to the latest code on master, we make many merges every day and it is possible the bug has already been fixed!
|
||||
2. Search [issues](https://github.com/kasenvr/project-athena/issues) to make sure that somebody has not already reported the same bug.
|
||||
3. [Add](https://github.com/kasenvr/project-athena/issues/new) your report to the issues list!
|
||||
2. Search [issues](https://github.com/vircadia/vircadia/issues) to make sure that somebody has not already reported the same bug.
|
||||
3. [Add](https://github.com/vircadia/vircadia/issues/new) your report to the issues list!
|
||||
|
||||
Requesting a Feature
|
||||
===
|
||||
1. Search [issues](https://github.com/kasenvr/project-athena/issues) to make sure that somebody has not already requested the same feature.
|
||||
2. [Add](https://github.com/kasenvr/project-athena/issues/new) your request to the issues list!
|
||||
1. Search [issues](https://github.com/vircadia/vircadia/issues) to make sure that somebody has not already requested the same feature.
|
||||
2. [Add](https://github.com/vircadia/vircadia/issues/new) your request to the issues list!
|
||||
|
|
16
README.md
16
README.md
|
@ -8,17 +8,17 @@ Vircadia is a 3D social software project seeking to incrementally bring about a
|
|||
|
||||
### Releases
|
||||
|
||||
[View Releases here](https://github.com/kasenvr/project-athena/releases/)
|
||||
[View Releases here](https://github.com/vircadia/vircadia/releases/)
|
||||
|
||||
### How to build the Interface
|
||||
|
||||
[For Windows](https://github.com/kasenvr/project-athena/blob/master/BUILD_WIN.md)
|
||||
[For Windows](https://github.com/vircadia/vircadia/blob/master/BUILD_WIN.md)
|
||||
|
||||
[For Mac](https://github.com/kasenvr/project-athena/blob/master/BUILD_OSX.md)
|
||||
[For Mac](https://github.com/vircadia/vircadia/blob/master/BUILD_OSX.md)
|
||||
|
||||
[For Linux](https://github.com/kasenvr/project-athena/blob/master/BUILD_LINUX.md)
|
||||
[For Linux](https://github.com/vircadia/vircadia/blob/master/BUILD_LINUX.md)
|
||||
|
||||
[For Linux - Vircadia Builder](https://github.com/kasenvr/vircadia-builder)
|
||||
[For Linux - Vircadia Builder](https://github.com/vircadia/vircadia-builder)
|
||||
|
||||
### How to deploy a Server
|
||||
|
||||
|
@ -26,13 +26,13 @@ Vircadia is a 3D social software project seeking to incrementally bring about a
|
|||
|
||||
### How to build a Server
|
||||
|
||||
[For Linux - Vircadia Builder](https://github.com/kasenvr/vircadia-builder)
|
||||
[For Linux - Vircadia Builder](https://github.com/vircadia/vircadia-builder)
|
||||
|
||||
### How to generate an Installer
|
||||
|
||||
[For Windows](https://github.com/kasenvr/project-athena/blob/master/INSTALLER.md)
|
||||
[For Windows](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md)
|
||||
|
||||
[For Linux - AppImage - Vircadia Builder](https://github.com/kasenvr/vircadia-builder/blob/master/README.md#building-appimages)
|
||||
[For Linux - AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages)
|
||||
|
||||
### Boot to Metaverse: The Goal
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ ANDROID_BUILD_TYPE=release
|
|||
ANDROID_BUILD_TARGET=assembleRelease
|
||||
|
||||
if [[ "$RELEASE_TYPE" == "PR" ]]; then
|
||||
ANDROID_APK_SUFFIX=PR${RELEASE_NUMBER}-${SHA7}.apk ;
|
||||
ANDROID_APK_SUFFIX=PR${RELEASE_NUMBER}-${GIT_COMMIT_SHORT}.apk ;
|
||||
elif [[ "${STABLE_BUILD}" == "1" ]]; then
|
||||
ANDROID_APK_SUFFIX=${RELEASE_NUMBER}.apk ;
|
||||
else
|
||||
ANDROID_APK_SUFFIX=${RELEASE_NUMBER}-${SHA7}.apk ;
|
||||
ANDROID_APK_SUFFIX=${RELEASE_NUMBER}-${GIT_COMMIT_SHORT}.apk ;
|
||||
fi
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ fi
|
|||
ANDROID_APP=interface
|
||||
ANDROID_OUTPUT_DIR=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_TYPE}
|
||||
ANDROID_OUTPUT_FILE=${ANDROID_APP}-${ANDROID_BUILD_TYPE}.apk
|
||||
ANDROID_APK_NAME=HighFidelity-Beta-${ANDROID_APK_SUFFIX}
|
||||
ANDROID_APK_NAME=Vircadia-Alpha-${ANDROID_APK_SUFFIX}
|
||||
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} ${ANDROID_APP}:${ANDROID_BUILD_TARGET}
|
||||
cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME}
|
||||
|
||||
|
@ -25,9 +25,9 @@ cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME}
|
|||
ANDROID_APP=questInterface
|
||||
ANDROID_OUTPUT_DIR=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_TYPE}
|
||||
ANDROID_OUTPUT_FILE=${ANDROID_APP}-${ANDROID_BUILD_TYPE}.apk
|
||||
ANDROID_APK_NAME=HighFidelity-Quest-Beta-${ANDROID_APK_SUFFIX}
|
||||
ANDROID_APK_NAME=Vircadia-Quest-Alpha-${ANDROID_APK_SUFFIX}
|
||||
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} ${ANDROID_APP}:${ANDROID_BUILD_TARGET} || true
|
||||
cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME} || true
|
||||
cp ${ANDROID_OUTPUT_DIR}/${ANDROID_OUTPUT_FILE} ./${ANDROID_APK_NAME}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
set -xeuo pipefail
|
||||
|
||||
DOCKER_IMAGE_NAME="hifi_androidbuild"
|
||||
DOCKER_IMAGE_NAME="vircadia_androidbuild"
|
||||
|
||||
docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/Dockerfile docker
|
||||
docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f ./android/docker/Dockerfile ./android/docker
|
||||
|
||||
# The Jenkins PR builds use VERSION_CODE, but the release builds use VERSION
|
||||
# So make sure we use VERSION_CODE consistently
|
||||
|
@ -17,7 +17,7 @@ test -z "$STABLE_BUILD" && export STABLE_BUILD=0
|
|||
docker run \
|
||||
--rm \
|
||||
--security-opt seccomp:unconfined \
|
||||
-v "${WORKSPACE}":/home/jenkins/hifi \
|
||||
-v "${WORKSPACE}":/home/gha/vircadia \
|
||||
-e RELEASE_NUMBER \
|
||||
-e RELEASE_TYPE \
|
||||
-e ANDROID_APP \
|
||||
|
@ -33,7 +33,7 @@ docker run \
|
|||
-e OAUTH_CLIENT_SECRET \
|
||||
-e OAUTH_CLIENT_ID \
|
||||
-e OAUTH_REDIRECT_URI \
|
||||
-e SHA7 \
|
||||
-e GIT_COMMIT_SHORT \
|
||||
-e STABLE_BUILD \
|
||||
-e VERSION_CODE \
|
||||
"${DOCKER_IMAGE_NAME}" \
|
||||
|
|
|
@ -57,13 +57,13 @@ RUN apt-get -y install \
|
|||
|
||||
# --- Gradle
|
||||
ARG BUILD_UID=1001
|
||||
RUN useradd -ms /bin/bash -u $BUILD_UID jenkins
|
||||
RUN echo "jenkins ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
USER jenkins
|
||||
WORKDIR /home/jenkins
|
||||
RUN useradd -ms /bin/bash -u $BUILD_UID gha
|
||||
RUN echo "gha ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
USER gha
|
||||
WORKDIR /home/gha
|
||||
|
||||
# Hifi dependencies
|
||||
ENV HIFI_BASE="/home/jenkins/hifi_android"
|
||||
# Vircadia dependencies
|
||||
ENV HIFI_BASE="/home/gha/vircadia_android"
|
||||
ENV HIFI_ANDROID_PRECOMPILED="$HIFI_BASE/dependencies"
|
||||
ENV HIFI_VCPKG_BASE="$HIFI_BASE/vcpkg"
|
||||
|
||||
|
@ -71,24 +71,18 @@ RUN mkdir "$HIFI_BASE" && \
|
|||
mkdir "$HIFI_VCPKG_BASE" && \
|
||||
mkdir "$HIFI_ANDROID_PRECOMPILED"
|
||||
|
||||
# Checkout a relatively recent commit from the main repository and use it to cache the
|
||||
# gradle and vcpkg dependencies
|
||||
# This commit ID should be updated whenever someone changes the dependency list
|
||||
# in cmake/ports
|
||||
RUN git clone https://github.com/highfidelity/hifi.git && \
|
||||
cd ~/hifi && \
|
||||
git checkout 796bfb5d6715ff14c2e60f3ee8fac1465b7578c6
|
||||
# Download the repo
|
||||
RUN git clone https://github.com/vircadia/vircadia.git
|
||||
|
||||
WORKDIR /home/jenkins/hifi
|
||||
WORKDIR /home/gha/vircadia
|
||||
|
||||
RUN mkdir build
|
||||
|
||||
# Pre-cache the vcpkg managed dependencies
|
||||
WORKDIR /home/jenkins/hifi/build
|
||||
WORKDIR /home/gha/vircadia/build
|
||||
RUN python3 ../prebuild.py --build-root `pwd` --android interface
|
||||
|
||||
# Pre-cache the gradle dependencies
|
||||
WORKDIR /home/jenkins/hifi/android
|
||||
WORKDIR /home/gha/vircadia/android
|
||||
RUN ./gradlew -m tasks -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED
|
||||
#RUN ./gradlew extractDependencies -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED
|
||||
|
||||
|
|
|
@ -470,7 +470,9 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
|||
scriptEngines->runScriptInitializers(newEngine);
|
||||
newEngine->runInThread();
|
||||
auto newEngineSP = qSharedPointerCast<EntitiesScriptEngineProvider>(newEngine);
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(newEngineSP);
|
||||
// On the entity script server, these are the same
|
||||
DependencyManager::get<EntityScriptingInterface>()->setPersistentEntitiesScriptEngine(newEngineSP);
|
||||
DependencyManager::get<EntityScriptingInterface>()->setNonPersistentEntitiesScriptEngine(newEngineSP);
|
||||
|
||||
if (_entitiesScriptEngine) {
|
||||
disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
macro(target_oculus_mobile)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculus_1.22/VrApi)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/ovr_sdk_mobile_1.37.0/VrApi)
|
||||
|
||||
# Mobile SDK
|
||||
set(OVR_MOBILE_INCLUDE_DIRS ${INSTALL_DIR}/Include)
|
||||
|
@ -12,7 +12,7 @@ macro(target_oculus_mobile)
|
|||
target_link_libraries(${TARGET_NAME} ${OVR_MOBILE_LIBRARIES})
|
||||
|
||||
# Platform SDK
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/oculusPlatform)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/ovr_platform_sdk_23.0.0)
|
||||
set(OVR_PLATFORM_INCLUDE_DIRS ${INSTALL_DIR}/Include)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${OVR_PLATFORM_INCLUDE_DIRS})
|
||||
set(OVR_PLATFORM_LIBRARIES ${INSTALL_DIR}/Android/libs/arm64-v8a/libovrplatformloader.so)
|
||||
|
|
|
@ -19,7 +19,7 @@ include(vcpkg_common_functions)
|
|||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO kasenvr/etc2comp
|
||||
REPO vircadia/etc2comp
|
||||
REF 7f1843bf07825c21cab711360c1ddbad04641036
|
||||
SHA512 d747076acda8537d39585858c793a35c3dcc9ef283d723619a47f8c81ec1454c95b3340ad35f0655a939eae5b8271c801c48a9a7568311a01903a344c44af25b
|
||||
HEAD_REF master
|
||||
|
|
|
@ -2,13 +2,22 @@ include(vcpkg_common_functions)
|
|||
vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
|
||||
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS)
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/USE_GLES.txt" USE_GLES)
|
||||
|
||||
# GitHub Actions Android builds fail with `FILENAME` set while desktop builds with GLES fail without a set `FILENAME`.
|
||||
if (ANDROID)
|
||||
vcpkg_download_distfile(
|
||||
SOURCE_ARCHIVE
|
||||
URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad32es.zip
|
||||
SHA512 2e02ac633eed8f2ba2adbf96ea85d08998f48dd2e9ec9a88ec3c25f48eaf1405371d258066327c783772fcb3793bdb82bd7375fdabb2ba5e2ce0835468b17f65
|
||||
)
|
||||
elseif (USE_GLES)
|
||||
vcpkg_download_distfile(
|
||||
SOURCE_ARCHIVE
|
||||
URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad32es.zip
|
||||
SHA512 2e02ac633eed8f2ba2adbf96ea85d08998f48dd2e9ec9a88ec3c25f48eaf1405371d258066327c783772fcb3793bdb82bd7375fdabb2ba5e2ce0835468b17f65
|
||||
FILENAME glad32es.zip
|
||||
)
|
||||
else()
|
||||
# else Linux desktop
|
||||
vcpkg_download_distfile(
|
||||
|
|
|
@ -3,7 +3,7 @@ include(vcpkg_common_functions)
|
|||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO kasenvr/scribe
|
||||
REPO vircadia/scribe
|
||||
REF 1bd638a36ca771e5a68d01985b6389b71835cbd2
|
||||
SHA512 dbe241d86df3912e544f6b9839873f9875df54efc93822b145e7b13243eaf2e3d690bc8a28b1e52d05bdcd7e68fca6b0b2f5c43ffd0f56a9b7a50d54dcf9e31e
|
||||
HEAD_REF master
|
||||
|
|
|
@ -9,7 +9,7 @@ include(vcpkg_common_functions)
|
|||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO kasenvr/nvidia-texture-tools
|
||||
REPO vircadia/nvidia-texture-tools
|
||||
REF 330c4d56274a0f602a5c70596e2eb670a4ed56c2
|
||||
SHA512 4c0bc2f369120d696cc27710b6d33086b27eef55f537ec66b9a5c8b1839bc2426c0413670b0f65be52c5d353468f0126dfe024be1f0690611d4d7e33ac530127
|
||||
HEAD_REF master
|
||||
|
|
|
@ -3,8 +3,8 @@ include(vcpkg_common_functions)
|
|||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO oneapi-src/oneTBB
|
||||
REF 4bdba61bafc6ba2d636f31564f1de5702d365cf7
|
||||
SHA512 f2a8d7e0476f846039390f4a79af3fe13770e23b01bf4741e738136f7ddb401357a0e50f35212e8d0fa5fc4cf1563418337309227d7243fc3676edd406ae652d
|
||||
REF eca91f16d7490a8abfdee652dadf457ec820cc37
|
||||
SHA512 7144e1dc68304b5358e6ea330431b6f0c61fadb147efa353a5b242777d6fabf7b8cf99b79cffb51b49b911dd17a9f1879619d6eebdf319f23ec3235c89cffc25
|
||||
HEAD_REF tbb_2019
|
||||
PATCHES fix-static-build.patch
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// index.html
|
||||
//
|
||||
// Created by kasenvr@gmail.com on 21 Jul 2020
|
||||
// Created by somnilibertas@gmail.com on 21 Jul 2020
|
||||
// Copyright 2020 Vircadia and contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// Here you can put a script that will be run by an assignment-client (AC)
|
||||
// For examples, please go to https://github.com/kasenvr/project-athena/tree/master/script-archive/acScripts
|
||||
// For examples, please go to https://github.com/vircadia/vircadia/tree/master/script-archive/acScripts
|
||||
// The directory named acScripts contains assignment-client specific scripts you can try.
|
||||
|
|
|
@ -1238,7 +1238,7 @@ void DomainGatekeeper::requestDomainUser(const QString& username, const QString&
|
|||
|
||||
// Get data pertaining to "me", the user who generated the access token.
|
||||
const QString WORDPRESS_USER_ROUTE = "wp/v2/users/me";
|
||||
const QString WORDPRESS_USER_QUERY = "_fields=username,roles";
|
||||
const QString WORDPRESS_USER_QUERY = "_fields=username,email,roles";
|
||||
QUrl domainUserURL = apiBase + WORDPRESS_USER_ROUTE + (apiBase.contains("?") ? "&" : "?") + WORDPRESS_USER_QUERY;
|
||||
|
||||
QNetworkRequest request;
|
||||
|
@ -1270,8 +1270,13 @@ void DomainGatekeeper::requestDomainUserFinished() {
|
|||
if (200 <= httpStatus && httpStatus < 300) {
|
||||
|
||||
QString username = rootObject.value("username").toString().toLower();
|
||||
if (_inFlightDomainUserIdentityRequests.contains(username)) {
|
||||
QString email = rootObject.value("email").toString().toLower();
|
||||
|
||||
if (_inFlightDomainUserIdentityRequests.contains(username) || _inFlightDomainUserIdentityRequests.contains(email)) {
|
||||
// Success! Verified user.
|
||||
if (!_inFlightDomainUserIdentityRequests.contains(username)) {
|
||||
username = email;
|
||||
}
|
||||
_verifiedDomainUserIdentities.insert(username, _inFlightDomainUserIdentityRequests.value(username));
|
||||
_inFlightDomainUserIdentityRequests.remove(username);
|
||||
|
||||
|
|
|
@ -39,15 +39,15 @@ ANDROID_PACKAGES = {
|
|||
'sharedLibFolder': 'lib',
|
||||
'includeLibs': ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so']
|
||||
},
|
||||
'oculus_1.22': {
|
||||
'file': 'ovr_sdk_mobile_1.22.zip',
|
||||
'checksum': '1ac3c5b0521e5406f287f351015daff8',
|
||||
'ovr_sdk_mobile_1.37.0': {
|
||||
'file': 'ovr_sdk_mobile_1.37.0.zip',
|
||||
'checksum': '6040e1966f335a3e5015295154cd7383',
|
||||
'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release',
|
||||
'includeLibs': ['libvrapi.so']
|
||||
},
|
||||
'oculusPlatform': {
|
||||
'file': 'OVRPlatformSDK_v1.34.0.zip',
|
||||
'checksum': '16e4c5f39520f122bc49cb6d5bb88289',
|
||||
'ovr_platform_sdk_23.0.0': {
|
||||
'file': 'ovr_platform_sdk_23.0.0.zip',
|
||||
'checksum': '29d02b560f60d0fa7b8a64cd965dd55b',
|
||||
'sharedLibFolder': 'Android/libs/arm64-v8a',
|
||||
'includeLibs': ['libovrplatformloader.so']
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ import functools
|
|||
|
||||
print = functools.partial(print, flush=True)
|
||||
|
||||
# Encapsulates the vcpkg system
|
||||
# Encapsulates the vcpkg system
|
||||
class QtDownloader:
|
||||
CMAKE_TEMPLATE = """
|
||||
# this file auto-generated by hifi_qt.py
|
||||
|
@ -72,7 +72,7 @@ endif()
|
|||
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-18.04.tar.gz'
|
||||
elif u_major == 19 and u_minor == 10:
|
||||
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.12.6-ubuntu-19.10.tar.xz'
|
||||
elif u_major > 18 and ( u_major != 19 and u_minor != 4):
|
||||
elif u_major > 19:
|
||||
print("We don't support " + distro.name(pretty=True) + " yet. Perhaps consider helping us out?")
|
||||
raise Exception('LINUX DISTRO IS NOT SUPPORTED YET!!!')
|
||||
else:
|
||||
|
@ -81,7 +81,7 @@ endif()
|
|||
else:
|
||||
print("Sorry, " + distro.name(pretty=True) + " is not supported. Please consider helping us out.")
|
||||
print("It's also possible to build Qt for your distribution, please see the documentation at:")
|
||||
print("https://github.com/kasenvr/project-athena/tree/kasen/core/tools/qt-builder")
|
||||
print("https://github.com/vircadia/vircadia/tree/master/tools/qt-builder")
|
||||
raise Exception('UNKNOWN LINUX VERSION!!!')
|
||||
else:
|
||||
print("System : " + platform.system())
|
||||
|
|
|
@ -114,7 +114,7 @@ Item {
|
|||
displayNameField.placeholderText = "Display Name (optional)";
|
||||
var savedDisplayName = Settings.getValue("Avatar/displayName", "");
|
||||
displayNameField.text = savedDisplayName;
|
||||
emailField.placeholderText = (!isLoggingInToDomain) ? "Username or Email" : "Username";
|
||||
emailField.placeholderText = "Username or Email";
|
||||
if (!isLoggingInToDomain) {
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
|
|
|
@ -309,8 +309,8 @@ FocusScope {
|
|||
if (child.hasOwnProperty("modality")) {
|
||||
var mappedPoint = mapToItem(child, point.x, point.y);
|
||||
if (child.hasOwnProperty("frame")) {
|
||||
var outLine = child.frame.children[2];
|
||||
var framePoint = outLine.mapFromGlobal(point.x, point.y);
|
||||
var outLine = child.frame.children[2]; // sizeOutline
|
||||
var framePoint = mapToItem(outLine, point.x, point.y);
|
||||
if (outLine.contains(framePoint)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -54,10 +54,10 @@ Rectangle {
|
|||
textFormat: Text.StyledText
|
||||
linkColor: "#00B4EF"
|
||||
color: "white"
|
||||
text: "<a href=\"https://github.com/kasenvr/project-athena\">Vircadia Github</a>."
|
||||
text: "<a href=\"https://github.com/vircadia/vircadia\">Vircadia Github</a>."
|
||||
size: 20
|
||||
onLinkActivated: {
|
||||
About.openUrl("https:/github.com/kasenvr/project-athena");
|
||||
About.openUrl("https:/github.com/vircadia/vircadia");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// EntityScriptQMLWhitelist.qml
|
||||
// interface/resources/qml/hifi/dialogs/security
|
||||
//
|
||||
// Created by Kasen IO on 2019.12.05 | realities.dev | kasenvr@gmail.com
|
||||
// Copyright 2019 Kasen IO
|
||||
// Created by Kalila L. on 2019.12.05 | realities.dev | somnilibertas@gmail.com
|
||||
// Copyright 2019 Kalila L.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
|
|
@ -312,9 +312,9 @@ Rectangle {
|
|||
parent.color = hifi.colors.blueHighlight;
|
||||
}
|
||||
onClicked: {
|
||||
lightboxPopup.titleText = "Script Plugin Infrastructure by Kasen";
|
||||
lightboxPopup.titleText = "Script Plugin Infrastructure";
|
||||
lightboxPopup.bodyText = "Toggles the activation of scripting plugins in the 'plugins/scripting' folder. \n\n"
|
||||
+ "Created by https://kasen.io/";
|
||||
+ "Created by:\n humbletim@gmail.com\n somnilibertas@gmail.com";
|
||||
lightboxPopup.button1text = "OK";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
|
|
|
@ -27,7 +27,6 @@ Item {
|
|||
readonly property int frameMarginRight: frame.decoration ? frame.decoration.frameMarginRight : 0
|
||||
readonly property int frameMarginTop: frame.decoration ? frame.decoration.frameMarginTop : 0
|
||||
readonly property int frameMarginBottom: frame.decoration ? frame.decoration.frameMarginBottom : 0
|
||||
readonly property int offsetCorrection: 20
|
||||
|
||||
// Frames always fill their parents, but their decorations may extend
|
||||
// beyond the window via negative margin sizes
|
||||
|
@ -76,7 +75,7 @@ Item {
|
|||
Rectangle {
|
||||
id: sizeOutline
|
||||
x: -frameMarginLeft
|
||||
y: -frameMarginTop - offsetCorrection
|
||||
y: -frameMarginTop
|
||||
width: window ? window.width + frameMarginLeft + frameMarginRight + 2 : 0
|
||||
height: window ? window.height + frameMarginTop + frameMarginBottom + 2 : 0
|
||||
color: hifi.colors.baseGrayHighlight15
|
||||
|
|
|
@ -355,6 +355,7 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
|
|||
static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin";
|
||||
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn";
|
||||
static const QString CACHEBUST_SCRIPT_REQUIRE_SETTING_NAME = "cachebustScriptRequire";
|
||||
|
||||
static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f;
|
||||
|
||||
|
@ -1966,6 +1967,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
loadSettings();
|
||||
|
||||
updateVerboseLogging();
|
||||
|
||||
setCachebustRequire();
|
||||
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
@ -2600,6 +2603,16 @@ void Application::updateVerboseLogging() {
|
|||
QLoggingCategory::setFilterRules(rules);
|
||||
}
|
||||
|
||||
void Application::setCachebustRequire() {
|
||||
auto menu = Menu::getInstance();
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
bool enable = menu->isOptionChecked(MenuOption::CachebustRequire);
|
||||
|
||||
Setting::Handle<bool>{ CACHEBUST_SCRIPT_REQUIRE_SETTING_NAME, false }.set(enable);
|
||||
}
|
||||
|
||||
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||
DomainHandler::ConnectionRefusedReason reasonCode = static_cast<DomainHandler::ConnectionRefusedReason>(reasonCodeInt);
|
||||
|
||||
|
@ -8699,7 +8712,7 @@ void Application::notifyPacketVersionMismatch() {
|
|||
}
|
||||
|
||||
void Application::checkSkeleton() const {
|
||||
if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) {
|
||||
if (getMyAvatar()->getSkeletonModel()->isLoaded() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) {
|
||||
qCDebug(interfaceapp) << "MyAvatar model has no skeleton";
|
||||
|
||||
QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded...";
|
||||
|
|
|
@ -472,6 +472,8 @@ public slots:
|
|||
void setIsInterstitialMode(bool interstitialMode);
|
||||
|
||||
void updateVerboseLogging();
|
||||
|
||||
void setCachebustRequire();
|
||||
|
||||
void changeViewAsNeeded(float boomLength);
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// For happ(ier) development of QML, use these two things:
|
||||
// This forces QML files to be pulled from the source as you edit it: set environment variable HIFI_USE_SOURCE_TREE_RESOURCES=1
|
||||
// Use this to live reload: DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
// For happ(ier) development of QML, use these two things:
|
||||
// This forces QML files to be pulled from the source as you edit it: set environment variable HIFI_USE_SOURCE_TREE_RESOURCES=1
|
||||
// Use this to live reload: DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
|
||||
#include "Menu.h"
|
||||
#include <QDesktopServices>
|
||||
|
@ -365,6 +365,10 @@ Menu::Menu() {
|
|||
// Developer > Scripting > Verbose Logging
|
||||
addCheckableActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::VerboseLogging, 0, false,
|
||||
qApp, SLOT(updateVerboseLogging()));
|
||||
|
||||
// Developer > Scripting > Enable Cachebusting of Script.require
|
||||
addCheckableActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::CachebustRequire, 0, false,
|
||||
qApp, SLOT(setCachebustRequire()));
|
||||
|
||||
// Developer > Scripting > Enable Speech Control API
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -54,6 +55,7 @@ namespace MenuOption {
|
|||
const QString BookmarkAvatarEntities = "Bookmark Avatar Entities";
|
||||
const QString BookmarkLocation = "Bookmark Location";
|
||||
const QString CalibrateCamera = "Calibrate Camera";
|
||||
const QString CachebustRequire = "Enable Cachebusting of Script.require";
|
||||
const QString CenterPlayerInView = "Center Player In View";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ClearDiskCaches = "Clear Disk Caches (requires restart)";
|
||||
|
|
|
@ -89,6 +89,7 @@ int main(int argc, const char* argv[]) {
|
|||
QCommandLineOption displayNameOption("displayName", "set user display name <string>", "string");
|
||||
QCommandLineOption setBookmarkOption("setBookmark", "set bookmark key=value pair", "string");
|
||||
QCommandLineOption defaultScriptOverrideOption("defaultScriptsOverride", "override defaultsScripts.js", "string");
|
||||
QCommandLineOption forceCrashReportingOption("forceCrashReporting", "Force crash reporting to initialize");
|
||||
|
||||
parser.addOption(urlOption);
|
||||
parser.addOption(noLauncherOption);
|
||||
|
@ -103,6 +104,7 @@ int main(int argc, const char* argv[]) {
|
|||
parser.addOption(displayNameOption);
|
||||
parser.addOption(setBookmarkOption);
|
||||
parser.addOption(defaultScriptOverrideOption);
|
||||
parser.addOption(forceCrashReportingOption);
|
||||
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
|
@ -218,8 +220,9 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled();
|
||||
|
||||
qDebug() << "Crash handler logger is enabled:" << ual.isCrashMonitorEnabled();
|
||||
if (ual.isCrashMonitorEnabled()) {
|
||||
bool isCrashHandlerEnabled = ual.isCrashMonitorEnabled() || parser.isSet(forceCrashReportingOption);
|
||||
qDebug() << "Crash handler logger is enabled:" << isCrashHandlerEnabled;
|
||||
if (isCrashHandlerEnabled) {
|
||||
auto crashHandlerStarted = startCrashHandler(argv[0]);
|
||||
qDebug() << "Crash handler started:" << crashHandlerStarted;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ OverlayConductor::OverlayConductor() {
|
|||
OverlayConductor::~OverlayConductor() {
|
||||
}
|
||||
|
||||
bool OverlayConductor::headOutsideOverlay() const {
|
||||
bool OverlayConductor::headNotCenteredInOverlay() const {
|
||||
glm::mat4 hmdMat = qApp->getHMDSensorPose();
|
||||
glm::vec3 hmdPos = extractTranslation(hmdMat);
|
||||
glm::vec3 hmdForward = transformVectorFast(hmdMat, glm::vec3(0.0f, 0.0f, -1.0f));
|
||||
|
@ -32,8 +32,8 @@ bool OverlayConductor::headOutsideOverlay() const {
|
|||
glm::vec3 uiPos = uiTransform.getTranslation();
|
||||
glm::vec3 uiForward = uiTransform.getRotation() * glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
const float MAX_COMPOSITOR_DISTANCE = 0.99f; // If you're 1m from center of ui sphere, you're at the surface.
|
||||
const float MAX_COMPOSITOR_ANGLE = 180.0f; // rotation check is effectively disabled
|
||||
const float MAX_COMPOSITOR_DISTANCE = 0.33f;
|
||||
const float MAX_COMPOSITOR_ANGLE = 90.0f;
|
||||
if (glm::distance(uiPos, hmdPos) > MAX_COMPOSITOR_DISTANCE ||
|
||||
glm::dot(uiForward, hmdForward) < cosf(glm::radians(MAX_COMPOSITOR_ANGLE))) {
|
||||
return true;
|
||||
|
@ -70,6 +70,8 @@ bool OverlayConductor::updateAvatarIsAtRest() {
|
|||
void OverlayConductor::centerUI() {
|
||||
// place the overlay at the current hmd position in sensor space
|
||||
auto camMat = cancelOutRollAndPitch(qApp->getHMDSensorPose());
|
||||
// Set its radius.
|
||||
camMat = glm::scale(camMat, glm::vec3(HUD_RADIUS));
|
||||
qApp->getApplicationCompositor().setModelTransform(Transform(camMat));
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,6 @@ void OverlayConductor::update(float dt) {
|
|||
if (!desktop) {
|
||||
return;
|
||||
}
|
||||
bool currentVisible = !desktop->property("pinned").toBool();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
// centerUI when hmd mode is first enabled and mounted
|
||||
|
@ -96,24 +97,24 @@ void OverlayConductor::update(float dt) {
|
|||
_hmdMode = false;
|
||||
}
|
||||
|
||||
bool shouldRecenter = false;
|
||||
|
||||
if (_suppressedByHead) {
|
||||
if (updateAvatarIsAtRest()) {
|
||||
_suppressedByHead = false;
|
||||
shouldRecenter = true;
|
||||
}
|
||||
} else {
|
||||
if (_hmdMode && headOutsideOverlay()) {
|
||||
_suppressedByHead = true;
|
||||
}
|
||||
bool initiateRecenter = false;
|
||||
if (_hmdMode && headNotCenteredInOverlay()) {
|
||||
initiateRecenter = true;
|
||||
}
|
||||
|
||||
bool shouldRecenter = false;
|
||||
if (initiateRecenter || _suppressedByHead) {
|
||||
_suppressedByHead = !updateAvatarIsAtRest();
|
||||
shouldRecenter = !_suppressedByHead;
|
||||
}
|
||||
|
||||
bool currentVisible = !desktop->property("pinned").toBool();
|
||||
bool targetVisible = Menu::getInstance()->isOptionChecked(MenuOption::Overlays) && !_suppressedByHead;
|
||||
if (targetVisible != currentVisible) {
|
||||
offscreenUi->setPinned(!targetVisible);
|
||||
}
|
||||
if (shouldRecenter && !_suppressedByHead) {
|
||||
|
||||
if (shouldRecenter) {
|
||||
centerUI();
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
void centerUI();
|
||||
|
||||
private:
|
||||
bool headOutsideOverlay() const;
|
||||
bool headNotCenteredInOverlay() const;
|
||||
bool updateAvatarIsAtRest();
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
|
|
|
@ -1246,7 +1246,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
|||
}
|
||||
case CAMERA_MATRIX_INDEX: {
|
||||
glm::quat rotation;
|
||||
if (_skeletonModel && _skeletonModel->isActive()) {
|
||||
if (_skeletonModel && _skeletonModel->isLoaded()) {
|
||||
int headJointIndex = getJointIndex("Head");
|
||||
if (headJointIndex >= 0) {
|
||||
_skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation);
|
||||
|
@ -1298,7 +1298,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
|||
}
|
||||
case CAMERA_MATRIX_INDEX: {
|
||||
glm::vec3 translation;
|
||||
if (_skeletonModel && _skeletonModel->isActive()) {
|
||||
if (_skeletonModel && _skeletonModel->isLoaded()) {
|
||||
int headJointIndex = getJointIndex("Head");
|
||||
if (headJointIndex >= 0) {
|
||||
_skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation);
|
||||
|
@ -1427,7 +1427,7 @@ void Avatar::withValidJointIndicesCache(std::function<void()> const& worker) con
|
|||
QWriteLocker writeLock(&_modelJointIndicesCacheLock);
|
||||
if (!_modelJointsCached) {
|
||||
_modelJointIndicesCache.clear();
|
||||
if (_skeletonModel && _skeletonModel->isActive()) {
|
||||
if (_skeletonModel && _skeletonModel->isLoaded()) {
|
||||
_modelJointIndicesCache = _skeletonModel->getHFMModel().jointIndices;
|
||||
_modelJointsCached = true;
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
updateRenderItems();
|
||||
}
|
||||
|
||||
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
||||
if (!isLoaded() || !_owningAvatar->isMyAvatar()) {
|
||||
return; // only simulate for own avatar
|
||||
}
|
||||
|
||||
|
@ -255,19 +255,19 @@ bool SkeletonModel::getRightHandPosition(glm::vec3& position) const {
|
|||
}
|
||||
|
||||
bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const {
|
||||
return isActive() && getJointPositionInWorldFrame(_rig.indexOfJoint("Head"), headPosition);
|
||||
return isLoaded() && getJointPositionInWorldFrame(_rig.indexOfJoint("Head"), headPosition);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const {
|
||||
return isActive() && getJointPositionInWorldFrame(_rig.indexOfJoint("Neck"), neckPosition);
|
||||
return isLoaded() && getJointPositionInWorldFrame(_rig.indexOfJoint("Neck"), neckPosition);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const {
|
||||
return isActive() && getJointPosition(_rig.indexOfJoint("Neck"), neckPosition);
|
||||
return isLoaded() && getJointPosition(_rig.indexOfJoint("Neck"), neckPosition);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -361,7 +361,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch&
|
|||
}
|
||||
|
||||
bool SkeletonModel::hasSkeleton() {
|
||||
return isActive() ? _rig.indexOfJoint("Hips") != -1 : false;
|
||||
return isLoaded() ? _rig.indexOfJoint("Hips") != -1 : false;
|
||||
}
|
||||
|
||||
void SkeletonModel::onInvalidate() {
|
||||
|
|
|
@ -44,10 +44,10 @@ public:
|
|||
bool getIsJointOverridden(int jointIndex) const;
|
||||
|
||||
/// Returns the index of the left hand joint, or -1 if not found.
|
||||
int getLeftHandJointIndex() const { return isActive() ? _rig.indexOfJoint("LeftHand") : -1; }
|
||||
int getLeftHandJointIndex() const { return isLoaded() ? _rig.indexOfJoint("LeftHand") : -1; }
|
||||
|
||||
/// Returns the index of the right hand joint, or -1 if not found.
|
||||
int getRightHandJointIndex() const { return isActive() ? _rig.indexOfJoint("RightHand") : -1; }
|
||||
int getRightHandJointIndex() const { return isLoaded() ? _rig.indexOfJoint("RightHand") : -1; }
|
||||
|
||||
bool getLeftGrabPosition(glm::vec3& position) const;
|
||||
bool getRightGrabPosition(glm::vec3& position) const;
|
||||
|
|
|
@ -41,10 +41,10 @@ static const float reticleSize = TWO_PI / 100.0f;
|
|||
//EntityItemID CompositorHelper::_noItemId;
|
||||
static QString _tooltipId;
|
||||
|
||||
const uvec2 CompositorHelper::VIRTUAL_SCREEN_SIZE = uvec2(3960, 1188); // ~10% more pixel density than old version, 72dx240d FOV
|
||||
const QRect CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT = QRect(956, 0, 2048, 1188); // don't include entire width only center 2048
|
||||
const uvec2 CompositorHelper::VIRTUAL_SCREEN_SIZE = uvec2(2640, 1188);
|
||||
const QRect CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT = QRect(296, 0, 2048, 1188); // Center 2048 pixels.
|
||||
const float CompositorHelper::VIRTUAL_UI_ASPECT_RATIO = (float)VIRTUAL_SCREEN_SIZE.x / (float)VIRTUAL_SCREEN_SIZE.y;
|
||||
const vec2 CompositorHelper::VIRTUAL_UI_TARGET_FOV = vec2(PI * 3.0f / 2.0f, PI * 3.0f / 2.0f / VIRTUAL_UI_ASPECT_RATIO);
|
||||
const vec2 CompositorHelper::VIRTUAL_UI_TARGET_FOV = vec2(PI, PI / VIRTUAL_UI_ASPECT_RATIO);
|
||||
const vec2 CompositorHelper::MOUSE_EXTENTS_ANGULAR_SIZE = vec2(PI * 2.0f, PI * 0.95f); // horizontal: full sphere, vertical: ~5deg from poles
|
||||
const vec2 CompositorHelper::MOUSE_EXTENTS_PIXELS = vec2(VIRTUAL_SCREEN_SIZE) * (MOUSE_EXTENTS_ANGULAR_SIZE / VIRTUAL_UI_TARGET_FOV);
|
||||
|
||||
|
@ -384,9 +384,9 @@ bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, c
|
|||
glm::vec3 localPosition = transformPoint(worldToUi, position);
|
||||
glm::vec3 localDirection = glm::normalize(transformVectorFast(worldToUi, direction));
|
||||
|
||||
const float UI_RADIUS = 1.0f;
|
||||
const float UNIT_RADIUS = 1.0f;
|
||||
float intersectionDistance;
|
||||
if (raySphereIntersect(localDirection, localPosition, UI_RADIUS, &intersectionDistance)) {
|
||||
if (raySphereIntersect(localDirection, localPosition, UNIT_RADIUS, &intersectionDistance)) {
|
||||
result = transformPoint(uiToWorld, localPosition + localDirection * intersectionDistance);
|
||||
#ifdef WANT_DEBUG
|
||||
DebugDraw::getInstance().drawRay(position, result, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f));
|
||||
|
@ -407,9 +407,8 @@ bool CompositorHelper::calculateParabolaUICollisionPoint(const glm::vec3& origin
|
|||
glm::vec3 localVelocity = glm::normalize(transformVectorFast(worldToUi, velocity));
|
||||
glm::vec3 localAcceleration = glm::normalize(transformVectorFast(worldToUi, acceleration));
|
||||
|
||||
const float UI_RADIUS = 1.0f;
|
||||
float intersectionDistance;
|
||||
if (findParabolaSphereIntersection(localOrigin, localVelocity, localAcceleration, glm::vec3(0.0f), UI_RADIUS, intersectionDistance)) {
|
||||
if (findParabolaSphereIntersection(localOrigin, localVelocity, localAcceleration, glm::vec3(0.0f), HUD_RADIUS, intersectionDistance)) {
|
||||
result = origin + velocity * intersectionDistance + 0.5f * acceleration * intersectionDistance * intersectionDistance;
|
||||
parabolicDistance = intersectionDistance;
|
||||
return true;
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
|
||||
class ReticleInterface;
|
||||
|
||||
const float DEFAULT_RETICLE_DEPTH = 1.0f; // FIXME - probably should be based on UI radius
|
||||
const float HUD_RADIUS = 1.5f;
|
||||
const float DEFAULT_RETICLE_DEPTH = HUD_RADIUS;
|
||||
|
||||
const float MAGNIFY_WIDTH = 220.0f;
|
||||
const float MAGNIFY_HEIGHT = 100.0f;
|
||||
|
@ -154,7 +155,7 @@ private:
|
|||
std::unique_ptr<QPropertyAnimation> _alphaPropertyAnimation;
|
||||
|
||||
std::atomic<bool> _reticleVisible { true };
|
||||
std::atomic<float> _reticleDepth { 1.0f };
|
||||
std::atomic<float> _reticleDepth { DEFAULT_RETICLE_DEPTH };
|
||||
|
||||
// NOTE: when the compositor is running in HMD mode, it will control the reticle position as a custom
|
||||
// application specific position, when it's in desktop mode, the reticle position will simply move
|
||||
|
|
|
@ -158,79 +158,98 @@ render::ItemID EntityTreeRenderer::renderableIdForEntityId(const EntityItemID& i
|
|||
|
||||
int EntityTreeRenderer::_entitiesScriptEngineCount = 0;
|
||||
|
||||
void EntityTreeRenderer::resetEntitiesScriptEngine() {
|
||||
_entitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT,
|
||||
QString("about:Entities %1").arg(++_entitiesScriptEngineCount));
|
||||
DependencyManager::get<ScriptEngines>()->runScriptInitializers(_entitiesScriptEngine);
|
||||
_entitiesScriptEngine->runInThread();
|
||||
auto entitiesScriptEngineProvider = qSharedPointerCast<EntitiesScriptEngineProvider>(_entitiesScriptEngine);
|
||||
void EntityTreeRenderer::setupEntityScriptEngineSignals(const ScriptEnginePointer& scriptEngine) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->setEntitiesScriptEngine(entitiesScriptEngineProvider);
|
||||
|
||||
// Connect mouse events to entity script callbacks
|
||||
if (!_mouseAndPreloadSignalHandlersConnected) {
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event);
|
||||
// FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event);
|
||||
// FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming
|
||||
scriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event);
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event);
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, scriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event);
|
||||
});
|
||||
|
||||
connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) {
|
||||
EntityItemPointer entity = getTree()->findEntityByID(entityID);
|
||||
if (entity) {
|
||||
entity->setScriptHasFinishedPreload(true);
|
||||
}
|
||||
});
|
||||
connect(scriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) {
|
||||
EntityItemPointer entity = getTree()->findEntityByID(entityID);
|
||||
if (entity) {
|
||||
entity->setScriptHasFinishedPreload(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_mouseAndPreloadSignalHandlersConnected = true;
|
||||
void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() {
|
||||
if (_persistentEntitiesScriptEngine) {
|
||||
_persistentEntitiesScriptEngine->unloadAllEntityScripts(true);
|
||||
_persistentEntitiesScriptEngine->stop();
|
||||
_persistentEntitiesScriptEngine->waitTillDoneRunning();
|
||||
_persistentEntitiesScriptEngine->disconnectNonEssentialSignals();
|
||||
}
|
||||
_persistentEntitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT,
|
||||
QString("about:Entities %1").arg(++_entitiesScriptEngineCount));
|
||||
DependencyManager::get<ScriptEngines>()->runScriptInitializers(_persistentEntitiesScriptEngine);
|
||||
_persistentEntitiesScriptEngine->runInThread();
|
||||
auto entitiesScriptEngineProvider = qSharedPointerCast<EntitiesScriptEngineProvider>(_persistentEntitiesScriptEngine);
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->setPersistentEntitiesScriptEngine(entitiesScriptEngineProvider);
|
||||
|
||||
setupEntityScriptEngineSignals(_persistentEntitiesScriptEngine);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() {
|
||||
if (_nonPersistentEntitiesScriptEngine) {
|
||||
_nonPersistentEntitiesScriptEngine->unloadAllEntityScripts(true);
|
||||
_nonPersistentEntitiesScriptEngine->stop();
|
||||
_nonPersistentEntitiesScriptEngine->waitTillDoneRunning();
|
||||
_nonPersistentEntitiesScriptEngine->disconnectNonEssentialSignals();
|
||||
}
|
||||
_nonPersistentEntitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT,
|
||||
QString("about:Entities %1").arg(++_entitiesScriptEngineCount));
|
||||
DependencyManager::get<ScriptEngines>()->runScriptInitializers(_nonPersistentEntitiesScriptEngine);
|
||||
_nonPersistentEntitiesScriptEngine->runInThread();
|
||||
auto entitiesScriptEngineProvider = qSharedPointerCast<EntitiesScriptEngineProvider>(_nonPersistentEntitiesScriptEngine);
|
||||
DependencyManager::get<EntityScriptingInterface>()->setNonPersistentEntitiesScriptEngine(entitiesScriptEngineProvider);
|
||||
|
||||
setupEntityScriptEngineSignals(_nonPersistentEntitiesScriptEngine);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
|
||||
leaveDomainAndNonOwnedEntities();
|
||||
// unload and stop the engine
|
||||
if (_entitiesScriptEngine) {
|
||||
QList<EntityItemID> entitiesWithEntityScripts = _entitiesScriptEngine->getListOfEntityScriptIDs();
|
||||
if (_nonPersistentEntitiesScriptEngine) {
|
||||
QList<EntityItemID> entitiesWithEntityScripts = _nonPersistentEntitiesScriptEngine->getListOfEntityScriptIDs();
|
||||
|
||||
foreach (const EntityItemID& entityID, entitiesWithEntityScripts) {
|
||||
foreach (const EntityItemID& entityID, entitiesWithEntityScripts) {
|
||||
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
|
||||
|
||||
if (entityItem && !entityItem->getScript().isEmpty()) {
|
||||
if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
|
||||
if (_currentEntitiesInside.contains(entityID)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
_nonPersistentEntitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +259,10 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
|
|||
void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
|
||||
stopDomainAndNonOwnedEntities();
|
||||
|
||||
if (!_shuttingDown && _wantScripts) {
|
||||
resetNonPersistentEntitiesScriptEngine();
|
||||
}
|
||||
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer> savedEntities;
|
||||
std::unordered_set<EntityRendererPointer> savedRenderables;
|
||||
// remove all entities from the scene
|
||||
|
@ -269,16 +292,22 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
|
|||
|
||||
void EntityTreeRenderer::clear() {
|
||||
leaveAllEntities();
|
||||
// unload and stop the engine
|
||||
if (_entitiesScriptEngine) {
|
||||
// do this here (instead of in deleter) to avoid marshalling unload signals back to this thread
|
||||
_entitiesScriptEngine->unloadAllEntityScripts(true);
|
||||
_entitiesScriptEngine->stop();
|
||||
}
|
||||
|
||||
// reset the engine
|
||||
auto scene = _viewState->getMain3DScene();
|
||||
if (_shuttingDown) {
|
||||
// unload and stop the engines
|
||||
if (_nonPersistentEntitiesScriptEngine) {
|
||||
// do this here (instead of in deleter) to avoid marshalling unload signals back to this thread
|
||||
_nonPersistentEntitiesScriptEngine->unloadAllEntityScripts(true);
|
||||
_nonPersistentEntitiesScriptEngine->stop();
|
||||
}
|
||||
if (_persistentEntitiesScriptEngine) {
|
||||
// do this here (instead of in deleter) to avoid marshalling unload signals back to this thread
|
||||
_persistentEntitiesScriptEngine->unloadAllEntityScripts(true);
|
||||
_persistentEntitiesScriptEngine->stop();
|
||||
}
|
||||
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
for (const auto& entry : _entitiesInScene) {
|
||||
|
@ -289,7 +318,8 @@ void EntityTreeRenderer::clear() {
|
|||
}
|
||||
} else {
|
||||
if (_wantScripts) {
|
||||
resetEntitiesScriptEngine();
|
||||
resetPersistentEntitiesScriptEngine();
|
||||
resetNonPersistentEntitiesScriptEngine();
|
||||
}
|
||||
if (scene) {
|
||||
for (const auto& entry : _entitiesInScene) {
|
||||
|
@ -313,13 +343,17 @@ void EntityTreeRenderer::clear() {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::reloadEntityScripts() {
|
||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||
_entitiesScriptEngine->resetModuleCache();
|
||||
_persistentEntitiesScriptEngine->unloadAllEntityScripts();
|
||||
_persistentEntitiesScriptEngine->resetModuleCache();
|
||||
_nonPersistentEntitiesScriptEngine->unloadAllEntityScripts();
|
||||
_nonPersistentEntitiesScriptEngine->resetModuleCache();
|
||||
|
||||
for (const auto& entry : _entitiesInScene) {
|
||||
const auto& renderer = entry.second;
|
||||
const auto& entity = renderer->getEntity();
|
||||
if (!entity->getScript().isEmpty()) {
|
||||
_entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), resolveScriptURL(entity->getScript()), true);
|
||||
if (entity && !entity->getScript().isEmpty()) {
|
||||
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
scriptEngine->loadEntityScript(entity->getEntityItemID(), resolveScriptURL(entity->getScript()), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +363,8 @@ void EntityTreeRenderer::init() {
|
|||
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
|
||||
if (_wantScripts) {
|
||||
resetEntitiesScriptEngine();
|
||||
resetPersistentEntitiesScriptEngine();
|
||||
resetNonPersistentEntitiesScriptEngine();
|
||||
}
|
||||
|
||||
forceRecheckEntities(); // setup our state to force checking our inside/outsideness of entities
|
||||
|
@ -341,8 +376,11 @@ void EntityTreeRenderer::init() {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::shutdown() {
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
|
||||
if (_persistentEntitiesScriptEngine) {
|
||||
_persistentEntitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
|
||||
}
|
||||
if (_nonPersistentEntitiesScriptEngine) {
|
||||
_nonPersistentEntitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
|
||||
}
|
||||
_shuttingDown = true;
|
||||
|
||||
|
@ -658,12 +696,16 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
// EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts
|
||||
// for entity IDs that no longer exist.
|
||||
|
||||
if (_entitiesScriptEngine) {
|
||||
if (_persistentEntitiesScriptEngine && _nonPersistentEntitiesScriptEngine) {
|
||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
if (!entitiesContainingAvatar.contains(entityID)) {
|
||||
emit leaveEntity(entityID);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
auto entity = getTree()->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,7 +713,11 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
|
||||
if (!_currentEntitiesInside.contains(entityID)) {
|
||||
emit enterEntity(entityID);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity");
|
||||
auto entity = getTree()->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
scriptEngine->callEntityScriptMethod(entityID, "enterEntity");
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentEntitiesInside = entitiesContainingAvatar;
|
||||
|
@ -687,8 +733,8 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
|
|||
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
|
||||
if (entityItem && !(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
|
||||
emit leaveEntity(entityID);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
if (_nonPersistentEntitiesScriptEngine) {
|
||||
_nonPersistentEntitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
} else {
|
||||
currentEntitiesInsideToSave.insert(entityID);
|
||||
|
@ -706,8 +752,12 @@ void EntityTreeRenderer::leaveAllEntities() {
|
|||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
emit leaveEntity(entityID);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
|
||||
if (entityItem) {
|
||||
auto& scriptEngine = (entityItem->isLocalEntity() || entityItem->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
if (scriptEngine) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentEntitiesInside.clear();
|
||||
|
@ -1003,11 +1053,12 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_tree && !_shuttingDown && _entitiesScriptEngine && !itr->second->getEntity()->getScript().isEmpty()) {
|
||||
auto& scriptEngine = (itr->second->getEntity()->isLocalEntity() || itr->second->getEntity()->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
if (_tree && !_shuttingDown && scriptEngine && !itr->second->getEntity()->getScript().isEmpty()) {
|
||||
if (_currentEntitiesInside.contains(entityID)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
scriptEngine->unloadEntityScript(entityID, true);
|
||||
}
|
||||
|
||||
auto scene = _viewState->getMain3DScene();
|
||||
|
@ -1052,20 +1103,21 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
bool shouldLoad = entity->shouldPreloadScript() && _entitiesScriptEngine;
|
||||
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
bool shouldLoad = entity->shouldPreloadScript() && scriptEngine;
|
||||
QString scriptUrl = entity->getScript();
|
||||
if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) {
|
||||
if (_entitiesScriptEngine) {
|
||||
if (scriptEngine) {
|
||||
if (_currentEntitiesInside.contains(entityID)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
scriptEngine->unloadEntityScript(entityID);
|
||||
}
|
||||
entity->scriptHasUnloaded();
|
||||
}
|
||||
if (shouldLoad) {
|
||||
entity->setScriptHasFinishedPreload(false);
|
||||
_entitiesScriptEngine->loadEntityScript(entityID, resolveScriptURL(scriptUrl), reload);
|
||||
scriptEngine->loadEntityScript(entityID, resolveScriptURL(scriptUrl), reload);
|
||||
entity->scriptHasPreloaded();
|
||||
}
|
||||
}
|
||||
|
@ -1172,8 +1224,9 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
if ((myNodeID == entityASimulatorID && entityAIsDynamic) || (myNodeID == entityBSimulatorID && (!entityAIsDynamic || entityASimulatorID.isNull()))) {
|
||||
playEntityCollisionSound(entityA, collision);
|
||||
emit collisionWithEntity(idA, idB, collision);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision);
|
||||
auto& scriptEngine = (entityA->isLocalEntity() || entityA->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
if (scriptEngine) {
|
||||
scriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1183,8 +1236,9 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
Collision invertedCollision(collision);
|
||||
invertedCollision.invert();
|
||||
emit collisionWithEntity(idB, idA, invertedCollision);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, invertedCollision);
|
||||
auto& scriptEngine = (entityB->isLocalEntity() || entityB->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
if (scriptEngine) {
|
||||
scriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, invertedCollision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,7 +173,9 @@ private:
|
|||
EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); }
|
||||
render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); }
|
||||
|
||||
void resetEntitiesScriptEngine();
|
||||
void resetPersistentEntitiesScriptEngine();
|
||||
void resetNonPersistentEntitiesScriptEngine();
|
||||
void setupEntityScriptEngineSignals(const ScriptEnginePointer& scriptEngine);
|
||||
|
||||
void findBestZoneAndMaybeContainingEntities(QSet<EntityItemID>& entitiesContainingAvatar);
|
||||
|
||||
|
@ -196,7 +198,8 @@ private:
|
|||
QSet<EntityItemID> _currentEntitiesInside;
|
||||
|
||||
bool _wantScripts;
|
||||
ScriptEnginePointer _entitiesScriptEngine;
|
||||
ScriptEnginePointer _nonPersistentEntitiesScriptEngine; // used for domain + non-owned avatar entities, cleared on domain switch
|
||||
ScriptEnginePointer _persistentEntitiesScriptEngine; // used for local + owned avatar entities, persists on domain switch, cleared on reload content
|
||||
|
||||
void playEntityCollisionSound(const EntityItemPointer& entity, const Collision& collision);
|
||||
|
||||
|
@ -214,8 +217,6 @@ private:
|
|||
std::function<RayToEntityIntersectionResult(unsigned int)> _getPrevRayPickResultOperator;
|
||||
std::function<void(unsigned int, bool)> _setPrecisionPickingOperator;
|
||||
|
||||
bool _mouseAndPreloadSignalHandlersConnected { false };
|
||||
|
||||
class LayeredZone {
|
||||
public:
|
||||
LayeredZone(std::shared_ptr<ZoneEntityItem> zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {}
|
||||
|
|
|
@ -431,13 +431,13 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
_visible = entity->getVisible();
|
||||
setIsVisibleInSecondaryCamera(entity->isVisibleInSecondaryCamera());
|
||||
setRenderLayer(entity->getRenderLayer());
|
||||
setPrimitiveMode(entity->getPrimitiveMode());
|
||||
_primitiveMode = entity->getPrimitiveMode();
|
||||
_canCastShadow = entity->getCanCastShadow();
|
||||
setCullWithParent(entity->getCullWithParent());
|
||||
_cauterized = entity->getCauterized();
|
||||
if (entity->needsZoneOcclusionUpdate()) {
|
||||
entity->resetNeedsZoneOcclusionUpdate();
|
||||
setRenderWithZones(entity->getRenderWithZones());
|
||||
_renderWithZones = entity->getRenderWithZones();
|
||||
}
|
||||
entity->setNeedsRenderUpdate(false);
|
||||
});
|
||||
|
@ -469,11 +469,13 @@ void EntityRenderer::onRemoveFromScene(const EntityItemPointer& entity) {
|
|||
void EntityRenderer::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
||||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].push(material);
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
void EntityRenderer::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
|
||||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].remove(material);
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start) {
|
||||
|
|
|
@ -108,18 +108,7 @@ protected:
|
|||
|
||||
virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; }
|
||||
virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; }
|
||||
virtual void setPrimitiveMode(PrimitiveMode value) { _primitiveMode = value; }
|
||||
virtual void setCullWithParent(bool value) { _cullWithParent = value; }
|
||||
virtual void setRenderWithZones(const QVector<QUuid>& renderWithZones) { _renderWithZones = renderWithZones; }
|
||||
|
||||
template <typename F, typename T>
|
||||
T withReadLockResult(const std::function<T()>& f) {
|
||||
T result;
|
||||
withReadLock([&] {
|
||||
result = f();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
signals:
|
||||
void requestRenderUpdate();
|
||||
|
|
|
@ -52,14 +52,11 @@ void GizmoEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
void GizmoEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
bool dirty = false;
|
||||
RingGizmoPropertyGroup ringProperties = entity->getRingProperties();
|
||||
withWriteLock([&] {
|
||||
_gizmoType = entity->getGizmoType();
|
||||
if (_ringProperties != ringProperties) {
|
||||
_ringProperties = ringProperties;
|
||||
dirty = true;
|
||||
|
||||
}
|
||||
});
|
||||
_gizmoType = entity->getGizmoType();
|
||||
if (_ringProperties != ringProperties) {
|
||||
_ringProperties = ringProperties;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty || _prevPrimitiveMode != _primitiveMode || !_ringGeometryID || !_majorTicksGeometryID || !_minorTicksGeometryID) {
|
||||
_prevPrimitiveMode = _primitiveMode;
|
||||
|
@ -242,19 +239,20 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) {
|
|||
|
||||
if (_gizmoType == GizmoType::RING) {
|
||||
Transform transform;
|
||||
bool hasTickMarks;
|
||||
glm::vec4 tickProperties;
|
||||
bool hasTickMarks = _ringProperties.getHasTickMarks();
|
||||
glm::vec4 tickProperties = glm::vec4(_ringProperties.getMajorTickMarksAngle(), _ringProperties.getMajorTickMarksLength(),
|
||||
_ringProperties.getMinorTickMarksAngle(), _ringProperties.getMinorTickMarksLength());
|
||||
bool forward;
|
||||
bool wireframe;
|
||||
bool transparent;
|
||||
withReadLock([&] {
|
||||
transform = _renderTransform;
|
||||
hasTickMarks = _ringProperties.getHasTickMarks();
|
||||
tickProperties = glm::vec4(_ringProperties.getMajorTickMarksAngle(), _ringProperties.getMajorTickMarksLength(),
|
||||
_ringProperties.getMinorTickMarksAngle(), _ringProperties.getMinorTickMarksLength());
|
||||
transparent = isTransparent();
|
||||
wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
|
||||
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD;
|
||||
});
|
||||
|
||||
bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
|
||||
geometryCache->bindSimpleProgram(batch, false, isTransparent(), wireframe, true, true, forward, graphics::MaterialKey::CULL_NONE);
|
||||
geometryCache->bindSimpleProgram(batch, false, transparent, wireframe, true, true, forward, graphics::MaterialKey::CULL_NONE);
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
|
|
|
@ -30,16 +30,6 @@ bool GridEntityRenderer::isTransparent() const {
|
|||
}
|
||||
|
||||
void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
withWriteLock([&] {
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
_followCamera = entity->getFollowCamera();
|
||||
_majorGridEvery = entity->getMajorGridEvery();
|
||||
_minorGridEvery = entity->getMinorGridEvery();
|
||||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
|
@ -49,6 +39,16 @@ void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
});
|
||||
}
|
||||
|
||||
void GridEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
_followCamera = entity->getFollowCamera();
|
||||
_majorGridEvery = entity->getMajorGridEvery();
|
||||
_minorGridEvery = entity->getMinorGridEvery();
|
||||
}
|
||||
|
||||
Item::Bound GridEntityRenderer::getBound() {
|
||||
if (_followCamera) {
|
||||
// This is a UI element that should always be in view, lie to the octree to avoid culling
|
||||
|
@ -73,13 +73,12 @@ ShapeKey GridEntityRenderer::getShapeKey() {
|
|||
}
|
||||
|
||||
void GridEntityRenderer::doRender(RenderArgs* args) {
|
||||
glm::vec4 color;
|
||||
glm::vec4 color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
glm::vec3 dimensions;
|
||||
Transform renderTransform;
|
||||
bool forward;
|
||||
withReadLock([&] {
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
dimensions = _dimensions;
|
||||
renderTransform = _renderTransform;
|
||||
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD;
|
||||
|
|
|
@ -30,10 +30,11 @@ protected:
|
|||
|
||||
private:
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
float _alpha { NAN };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
bool _followCamera;
|
||||
|
|
|
@ -29,44 +29,7 @@ bool ImageEntityRenderer::isTransparent() const {
|
|||
return Parent::isTransparent() || (_textureIsLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha()) || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool ImageEntityRenderer::needsRenderUpdate() const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
return !_textureIsLoaded;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
withWriteLock([&] {
|
||||
auto imageURL = entity->getImageURL();
|
||||
if (_imageURL != imageURL) {
|
||||
_imageURL = imageURL;
|
||||
if (imageURL.isEmpty()) {
|
||||
_texture.reset();
|
||||
} else {
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(_imageURL);
|
||||
}
|
||||
_textureIsLoaded = false;
|
||||
}
|
||||
|
||||
_emissive = entity->getEmissive();
|
||||
_keepAspectRatio = entity->getKeepAspectRatio();
|
||||
_subImage = entity->getSubImage();
|
||||
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
|
||||
if (!_textureIsLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
_textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
||||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
|
@ -76,6 +39,33 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
});
|
||||
}
|
||||
|
||||
void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
auto imageURL = entity->getImageURL();
|
||||
if (_imageURL != imageURL) {
|
||||
_imageURL = imageURL;
|
||||
if (imageURL.isEmpty()) {
|
||||
_texture.reset();
|
||||
} else {
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(_imageURL);
|
||||
}
|
||||
_textureIsLoaded = false;
|
||||
}
|
||||
|
||||
_emissive = entity->getEmissive();
|
||||
_keepAspectRatio = entity->getKeepAspectRatio();
|
||||
_subImage = entity->getSubImage();
|
||||
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
|
||||
if (!_textureIsLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
_textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
||||
}
|
||||
|
||||
Item::Bound ImageEntityRenderer::getBound() {
|
||||
auto bound = Parent::getBound();
|
||||
if (_billboardMode != BillboardMode::NONE) {
|
||||
|
@ -93,33 +83,26 @@ ShapeKey ImageEntityRenderer::getShapeKey() {
|
|||
builder.withTranslucent();
|
||||
}
|
||||
|
||||
withReadLock([&] {
|
||||
if (_emissive) {
|
||||
builder.withUnlit();
|
||||
}
|
||||
if (_emissive) {
|
||||
builder.withUnlit();
|
||||
}
|
||||
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
});
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||
NetworkTexturePointer texture;
|
||||
QRect subImage;
|
||||
glm::vec4 color;
|
||||
glm::vec4 color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
Transform transform;
|
||||
withReadLock([&] {
|
||||
texture = _texture;
|
||||
subImage = _subImage;
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
transform = _renderTransform;
|
||||
});
|
||||
|
||||
if (!_visible || !texture || !texture->isLoaded() || color.a == 0.0f) {
|
||||
if (!_visible || !_texture || !_texture->isLoaded() || color.a == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -129,28 +112,28 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
|||
transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, args->getViewFrustum().getPosition()));
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
batch->setResourceTexture(0, texture->getGPUTexture());
|
||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||
|
||||
float imageWidth = texture->getWidth();
|
||||
float imageHeight = texture->getHeight();
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
|
||||
QRect fromImage;
|
||||
if (subImage.width() <= 0) {
|
||||
if (_subImage.width() <= 0) {
|
||||
fromImage.setX(0);
|
||||
fromImage.setWidth(imageWidth);
|
||||
} else {
|
||||
float scaleX = imageWidth / texture->getOriginalWidth();
|
||||
fromImage.setX(scaleX * subImage.x());
|
||||
fromImage.setWidth(scaleX * subImage.width());
|
||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
||||
fromImage.setX(scaleX * _subImage.x());
|
||||
fromImage.setWidth(scaleX * _subImage.width());
|
||||
}
|
||||
|
||||
if (subImage.height() <= 0) {
|
||||
if (_subImage.height() <= 0) {
|
||||
fromImage.setY(0);
|
||||
fromImage.setHeight(imageHeight);
|
||||
} else {
|
||||
float scaleY = imageHeight / texture->getOriginalHeight();
|
||||
fromImage.setY(scaleY * subImage.y());
|
||||
fromImage.setHeight(scaleY * subImage.height());
|
||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
||||
fromImage.setY(scaleY * _subImage.y());
|
||||
fromImage.setHeight(scaleY * _subImage.height());
|
||||
}
|
||||
|
||||
float maxSize = glm::max(fromImage.width(), fromImage.height());
|
||||
|
|
|
@ -29,8 +29,8 @@ protected:
|
|||
bool isTransparent() const override;
|
||||
|
||||
private:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
QString _imageURL;
|
||||
|
|
|
@ -16,197 +16,177 @@
|
|||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
||||
bool MaterialEntityRenderer::needsRenderUpdate() const {
|
||||
if (_retryApply) {
|
||||
return true;
|
||||
}
|
||||
if (!_texturesLoaded) {
|
||||
return true;
|
||||
}
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
if (entity->getTransform() != _transform) {
|
||||
return true;
|
||||
}
|
||||
if (entity->getUnscaledDimensions() != _dimensions) {
|
||||
return true;
|
||||
}
|
||||
if (entity->getParentID() != _parentID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
_renderTransform = getModelTransform();
|
||||
// Material entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
|
||||
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
||||
const float MATERIAL_ENTITY_SCALE = 0.5f;
|
||||
_renderTransform.postScale(MATERIAL_ENTITY_SCALE);
|
||||
_renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
withWriteLock([&] {
|
||||
bool deleteNeeded = false;
|
||||
bool addNeeded = _retryApply;
|
||||
bool transformChanged = false;
|
||||
{
|
||||
MaterialMappingMode mode = entity->getMaterialMappingMode();
|
||||
if (mode != _materialMappingMode) {
|
||||
_materialMappingMode = mode;
|
||||
transformChanged = true;
|
||||
}
|
||||
bool deleteNeeded = false;
|
||||
bool addNeeded = _retryApply;
|
||||
bool transformChanged = false;
|
||||
{
|
||||
MaterialMappingMode mode = entity->getMaterialMappingMode();
|
||||
if (mode != _materialMappingMode) {
|
||||
_materialMappingMode = mode;
|
||||
transformChanged = true;
|
||||
}
|
||||
{
|
||||
bool repeat = entity->getMaterialRepeat();
|
||||
if (repeat != _materialRepeat) {
|
||||
_materialRepeat = repeat;
|
||||
transformChanged = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
bool repeat = entity->getMaterialRepeat();
|
||||
if (repeat != _materialRepeat) {
|
||||
_materialRepeat = repeat;
|
||||
transformChanged = true;
|
||||
}
|
||||
{
|
||||
glm::vec2 mappingPos = entity->getMaterialMappingPos();
|
||||
glm::vec2 mappingScale = entity->getMaterialMappingScale();
|
||||
float mappingRot = entity->getMaterialMappingRot();
|
||||
if (mappingPos != _materialMappingPos || mappingScale != _materialMappingScale || mappingRot != _materialMappingRot) {
|
||||
_materialMappingPos = mappingPos;
|
||||
_materialMappingScale = mappingScale;
|
||||
_materialMappingRot = mappingRot;
|
||||
transformChanged |= _materialMappingMode == MaterialMappingMode::UV;
|
||||
}
|
||||
}
|
||||
{
|
||||
glm::vec2 mappingPos = entity->getMaterialMappingPos();
|
||||
glm::vec2 mappingScale = entity->getMaterialMappingScale();
|
||||
float mappingRot = entity->getMaterialMappingRot();
|
||||
if (mappingPos != _materialMappingPos || mappingScale != _materialMappingScale || mappingRot != _materialMappingRot) {
|
||||
_materialMappingPos = mappingPos;
|
||||
_materialMappingScale = mappingScale;
|
||||
_materialMappingRot = mappingRot;
|
||||
transformChanged |= _materialMappingMode == MaterialMappingMode::UV;
|
||||
}
|
||||
{
|
||||
Transform transform = entity->getTransform();
|
||||
glm::vec3 dimensions = entity->getUnscaledDimensions();
|
||||
if (transform != _transform || dimensions != _dimensions) {
|
||||
_transform = transform;
|
||||
_dimensions = dimensions;
|
||||
transformChanged |= _materialMappingMode == MaterialMappingMode::PROJECTED;
|
||||
}
|
||||
}
|
||||
{
|
||||
Transform transform = entity->getTransform();
|
||||
glm::vec3 dimensions = entity->getUnscaledDimensions();
|
||||
if (transform != _transform || dimensions != _dimensions) {
|
||||
_transform = transform;
|
||||
_dimensions = dimensions;
|
||||
transformChanged |= _materialMappingMode == MaterialMappingMode::PROJECTED;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto material = getMaterial();
|
||||
// Update the old material regardless of if it's going to change
|
||||
if (transformChanged && material && !_parentID.isNull()) {
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
applyTextureTransform(material);
|
||||
}
|
||||
{
|
||||
auto material = getMaterial();
|
||||
// Update the old material regardless of if it's going to change
|
||||
if (transformChanged && material && !_parentID.isNull()) {
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
applyTextureTransform(material);
|
||||
}
|
||||
}
|
||||
|
||||
bool urlChanged = false;
|
||||
std::string newCurrentMaterialName = _currentMaterialName;
|
||||
{
|
||||
QString materialURL = entity->getMaterialURL();
|
||||
if (materialURL != _materialURL) {
|
||||
_materialURL = materialURL;
|
||||
if (_materialURL.contains("#")) {
|
||||
auto split = _materialURL.split("#");
|
||||
newCurrentMaterialName = split.last().toStdString();
|
||||
} else if (_materialURL.contains("?")) {
|
||||
qDebug() << "DEPRECATED: Use # instead of ? for material URLS:" << _materialURL;
|
||||
auto split = _materialURL.split("?");
|
||||
newCurrentMaterialName = split.last().toStdString();
|
||||
}
|
||||
urlChanged = true;
|
||||
bool urlChanged = false;
|
||||
std::string newCurrentMaterialName = _currentMaterialName;
|
||||
{
|
||||
QString materialURL = entity->getMaterialURL();
|
||||
if (materialURL != _materialURL) {
|
||||
_materialURL = materialURL;
|
||||
if (_materialURL.contains("#")) {
|
||||
auto split = _materialURL.split("#");
|
||||
newCurrentMaterialName = split.last().toStdString();
|
||||
} else if (_materialURL.contains("?")) {
|
||||
qDebug() << "DEPRECATED: Use # instead of ? for material URLS:" << _materialURL;
|
||||
auto split = _materialURL.split("?");
|
||||
newCurrentMaterialName = split.last().toStdString();
|
||||
}
|
||||
urlChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool usingMaterialData = _materialURL.startsWith("materialData");
|
||||
bool materialDataChanged = false;
|
||||
QUuid oldParentID = _parentID;
|
||||
QString oldParentMaterialName = _parentMaterialName;
|
||||
{
|
||||
QString materialData = entity->getMaterialData();
|
||||
if (materialData != _materialData) {
|
||||
_materialData = materialData;
|
||||
if (usingMaterialData) {
|
||||
materialDataChanged = true;
|
||||
}
|
||||
bool usingMaterialData = _materialURL.startsWith("materialData");
|
||||
bool materialDataChanged = false;
|
||||
QUuid oldParentID = _parentID;
|
||||
QString oldParentMaterialName = _parentMaterialName;
|
||||
{
|
||||
QString materialData = entity->getMaterialData();
|
||||
if (materialData != _materialData) {
|
||||
_materialData = materialData;
|
||||
if (usingMaterialData) {
|
||||
materialDataChanged = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
QString parentMaterialName = entity->getParentMaterialName();
|
||||
if (parentMaterialName != _parentMaterialName) {
|
||||
_parentMaterialName = parentMaterialName;
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
QString parentMaterialName = entity->getParentMaterialName();
|
||||
if (parentMaterialName != _parentMaterialName) {
|
||||
_parentMaterialName = parentMaterialName;
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
}
|
||||
{
|
||||
QUuid parentID = entity->getParentID();
|
||||
if (parentID != _parentID) {
|
||||
_parentID = parentID;
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
QUuid parentID = entity->getParentID();
|
||||
if (parentID != _parentID) {
|
||||
_parentID = parentID;
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
}
|
||||
{
|
||||
quint16 priority = entity->getPriority();
|
||||
if (priority != _priority) {
|
||||
_priority = priority;
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
}
|
||||
}
|
||||
{
|
||||
quint16 priority = entity->getPriority();
|
||||
if (priority != _priority) {
|
||||
_priority = priority;
|
||||
deleteNeeded = true;
|
||||
addNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlChanged && !usingMaterialData) {
|
||||
_networkMaterial = DependencyManager::get<MaterialCache>()->getMaterial(_materialURL);
|
||||
auto onMaterialRequestFinished = [this, entity, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) {
|
||||
if (success) {
|
||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||
_texturesLoaded = false;
|
||||
_parsedMaterials = _networkMaterial->parsedMaterials;
|
||||
setCurrentMaterialName(newCurrentMaterialName);
|
||||
applyMaterial(entity);
|
||||
} else {
|
||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||
_retryApply = false;
|
||||
_texturesLoaded = true;
|
||||
}
|
||||
};
|
||||
if (_networkMaterial) {
|
||||
if (_networkMaterial->isLoaded()) {
|
||||
onMaterialRequestFinished(!_networkMaterial->isFailed());
|
||||
} else {
|
||||
connect(_networkMaterial.data(), &Resource::finished, this, [this, onMaterialRequestFinished](bool success) {
|
||||
withWriteLock([&] {
|
||||
onMaterialRequestFinished(success);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (materialDataChanged && usingMaterialData) {
|
||||
if (urlChanged && !usingMaterialData) {
|
||||
_networkMaterial = DependencyManager::get<MaterialCache>()->getMaterial(_materialURL);
|
||||
auto onMaterialRequestFinished = [this, entity, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) {
|
||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||
_texturesLoaded = false;
|
||||
_parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL);
|
||||
// Since our material changed, the current name might not be valid anymore, so we need to update
|
||||
setCurrentMaterialName(newCurrentMaterialName);
|
||||
applyMaterial(entity);
|
||||
} else {
|
||||
if (deleteNeeded) {
|
||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||
}
|
||||
if (addNeeded) {
|
||||
if (success) {
|
||||
_texturesLoaded = false;
|
||||
_parsedMaterials = _networkMaterial->parsedMaterials;
|
||||
setCurrentMaterialName(newCurrentMaterialName);
|
||||
applyMaterial(entity);
|
||||
emit requestRenderUpdate();
|
||||
} else {
|
||||
_retryApply = false;
|
||||
_texturesLoaded = true;
|
||||
}
|
||||
};
|
||||
if (_networkMaterial) {
|
||||
if (_networkMaterial->isLoaded()) {
|
||||
onMaterialRequestFinished(!_networkMaterial->isFailed());
|
||||
} else {
|
||||
connect(_networkMaterial.data(), &Resource::finished, this, [this, onMaterialRequestFinished](bool success) {
|
||||
onMaterialRequestFinished(success);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto material = getMaterial();
|
||||
bool newTexturesLoaded = material ? !material->isMissingTexture() : false;
|
||||
if (!_texturesLoaded && newTexturesLoaded) {
|
||||
material->checkResetOpacityMap();
|
||||
}
|
||||
_texturesLoaded = newTexturesLoaded;
|
||||
} else if (materialDataChanged && usingMaterialData) {
|
||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||
_texturesLoaded = false;
|
||||
_parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL);
|
||||
// Since our material changed, the current name might not be valid anymore, so we need to update
|
||||
setCurrentMaterialName(newCurrentMaterialName);
|
||||
applyMaterial(entity);
|
||||
} else {
|
||||
if (deleteNeeded) {
|
||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||
}
|
||||
if (addNeeded) {
|
||||
applyMaterial(entity);
|
||||
}
|
||||
}
|
||||
|
||||
_renderTransform = getModelTransform();
|
||||
const float MATERIAL_ENTITY_SCALE = 0.5f;
|
||||
_renderTransform.postScale(MATERIAL_ENTITY_SCALE);
|
||||
_renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS);
|
||||
});
|
||||
{
|
||||
auto material = getMaterial();
|
||||
bool newTexturesLoaded = material ? !material->isMissingTexture() : false;
|
||||
if (!_texturesLoaded && newTexturesLoaded) {
|
||||
material->checkResetOpacityMap();
|
||||
}
|
||||
_texturesLoaded = newTexturesLoaded;
|
||||
}
|
||||
|
||||
if (!_texturesLoaded || _retryApply) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
ItemKey MaterialEntityRenderer::getKey() {
|
||||
|
@ -268,33 +248,30 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
|
|||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
// Don't render if our parent is set or our material is null
|
||||
QUuid parentID;
|
||||
withReadLock([&] {
|
||||
parentID = _parentID;
|
||||
});
|
||||
if (!parentID.isNull()) {
|
||||
if (!_parentID.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform renderTransform;
|
||||
graphics::MaterialPointer drawMaterial;
|
||||
graphics::MaterialPointer drawMaterial = getMaterial();
|
||||
bool proceduralRender = false;
|
||||
Transform textureTransform;
|
||||
textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0));
|
||||
textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialMappingRot)));
|
||||
textureTransform.setScale(glm::vec3(_materialMappingScale, 1));
|
||||
|
||||
Transform renderTransform;
|
||||
withReadLock([&] {
|
||||
renderTransform = _renderTransform;
|
||||
drawMaterial = getMaterial();
|
||||
textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0));
|
||||
textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialMappingRot)));
|
||||
textureTransform.setScale(glm::vec3(_materialMappingScale, 1));
|
||||
|
||||
if (drawMaterial && drawMaterial->isProcedural() && drawMaterial->isReady()) {
|
||||
proceduralRender = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!drawMaterial) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drawMaterial->isProcedural() && drawMaterial->isReady()) {
|
||||
proceduralRender = true;
|
||||
}
|
||||
|
||||
batch.setModelTransform(renderTransform);
|
||||
|
||||
if (!proceduralRender) {
|
||||
|
|
|
@ -27,8 +27,7 @@ public:
|
|||
~MaterialEntityRenderer() { deleteMaterial(_parentID, _parentMaterialName); }
|
||||
|
||||
private:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
|
|
|
@ -51,12 +51,6 @@ ModelPointer ModelEntityWrapper::getModel() const {
|
|||
});
|
||||
}
|
||||
|
||||
bool ModelEntityWrapper::isModelLoaded() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _model.operator bool() && _model->isLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity(new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()),
|
||||
[](EntityItem* ptr) { ptr->deleteLater(); });
|
||||
|
@ -69,8 +63,7 @@ EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityI
|
|||
RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized) :
|
||||
ModelEntityWrapper(entityItemID),
|
||||
_dimensionsInitialized(dimensionsInitialized) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
RenderableModelEntityItem::~RenderableModelEntityItem() { }
|
||||
|
@ -83,34 +76,6 @@ void RenderableModelEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::doInitialModelSimulation() {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
||||
ModelPointer model = getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
// The machinery for updateModelBounds will give existing models the opportunity to fix their
|
||||
// translation/rotation/scale/registration. The first two are straightforward, but the latter two have guards to
|
||||
// make sure they don't happen after they've already been set. Here we reset those guards. This doesn't cause the
|
||||
// entity values to change -- it just allows the model to match once it comes in.
|
||||
model->setScaleToFit(false, getScaledDimensions());
|
||||
model->setSnapModelToRegistrationPoint(false, getRegistrationPoint());
|
||||
|
||||
// now recalculate the bounds and registration
|
||||
model->setScaleToFit(true, getScaledDimensions());
|
||||
model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
|
||||
model->setRotation(getWorldOrientation());
|
||||
model->setTranslation(getWorldPosition());
|
||||
|
||||
glm::vec3 scale = model->getScale();
|
||||
model->setUseDualQuaternionSkinning(!isNonUniformScale(scale));
|
||||
|
||||
if (_needsInitialSimulation) {
|
||||
model->simulate(0.0f);
|
||||
_needsInitialSimulation = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::autoResizeJointArrays() {
|
||||
ModelPointer model = getModel();
|
||||
if (model && model->isLoaded() && !_needsInitialSimulation) {
|
||||
|
@ -125,7 +90,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!_dimensionsInitialized || !model->isActive()) {
|
||||
if (!_dimensionsInitialized || !model->isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -182,22 +147,25 @@ void RenderableModelEntityItem::updateModelBounds() {
|
|||
}
|
||||
|
||||
bool overridingModelTransform = model->isOverridingModelTransformAndOffset();
|
||||
glm::vec3 scaledDimensions = getScaledDimensions();
|
||||
glm::vec3 registrationPoint = getRegistrationPoint();
|
||||
if (!overridingModelTransform &&
|
||||
(model->getScaleToFitDimensions() != getScaledDimensions() ||
|
||||
model->getRegistrationPoint() != getRegistrationPoint() ||
|
||||
!model->getIsScaledToFit())) {
|
||||
(model->getScaleToFitDimensions() != scaledDimensions ||
|
||||
model->getRegistrationPoint() != registrationPoint ||
|
||||
!model->getIsScaledToFit() || _needsToRescaleModel)) {
|
||||
// The machinery for updateModelBounds will give existing models the opportunity to fix their
|
||||
// translation/rotation/scale/registration. The first two are straightforward, but the latter two
|
||||
// have guards to make sure they don't happen after they've already been set. Here we reset those guards.
|
||||
// This doesn't cause the entity values to change -- it just allows the model to match once it comes in.
|
||||
model->setScaleToFit(false, getScaledDimensions());
|
||||
model->setSnapModelToRegistrationPoint(false, getRegistrationPoint());
|
||||
model->setScaleToFit(false, scaledDimensions);
|
||||
model->setSnapModelToRegistrationPoint(false, registrationPoint);
|
||||
|
||||
// now recalculate the bounds and registration
|
||||
model->setScaleToFit(true, getScaledDimensions());
|
||||
model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
|
||||
model->setScaleToFit(true, scaledDimensions);
|
||||
model->setSnapModelToRegistrationPoint(true, registrationPoint);
|
||||
updateRenderItems = true;
|
||||
model->scaleToFit();
|
||||
_needsToRescaleModel = false;
|
||||
}
|
||||
|
||||
bool success;
|
||||
|
@ -221,6 +189,9 @@ void RenderableModelEntityItem::updateModelBounds() {
|
|||
glm::vec3 scale = model->getScale();
|
||||
model->setUseDualQuaternionSkinning(!isNonUniformScale(scale));
|
||||
model->updateRenderItems();
|
||||
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
locationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,8 +219,6 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -261,7 +230,7 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
auto model = getModel();
|
||||
if (!model || !isModelLoaded()) {
|
||||
if (!model || !model->isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -273,7 +242,7 @@ bool RenderableModelEntityItem::findDetailedParabolaIntersection(const glm::vec3
|
|||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face,
|
||||
glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
auto model = getModel();
|
||||
if (!model || !isModelLoaded()) {
|
||||
if (!model || !model->isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -281,8 +250,23 @@ bool RenderableModelEntityItem::findDetailedParabolaIntersection(const glm::vec3
|
|||
face, surfaceNormal, extraInfo, precisionPicking, false);
|
||||
}
|
||||
|
||||
QString RenderableModelEntityItem::getCollisionShapeURL() const {
|
||||
return getShapeType() == SHAPE_TYPE_COMPOUND ? getCompoundShapeURL() : getModelURL();
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::fetchCollisionGeometryResource() {
|
||||
_collisionGeometryResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(getCollisionShapeURL());
|
||||
if (_collisionGeometryResource) {
|
||||
if (_collisionGeometryResource->isLoaded()) {
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
locationChanged();
|
||||
} else {
|
||||
connect(_collisionGeometryResource.get(), &GeometryResource::finished, this, [&] {
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
locationChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::unableToLoadCollisionShape() {
|
||||
|
@ -299,7 +283,7 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
|
|||
if (!_collisionGeometryResource && !getCollisionShapeURL().isEmpty()) {
|
||||
fetchCollisionGeometryResource();
|
||||
}
|
||||
} else if (_collisionGeometryResource && !getCompoundShapeURL().isEmpty()) {
|
||||
} else if (_collisionGeometryResource) {
|
||||
// the compoundURL has been set but the shapeType does not agree
|
||||
_collisionGeometryResource.reset();
|
||||
}
|
||||
|
@ -308,49 +292,43 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
|
|||
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
||||
auto currentCompoundShapeURL = getCompoundShapeURL();
|
||||
ModelEntityItem::setCompoundShapeURL(url);
|
||||
if (getCompoundShapeURL() != currentCompoundShapeURL || !getModel()) {
|
||||
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
|
||||
if (url != currentCompoundShapeURL && !url.isEmpty()) {
|
||||
auto shapeType = getShapeType();
|
||||
if (shapeType == SHAPE_TYPE_COMPOUND) {
|
||||
fetchCollisionGeometryResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setModelURL(const QString& url) {
|
||||
auto currentModelURL = getModelURL();
|
||||
ModelEntityItem::setModelURL(url);
|
||||
if (url != currentModelURL && !url.isEmpty()) {
|
||||
auto shapeType = getShapeType();
|
||||
if (shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
fetchCollisionGeometryResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::isReadyToComputeShape() const {
|
||||
ShapeType type = getShapeType();
|
||||
auto model = getModel();
|
||||
auto shapeType = getShapeType();
|
||||
if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
auto shapeURL = getCollisionShapeURL();
|
||||
|
||||
if (!model || shapeURL.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (model->getURL().isEmpty() || !_dimensionsInitialized) {
|
||||
// we need a render geometry with a scale to proceed, so give up.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (model->isLoaded()) {
|
||||
if (!shapeURL.isEmpty() && !_collisionGeometryResource) {
|
||||
// we need a render geometry with a scale to proceed
|
||||
if (model && !model->getURL().isEmpty() && !shapeURL.isEmpty() && _dimensionsInitialized && model->isLoaded()) {
|
||||
if (!_collisionGeometryResource) {
|
||||
const_cast<RenderableModelEntityItem*>(this)->fetchCollisionGeometryResource();
|
||||
}
|
||||
|
||||
if (_collisionGeometryResource && _collisionGeometryResource->isLoaded()) {
|
||||
// we have both URLs AND both geometries AND they are both fully loaded.
|
||||
if (_needsInitialSimulation) {
|
||||
// the _model's offset will be wrong until _needsInitialSimulation is false
|
||||
DETAILED_PERFORMANCE_TIMER("_model->simulate");
|
||||
const_cast<RenderableModelEntityItem*>(this)->doInitialModelSimulation();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// do we have both URLs AND both geometries AND they are both fully loaded?
|
||||
return _collisionGeometryResource && _collisionGeometryResource->isLoaded() && _collisionGeometryResource->isHFMModelLoaded();
|
||||
}
|
||||
|
||||
// the model is still being downloaded.
|
||||
return false;
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
return isModelLoaded();
|
||||
} else if (shapeType >= SHAPE_TYPE_SIMPLE_HULL && shapeType <= SHAPE_TYPE_STATIC_MESH) {
|
||||
return model && model->isLoaded() && _dimensionsInitialized;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -362,20 +340,31 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
ShapeType type = getShapeType();
|
||||
|
||||
auto model = getModel();
|
||||
if (!model || !model->isLoaded()) {
|
||||
type = SHAPE_TYPE_NONE;
|
||||
if (type >= SHAPE_TYPE_COMPOUND && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
if (!model) {
|
||||
type = SHAPE_TYPE_NONE;
|
||||
} else if (!model->isLoaded()) {
|
||||
type = SHAPE_TYPE_NONE;
|
||||
if (!model->didVisualGeometryRequestFail()) {
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
locationChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == SHAPE_TYPE_COMPOUND || type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
if (!_collisionGeometryResource || !_collisionGeometryResource->isLoaded() || !_collisionGeometryResource->isHFMModelLoaded()) {
|
||||
type = SHAPE_TYPE_NONE;
|
||||
if (!_collisionGeometryResource->isFailed()) {
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
locationChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
if (!_collisionGeometryResource || !_collisionGeometryResource->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateModelBounds();
|
||||
|
||||
// should never fall in here when collision model not fully loaded
|
||||
// TODO: assert that all geometries exist and are loaded
|
||||
//assert(_model && _model->isLoaded() && _collisionGeometryResource && _collisionGeometryResource->isLoaded());
|
||||
const HFMModel& collisionGeometry = _collisionGeometryResource->getHFMModel();
|
||||
|
||||
ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection();
|
||||
|
@ -452,7 +441,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
// collision model's extents).
|
||||
|
||||
glm::vec3 dimensions = getScaledDimensions();
|
||||
glm::vec3 scaleToFit = dimensions / model->getHFMModel().getUnscaledMeshExtents().size();
|
||||
glm::vec3 extents = model->getHFMModel().getUnscaledMeshExtents().size();
|
||||
glm::vec3 scaleToFit = dimensions / extents;
|
||||
// multiply each point by scale before handing the point-set off to the physics engine.
|
||||
// also determine the extents of the collision model.
|
||||
glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
|
||||
|
@ -462,12 +452,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + model->getOffset()) - registrationOffset;
|
||||
}
|
||||
}
|
||||
shapeInfo.setParams(type, dimensions, getCompoundShapeURL());
|
||||
shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL());
|
||||
adjustShapeInfoByRegistration(shapeInfo);
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
updateModelBounds();
|
||||
// assert we never fall in here when model not fully loaded
|
||||
assert(model && model->isLoaded());
|
||||
model->updateGeometry();
|
||||
|
||||
// compute meshPart local transforms
|
||||
|
@ -476,16 +464,16 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
int numHFMMeshes = hfmModel.meshes.size();
|
||||
int totalNumVertices = 0;
|
||||
glm::vec3 dimensions = getScaledDimensions();
|
||||
glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||
glm::mat4 invRegistrationOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||
for (int i = 0; i < numHFMMeshes; i++) {
|
||||
const HFMMesh& mesh = hfmModel.meshes.at(i);
|
||||
if (mesh.clusters.size() > 0) {
|
||||
const HFMCluster& cluster = mesh.clusters.at(0);
|
||||
auto jointMatrix = model->getRig().getJointTransform(cluster.jointIndex);
|
||||
// we backtranslate by the registration offset so we can apply that offset to the shapeInfo later
|
||||
localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix);
|
||||
localTransforms.push_back(invRegistrationOffset * jointMatrix * cluster.inverseBindMatrix);
|
||||
} else {
|
||||
localTransforms.push_back(invRegistraionOffset);
|
||||
localTransforms.push_back(invRegistrationOffset);
|
||||
}
|
||||
totalNumVertices += mesh.vertices.size();
|
||||
}
|
||||
|
@ -697,7 +685,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
}
|
||||
}
|
||||
|
||||
shapeInfo.setParams(type, 0.5f * dimensions, getModelURL());
|
||||
shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL());
|
||||
adjustShapeInfoByRegistration(shapeInfo);
|
||||
} else {
|
||||
EntityItem::computeShapeInfo(shapeInfo);
|
||||
|
@ -716,8 +704,8 @@ void RenderableModelEntityItem::setJointMap(std::vector<int> jointMap) {
|
|||
|
||||
int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) {
|
||||
int result = -1;
|
||||
int mapSize = (int) _jointMap.size();
|
||||
if (modelJointIndex >=0 && modelJointIndex < mapSize) {
|
||||
int mapSize = (int)_jointMap.size();
|
||||
if (modelJointIndex >= 0 && modelJointIndex < mapSize) {
|
||||
result = _jointMap[modelJointIndex];
|
||||
}
|
||||
|
||||
|
@ -726,28 +714,44 @@ int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) {
|
|||
|
||||
bool RenderableModelEntityItem::contains(const glm::vec3& point) const {
|
||||
auto model = getModel();
|
||||
if (EntityItem::contains(point) && model && _collisionGeometryResource && _collisionGeometryResource->isLoaded()) {
|
||||
glm::mat4 worldToHFMMatrix = model->getWorldToHFMMatrix();
|
||||
glm::vec3 hfmPoint = worldToHFMMatrix * glm::vec4(point, 1.0f);
|
||||
return _collisionGeometryResource->getHFMModel().convexHullContains(hfmPoint);
|
||||
if (model && model->isLoaded()) {
|
||||
auto shapeType = getShapeType();
|
||||
if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
if (_collisionGeometryResource && _collisionGeometryResource->isLoaded() && _collisionGeometryResource->isHFMModelLoaded() && EntityItem::contains(point)) {
|
||||
glm::mat4 worldToHFMMatrix = model->getWorldToHFMMatrix();
|
||||
glm::vec3 hfmPoint = worldToHFMMatrix * glm::vec4(point, 1.0f);
|
||||
return _collisionGeometryResource->getHFMModel().convexHullContains(hfmPoint);
|
||||
}
|
||||
} else if (shapeType >= SHAPE_TYPE_SIMPLE_HULL && shapeType <= SHAPE_TYPE_STATIC_MESH) {
|
||||
if (EntityItem::contains(point)) {
|
||||
glm::mat4 worldToHFMMatrix = model->getWorldToHFMMatrix();
|
||||
glm::vec3 hfmPoint = worldToHFMMatrix * glm::vec4(point, 1.0f);
|
||||
return model->getHFMModel().convexHullContains(hfmPoint);
|
||||
}
|
||||
} else {
|
||||
return EntityItem::contains(point);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::shouldBePhysical() const {
|
||||
auto model = getModel();
|
||||
// If we have a model, make sure it hasn't failed to download.
|
||||
// If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready.
|
||||
bool physicalModelLoaded = false;
|
||||
ShapeType shapeType = getShapeType();
|
||||
if (model) {
|
||||
if ((shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) {
|
||||
return false;
|
||||
} else if (shapeType != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) {
|
||||
return false;
|
||||
if (shapeType >= SHAPE_TYPE_COMPOUND && shapeType <= SHAPE_TYPE_STATIC_MESH) {
|
||||
auto model = getModel();
|
||||
// If we have a model, make sure it hasn't failed to download.
|
||||
// If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready.
|
||||
physicalModelLoaded = model && !model->didVisualGeometryRequestFail();
|
||||
if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
physicalModelLoaded &= _collisionGeometryResource && !_collisionGeometryResource->isFailed();
|
||||
}
|
||||
} else if (shapeType != SHAPE_TYPE_NONE) {
|
||||
physicalModelLoaded = true;
|
||||
}
|
||||
return !isDead() && shapeType != SHAPE_TYPE_NONE && !isLocalEntity() && QUrl(_modelURL).isValid();
|
||||
|
||||
return physicalModelLoaded && !isDead() && !isLocalEntity() && QUrl(getModelURL()).isValid();
|
||||
}
|
||||
|
||||
int RenderableModelEntityItem::getJointParent(int index) const {
|
||||
|
@ -943,13 +947,13 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics, bool tellChild
|
|||
|
||||
int RenderableModelEntityItem::getJointIndex(const QString& name) const {
|
||||
auto model = getModel();
|
||||
return (model && model->isActive()) ? model->getRig().indexOfJoint(name) : -1;
|
||||
return (model && model->isLoaded()) ? model->getRig().indexOfJoint(name) : -1;
|
||||
}
|
||||
|
||||
QStringList RenderableModelEntityItem::getJointNames() const {
|
||||
QStringList result;
|
||||
auto model = getModel();
|
||||
if (model && model->isActive()) {
|
||||
if (model && model->isLoaded()) {
|
||||
const Rig& rig = model->getRig();
|
||||
int jointCount = rig.getJointStateCount();
|
||||
for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) {
|
||||
|
@ -960,13 +964,13 @@ QStringList RenderableModelEntityItem::getJointNames() const {
|
|||
}
|
||||
|
||||
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() {
|
||||
auto model = resultWithReadLock<ModelPointer>([this]{ return _model; });
|
||||
auto model = resultWithReadLock<ModelPointer>([&] { return _model; });
|
||||
|
||||
if (!model || !model->isLoaded()) {
|
||||
return scriptable::ScriptableModelBase();
|
||||
}
|
||||
|
||||
auto result = _model->getScriptableModel();
|
||||
auto result = model->getScriptableModel();
|
||||
result.objectID = getEntity()->getID();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
|
@ -1036,7 +1040,7 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
|||
});
|
||||
|
||||
if (changed) {
|
||||
locationChanged(true, true);
|
||||
locationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1054,10 +1058,14 @@ ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Pare
|
|||
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) {
|
||||
void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed, const ModelPointer& model) {
|
||||
auto builder = ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
|
||||
if (!_cullWithParent && _model && _model->isGroupCulled()) {
|
||||
if (!_visible) {
|
||||
builder.withInvisible();
|
||||
}
|
||||
|
||||
if (!_cullWithParent && model && model->isGroupCulled()) {
|
||||
builder.withMetaCullGroup();
|
||||
} else if (_cullWithParent) {
|
||||
builder.withSubMetaCulled();
|
||||
|
@ -1075,8 +1083,9 @@ ItemKey ModelEntityRenderer::getKey() {
|
|||
}
|
||||
|
||||
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const {
|
||||
if (_model) {
|
||||
auto metaSubItems = _model->fetchRenderItemIDs();
|
||||
auto model = resultWithReadLock<ModelPointer>([&] { return _model; });
|
||||
if (model) {
|
||||
auto metaSubItems = model->fetchRenderItemIDs();
|
||||
subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end());
|
||||
return (uint32_t)metaSubItems.size();
|
||||
}
|
||||
|
@ -1089,8 +1098,9 @@ void ModelEntityRenderer::handleBlendedVertices(int blendshapeNumber, const QVec
|
|||
}
|
||||
|
||||
void ModelEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& transaction) {
|
||||
if (_model) {
|
||||
_model->removeFromScene(scene, transaction);
|
||||
auto model = resultWithReadLock<ModelPointer>([&] { return _model; });
|
||||
if (model) {
|
||||
model->removeFromScene(scene, transaction);
|
||||
}
|
||||
Parent::removeFromScene(scene, transaction);
|
||||
}
|
||||
|
@ -1099,14 +1109,14 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit
|
|||
entity->setModel({});
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
||||
void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelPointer& model) {
|
||||
if (!_animation || !_animation->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<EntityJointData> jointsData;
|
||||
|
||||
const QVector<HFMAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||
const QVector<HFMAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||
int frameCount = frames.size();
|
||||
if (frameCount <= 0) {
|
||||
return;
|
||||
|
@ -1124,17 +1134,17 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
_lastKnownCurrentFrame = currentIntegerFrame;
|
||||
}
|
||||
|
||||
if (_jointMapping.size() != _model->getJointStateCount()) {
|
||||
if (_jointMapping.size() != model->getJointStateCount()) {
|
||||
qCWarning(entitiesrenderer) << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch"
|
||||
<< _jointMapping.size() << _model->getJointStateCount();
|
||||
<< _jointMapping.size() << model->getJointStateCount();
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList animationJointNames = _animation->getHFMModel().getJointNames();
|
||||
auto& hfmJoints = _animation->getHFMModel().joints;
|
||||
|
||||
auto& originalHFMJoints = _model->getHFMModel().joints;
|
||||
auto& originalHFMIndices = _model->getHFMModel().jointIndices;
|
||||
auto& originalHFMJoints = model->getHFMModel().joints;
|
||||
auto& originalHFMIndices = model->getHFMModel().jointIndices;
|
||||
|
||||
bool allowTranslation = entity->getAnimationAllowTranslation();
|
||||
|
||||
|
@ -1182,146 +1192,49 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
entity->copyAnimationJointDataToModel();
|
||||
}
|
||||
|
||||
bool ModelEntityRenderer::needsRenderUpdate() const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
if (_moving || _animating) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_texturesLoaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_prevModelLoaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})) {
|
||||
bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (entity->blendshapesChanged()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ModelPointer model;
|
||||
QUrl parsedModelURL;
|
||||
withReadLock([&] {
|
||||
model = _model;
|
||||
parsedModelURL = _parsedModelURL;
|
||||
});
|
||||
|
||||
if (model) {
|
||||
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
|
||||
// we will watch for that and ask the model to update it's render items
|
||||
if (parsedModelURL != model->getURL()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model->needsReload()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model->needsFixupInScene()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model->getRenderItemsNeedUpdate()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
if (entity->hasModel() != _hasModel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No model to render, early exit
|
||||
if (!_hasModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_animating != entity->isAnimatingSomething()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})) { return true; }
|
||||
|
||||
ModelPointer model;
|
||||
withReadLock([&] {
|
||||
model = _model;
|
||||
});
|
||||
|
||||
if (model && model->isLoaded()) {
|
||||
if (!entity->_dimensionsInitialized || entity->_needsInitialSimulation || !entity->_originalTexturesRead) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity->blendshapesChanged()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check to see if we need to update the model bounds
|
||||
if (entity->needsUpdateModelBounds()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check to see if we need to update the model bounds
|
||||
auto transform = entity->getTransform();
|
||||
if (model->getTranslation() != transform.getTranslation() ||
|
||||
model->getRotation() != transform.getRotation()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model->getScaleToFitDimensions() != entity->getScaledDimensions() ||
|
||||
model->getRegistrationPoint() != entity->getRegistrationPoint()) {
|
||||
return true;
|
||||
}
|
||||
// Check to see if we need to update the model bounds
|
||||
if (entity->needsUpdateModelBounds()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Parent::needsRenderUpdateFromTypedEntity(entity);
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
||||
if (_hasModel != entity->hasModel()) {
|
||||
withWriteLock([&] {
|
||||
_hasModel = entity->hasModel();
|
||||
});
|
||||
|
||||
_hasModel = entity->hasModel();
|
||||
QUrl modelURL = QUrl(entity->getModelURL());
|
||||
if (_parsedModelURL != modelURL) {
|
||||
_parsedModelURL = modelURL;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_animating = entity->isAnimatingSomething();
|
||||
if (_parsedModelURL != entity->getModelURL()) {
|
||||
_parsedModelURL = QUrl(entity->getModelURL());
|
||||
}
|
||||
ModelPointer model = resultWithReadLock<ModelPointer>([&] {
|
||||
return _model;
|
||||
});
|
||||
|
||||
ModelPointer model;
|
||||
withReadLock([&] { model = _model; });
|
||||
bool visuallyReady = model && model->isLoaded() && _didLastVisualGeometryRequestSucceed && _texturesLoaded;
|
||||
entity->setVisuallyReady(visuallyReady);
|
||||
|
||||
withWriteLock([&] {
|
||||
bool visuallyReady = true;
|
||||
if (_hasModel) {
|
||||
if (model && _didLastVisualGeometryRequestSucceed) {
|
||||
visuallyReady = (_prevModelLoaded && _texturesLoaded);
|
||||
}
|
||||
}
|
||||
entity->setVisuallyReady(visuallyReady);
|
||||
});
|
||||
const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
|
||||
// Check for removal
|
||||
if (!_hasModel) {
|
||||
if (model) {
|
||||
model->removeFromScene(scene, transaction);
|
||||
entity->bumpAncestorChainRenderableVersion();
|
||||
withWriteLock([&] { _model.reset(); });
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, _model);
|
||||
modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, model);
|
||||
withWriteLock([&] { _model.reset(); });
|
||||
}
|
||||
setKey(false);
|
||||
_didLastVisualGeometryRequestSucceed = false;
|
||||
setKey(_didLastVisualGeometryRequestSucceed, model);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1330,48 +1243,52 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
model = std::make_shared<Model>(nullptr, entity.get(), _created);
|
||||
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
|
||||
connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) {
|
||||
setKey(didVisualGeometryRequestSucceed);
|
||||
_model->setTagMask(getTagMask());
|
||||
_model->setHifiRenderLayer(getHifiRenderLayer());
|
||||
_model->setPrimitiveMode(_primitiveMode);
|
||||
_model->setCullWithParent(_cullWithParent);
|
||||
_model->setRenderWithZones(_renderWithZones);
|
||||
emit requestRenderUpdate();
|
||||
if (didVisualGeometryRequestSucceed) {
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model);
|
||||
}
|
||||
_didLastVisualGeometryRequestSucceed = didVisualGeometryRequestSucceed;
|
||||
const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem<PayloadProxyInterface>(_renderItemID, [&](PayloadProxyInterface& self) {
|
||||
const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
withWriteLock([&] {
|
||||
setKey(didVisualGeometryRequestSucceed, _model);
|
||||
_model->setVisibleInScene(_visible, scene);
|
||||
_model->setCauterized(_cauterized, scene);
|
||||
_model->setCanCastShadow(_canCastShadow, scene);
|
||||
_model->setGroupCulled(entity->getGroupCulled(), scene);
|
||||
_model->setTagMask(getTagMask(), scene);
|
||||
_model->setHifiRenderLayer(getHifiRenderLayer(), scene);
|
||||
_model->setPrimitiveMode(_primitiveMode, scene);
|
||||
_model->setCullWithParent(_cullWithParent, scene);
|
||||
_model->setRenderWithZones(_renderWithZones, scene);
|
||||
});
|
||||
if (didVisualGeometryRequestSucceed) {
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, model);
|
||||
}
|
||||
entity->_originalTexturesRead = false;
|
||||
entity->_needsJointSimulation = true;
|
||||
entity->_needsToRescaleModel = true;
|
||||
emit requestRenderUpdate();
|
||||
});
|
||||
scene->enqueueTransaction(transaction);
|
||||
});
|
||||
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
|
||||
entity->setModel(model);
|
||||
withWriteLock([&] { _model = model; });
|
||||
}
|
||||
|
||||
// From here on, we are guaranteed a populated model
|
||||
if (_parsedModelURL != model->getURL()) {
|
||||
withWriteLock([&] {
|
||||
_texturesLoaded = false;
|
||||
_jointMappingCompleted = false;
|
||||
model->setURL(_parsedModelURL);
|
||||
});
|
||||
_texturesLoaded = false;
|
||||
_jointMappingCompleted = false;
|
||||
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
|
||||
model->setURL(_parsedModelURL);
|
||||
}
|
||||
|
||||
// Nothing else to do unless the model is loaded
|
||||
if (!model->isLoaded()) {
|
||||
withWriteLock([&] {
|
||||
_prevModelLoaded = false;
|
||||
});
|
||||
emit requestRenderUpdate();
|
||||
return;
|
||||
} else if (!_prevModelLoaded) {
|
||||
withWriteLock([&] {
|
||||
_prevModelLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
// Check for initializing the model
|
||||
// FIXME: There are several places below here where we are modifying the entity, which we should not be doing from the renderable
|
||||
if (!entity->_dimensionsInitialized) {
|
||||
EntityItemProperties properties;
|
||||
properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it
|
||||
|
@ -1391,53 +1308,39 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
entity->_originalTexturesRead = true;
|
||||
}
|
||||
|
||||
if (_textures != entity->getTextures()) {
|
||||
auto textures = entity->getTextures();
|
||||
if (_textures != textures) {
|
||||
QVariantMap newTextures;
|
||||
withWriteLock([&] {
|
||||
_texturesLoaded = false;
|
||||
_textures = entity->getTextures();
|
||||
newTextures = parseTexturesToMap(_textures, entity->_originalTextures);
|
||||
});
|
||||
_texturesLoaded = false;
|
||||
_textures = textures;
|
||||
newTextures = parseTexturesToMap(_textures, entity->_originalTextures);
|
||||
model->setTextures(newTextures);
|
||||
}
|
||||
|
||||
if (entity->_needsJointSimulation) {
|
||||
entity->copyAnimationJointDataToModel();
|
||||
}
|
||||
entity->updateModelBounds();
|
||||
entity->stopModelOverrideIfNoParent();
|
||||
|
||||
if (model->isVisible() != _visible) {
|
||||
withWriteLock([&] {
|
||||
setKey(_didLastVisualGeometryRequestSucceed, model);
|
||||
model->setVisibleInScene(_visible, scene);
|
||||
}
|
||||
|
||||
if (model->isCauterized() != _cauterized) {
|
||||
model->setCauterized(_cauterized, scene);
|
||||
}
|
||||
|
||||
render::hifi::Tag tagMask = getTagMask();
|
||||
if (model->getTagMask() != tagMask) {
|
||||
model->setTagMask(tagMask, scene);
|
||||
}
|
||||
model->setCanCastShadow(_canCastShadow, scene);
|
||||
model->setGroupCulled(entity->getGroupCulled(), scene);
|
||||
model->setTagMask(getTagMask(), scene);
|
||||
model->setHifiRenderLayer(getHifiRenderLayer(), scene);
|
||||
model->setPrimitiveMode(_primitiveMode, scene);
|
||||
model->setCullWithParent(_cullWithParent, scene);
|
||||
model->setRenderWithZones(_renderWithZones, scene);
|
||||
});
|
||||
|
||||
if (entity->blendshapesChanged()) {
|
||||
model->setBlendshapeCoefficients(entity->getBlendshapeCoefficientVector());
|
||||
model->updateBlendshapes();
|
||||
}
|
||||
|
||||
// TODO? early exit here when not visible?
|
||||
|
||||
if (model->canCastShadow() != _canCastShadow) {
|
||||
model->setCanCastShadow(_canCastShadow, scene);
|
||||
}
|
||||
|
||||
{
|
||||
bool groupCulled = entity->getGroupCulled();
|
||||
if (model->isGroupCulled() != groupCulled) {
|
||||
model->setGroupCulled(groupCulled);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Fixup");
|
||||
if (model->needsFixupInScene()) {
|
||||
|
@ -1451,11 +1354,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
}
|
||||
|
||||
bool needsUpdate = false;
|
||||
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||
withWriteLock([&] {
|
||||
_texturesLoaded = true;
|
||||
});
|
||||
model->updateRenderItems();
|
||||
_texturesLoaded = true;
|
||||
needsUpdate = true;
|
||||
} else if (!_texturesLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
@ -1481,19 +1383,23 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
emit requestRenderUpdate();
|
||||
} else {
|
||||
_allProceduralMaterialsLoaded = true;
|
||||
model->setRenderItemsNeedUpdate();
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
|
||||
// we will watch for that and ask the model to update it's render items
|
||||
if (model->getRenderItemsNeedUpdate()) {
|
||||
if (needsUpdate || model->getRenderItemsNeedUpdate()) {
|
||||
model->updateRenderItems();
|
||||
}
|
||||
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
// The code to deal with the change of properties is now in ModelEntityItem.cpp
|
||||
// That is where _currentFrame and _lastAnimated were updated.
|
||||
if (_animating) {
|
||||
if (entity->isAnimatingSomething()) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||
|
||||
auto animationURL = entity->getAnimationURL();
|
||||
|
@ -1502,18 +1408,22 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_animationURL = animationURL;
|
||||
|
||||
if (_animation) {
|
||||
//(_animation->getURL().toString() != entity->getAnimationURL())) { // bad check
|
||||
// the joints have been mapped before but we have a new animation to load
|
||||
_animation.reset();
|
||||
_jointMappingCompleted = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_jointMappingCompleted) {
|
||||
mapJoints(entity, model);
|
||||
}
|
||||
if (entity->readyToAnimate()) {
|
||||
animate(entity);
|
||||
|
||||
ModelPointer model = resultWithReadLock<ModelPointer>([&] {
|
||||
return _model;
|
||||
});
|
||||
if (model && model->isLoaded()) {
|
||||
if (!_jointMappingCompleted) {
|
||||
mapJoints(entity, model);
|
||||
}
|
||||
if (entity->readyToAnimate()) {
|
||||
animate(entity, model);
|
||||
}
|
||||
}
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
@ -1521,40 +1431,20 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
|
||||
void ModelEntityRenderer::setIsVisibleInSecondaryCamera(bool value) {
|
||||
Parent::setIsVisibleInSecondaryCamera(value);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
if (_model) {
|
||||
_model->setTagMask(getTagMask());
|
||||
}
|
||||
// called within a lock so no need to lock for _model
|
||||
setKey(_didLastVisualGeometryRequestSucceed, _model);
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setRenderLayer(RenderLayer value) {
|
||||
Parent::setRenderLayer(value);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
if (_model) {
|
||||
_model->setHifiRenderLayer(getHifiRenderLayer());
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setPrimitiveMode(PrimitiveMode value) {
|
||||
Parent::setPrimitiveMode(value);
|
||||
if (_model) {
|
||||
_model->setPrimitiveMode(_primitiveMode);
|
||||
}
|
||||
// called within a lock so no need to lock for _model
|
||||
setKey(_didLastVisualGeometryRequestSucceed, _model);
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setCullWithParent(bool value) {
|
||||
Parent::setCullWithParent(value);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
if (_model) {
|
||||
_model->setCullWithParent(_cullWithParent);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setRenderWithZones(const QVector<QUuid>& renderWithZones) {
|
||||
Parent::setRenderWithZones(renderWithZones);
|
||||
if (_model) {
|
||||
_model->setRenderWithZones(renderWithZones);
|
||||
}
|
||||
// called within a lock so no need to lock for _model
|
||||
setKey(_didLastVisualGeometryRequestSucceed, _model);
|
||||
}
|
||||
|
||||
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items
|
||||
|
@ -1570,9 +1460,8 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
|
|||
geometryCache->renderWireCubeInstance(args, batch, greenColor, geometryCache->getShapePipelinePointer(false, false, args->_renderMethod == Args::RenderMethod::FORWARD));
|
||||
|
||||
#if WANT_EXTRA_DEBUGGING
|
||||
ModelPointer model;
|
||||
withReadLock([&] {
|
||||
model = _model;
|
||||
ModelPointer model = resultWithReadLock<ModelPointer>([&] {
|
||||
return _model;
|
||||
});
|
||||
if (model) {
|
||||
model->renderDebugMeshBoxes(batch, args->_renderMethod == Args::RenderMethod::FORWARD);
|
||||
|
|
|
@ -41,7 +41,6 @@ protected:
|
|||
ModelEntityWrapper(const EntityItemID& entityItemID) : Parent(entityItemID) {}
|
||||
void setModel(const ModelPointer& model);
|
||||
ModelPointer getModel() const;
|
||||
bool isModelLoaded() const;
|
||||
|
||||
bool _needsInitialSimulation{ true };
|
||||
private:
|
||||
|
@ -62,7 +61,6 @@ public:
|
|||
virtual void setUnscaledDimensions(const glm::vec3& value) override;
|
||||
|
||||
virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
|
||||
void doInitialModelSimulation();
|
||||
void updateModelBounds();
|
||||
|
||||
virtual bool supportsDetailedIntersection() const override;
|
||||
|
@ -77,6 +75,7 @@ public:
|
|||
|
||||
virtual void setShapeType(ShapeType type) override;
|
||||
virtual void setCompoundShapeURL(const QString& url) override;
|
||||
virtual void setModelURL(const QString& url) override;
|
||||
|
||||
virtual bool isReadyToComputeShape() const override;
|
||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo) override;
|
||||
|
@ -121,6 +120,8 @@ private:
|
|||
bool readyToAnimate() const;
|
||||
void fetchCollisionGeometryResource();
|
||||
|
||||
QString getCollisionShapeURL() const;
|
||||
|
||||
GeometryResource::Pointer _collisionGeometryResource;
|
||||
std::vector<int> _jointMap;
|
||||
QVariantMap _originalTextures;
|
||||
|
@ -128,6 +129,7 @@ private:
|
|||
bool _originalTexturesRead { false };
|
||||
bool _dimensionsInitialized { true };
|
||||
bool _needsJointSimulation { false };
|
||||
bool _needsToRescaleModel { false };
|
||||
};
|
||||
|
||||
namespace render { namespace entities {
|
||||
|
@ -153,25 +155,23 @@ protected:
|
|||
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
|
||||
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
void setKey(bool didVisualGeometryRequestSucceed);
|
||||
void setKey(bool didVisualGeometryRequestSucceed, const ModelPointer& model);
|
||||
virtual ItemKey getKey() override;
|
||||
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
|
||||
virtual void handleBlendedVertices(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
|
||||
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs) override;
|
||||
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
void setIsVisibleInSecondaryCamera(bool value) override;
|
||||
void setRenderLayer(RenderLayer value) override;
|
||||
void setPrimitiveMode(PrimitiveMode value) override;
|
||||
void setCullWithParent(bool value) override;
|
||||
void setRenderWithZones(const QVector<QUuid>& renderWithZones) override;
|
||||
|
||||
private:
|
||||
void animate(const TypedEntityPointer& entity);
|
||||
void animate(const TypedEntityPointer& entity, const ModelPointer& model);
|
||||
void mapJoints(const TypedEntityPointer& entity, const ModelPointer& model);
|
||||
|
||||
// Transparency is handled in ModelMeshPartPayload
|
||||
|
@ -186,20 +186,16 @@ private:
|
|||
bool _hasTransitioned{ false };
|
||||
#endif
|
||||
|
||||
const void* _collisionMeshKey { nullptr };
|
||||
|
||||
QUrl _parsedModelURL;
|
||||
bool _jointMappingCompleted { false };
|
||||
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
||||
AnimationPointer _animation;
|
||||
bool _animating { false };
|
||||
QString _animationURL;
|
||||
uint64_t _lastAnimated { 0 };
|
||||
|
||||
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
|
||||
|
||||
bool _didLastVisualGeometryRequestSucceed { true };
|
||||
bool _prevModelLoaded { false };
|
||||
|
||||
void processMaterials();
|
||||
bool _allProceduralMaterialsLoaded { false };
|
||||
|
|
|
@ -64,84 +64,7 @@ ParticleEffectEntityRenderer::ParticleEffectEntityRenderer(const EntityItemPoint
|
|||
});
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityRenderer::needsRenderUpdate() const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
return !_textureLoaded;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
auto newParticleProperties = entity->getParticleProperties();
|
||||
if (!newParticleProperties.valid()) {
|
||||
qCWarning(entitiesrenderer) << "Bad particle properties";
|
||||
}
|
||||
|
||||
if (resultWithReadLock<bool>([&] { return _particleProperties != newParticleProperties; })) {
|
||||
_timeUntilNextEmit = 0;
|
||||
withWriteLock([&] {
|
||||
_particleProperties = newParticleProperties;
|
||||
if (!_prevEmitterShouldTrailInitialized) {
|
||||
_prevEmitterShouldTrailInitialized = true;
|
||||
_prevEmitterShouldTrail = _particleProperties.emission.shouldTrail;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_shapeType = entity->getShapeType();
|
||||
QString compoundShapeURL = entity->getCompoundShapeURL();
|
||||
if (_compoundShapeURL != compoundShapeURL) {
|
||||
_compoundShapeURL = compoundShapeURL;
|
||||
_hasComputedTriangles = false;
|
||||
fetchGeometryResource();
|
||||
}
|
||||
});
|
||||
_emitting = entity->getIsEmitting();
|
||||
|
||||
bool textureEmpty = resultWithReadLock<bool>([&] { return _particleProperties.textures.isEmpty(); });
|
||||
if (textureEmpty) {
|
||||
if (_networkTexture) {
|
||||
withWriteLock([&] {
|
||||
_networkTexture.reset();
|
||||
});
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_textureLoaded = true;
|
||||
entity->setVisuallyReady(true);
|
||||
});
|
||||
} else {
|
||||
bool textureNeedsUpdate = resultWithReadLock<bool>([&] {
|
||||
return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
|
||||
});
|
||||
if (textureNeedsUpdate) {
|
||||
withWriteLock([&] {
|
||||
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
|
||||
_textureLoaded = false;
|
||||
entity->setVisuallyReady(false);
|
||||
});
|
||||
}
|
||||
|
||||
if (!_textureLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
bool textureLoaded = resultWithReadLock<bool>([&] {
|
||||
return _networkTexture && (_networkTexture->isLoaded() || _networkTexture->isFailed());
|
||||
});
|
||||
if (textureLoaded) {
|
||||
withWriteLock([&] {
|
||||
entity->setVisuallyReady(true);
|
||||
_textureLoaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] {
|
||||
withWriteLock([&] {
|
||||
|
@ -151,20 +74,66 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
}
|
||||
|
||||
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
auto newParticleProperties = entity->getParticleProperties();
|
||||
if (!newParticleProperties.valid()) {
|
||||
qCWarning(entitiesrenderer) << "Bad particle properties";
|
||||
}
|
||||
|
||||
if (_particleProperties != newParticleProperties) {
|
||||
_timeUntilNextEmit = 0;
|
||||
_particleProperties = newParticleProperties;
|
||||
if (!_prevEmitterShouldTrailInitialized) {
|
||||
_prevEmitterShouldTrailInitialized = true;
|
||||
_prevEmitterShouldTrail = _particleProperties.emission.shouldTrail;
|
||||
}
|
||||
}
|
||||
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_shapeType = entity->getShapeType();
|
||||
QString compoundShapeURL = entity->getCompoundShapeURL();
|
||||
if (_compoundShapeURL != compoundShapeURL) {
|
||||
_compoundShapeURL = compoundShapeURL;
|
||||
_hasComputedTriangles = false;
|
||||
fetchGeometryResource();
|
||||
}
|
||||
_emitting = entity->getIsEmitting();
|
||||
|
||||
if (_particleProperties.textures.isEmpty()) {
|
||||
if (_networkTexture) {
|
||||
_networkTexture.reset();
|
||||
}
|
||||
|
||||
_textureLoaded = true;
|
||||
entity->setVisuallyReady(true);
|
||||
} else {
|
||||
if (!_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures)) {
|
||||
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
|
||||
_textureLoaded = false;
|
||||
entity->setVisuallyReady(false);
|
||||
}
|
||||
|
||||
if (!_textureLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
if (_networkTexture && (_networkTexture->isLoaded() || _networkTexture->isFailed())) {
|
||||
entity->setVisuallyReady(true);
|
||||
_textureLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in Uniforms structure
|
||||
ParticleUniforms particleUniforms;
|
||||
withReadLock([&] {
|
||||
particleUniforms.radius.start = _particleProperties.radius.range.start;
|
||||
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
|
||||
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
|
||||
particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
|
||||
particleUniforms.spin.start = _particleProperties.spin.range.start;
|
||||
particleUniforms.spin.middle = _particleProperties.spin.gradient.target;
|
||||
particleUniforms.spin.finish = _particleProperties.spin.range.finish;
|
||||
particleUniforms.spin.spread = _particleProperties.spin.gradient.spread;
|
||||
particleUniforms.lifespan = _particleProperties.lifespan;
|
||||
particleUniforms.rotateWithEntity = _particleProperties.rotateWithEntity ? 1 : 0;
|
||||
});
|
||||
particleUniforms.radius.start = _particleProperties.radius.range.start;
|
||||
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
|
||||
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
|
||||
particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
|
||||
particleUniforms.spin.start = _particleProperties.spin.range.start;
|
||||
particleUniforms.spin.middle = _particleProperties.spin.gradient.target;
|
||||
particleUniforms.spin.finish = _particleProperties.spin.range.finish;
|
||||
particleUniforms.spin.spread = _particleProperties.spin.gradient.spread;
|
||||
particleUniforms.lifespan = _particleProperties.lifespan;
|
||||
particleUniforms.rotateWithEntity = _particleProperties.rotateWithEntity ? 1 : 0;
|
||||
// Update particle uniforms
|
||||
_uniformBuffer.edit<ParticleUniforms>() = particleUniforms;
|
||||
}
|
||||
|
@ -403,27 +372,18 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
const auto interval = std::min<uint64_t>(USECS_PER_SECOND / 60, now - _lastSimulated);
|
||||
_lastSimulated = now;
|
||||
|
||||
particle::Properties particleProperties;
|
||||
ShapeType shapeType;
|
||||
GeometryResource::Pointer geometryResource;
|
||||
withReadLock([&] {
|
||||
particleProperties = _particleProperties;
|
||||
shapeType = _shapeType;
|
||||
geometryResource = _geometryResource;
|
||||
});
|
||||
|
||||
const auto& modelTransform = getModelTransform();
|
||||
if (_emitting && particleProperties.emitting() &&
|
||||
(shapeType != SHAPE_TYPE_COMPOUND || (geometryResource && geometryResource->isLoaded()))) {
|
||||
uint64_t emitInterval = particleProperties.emitIntervalUsecs();
|
||||
if (_emitting && _particleProperties.emitting() &&
|
||||
(_shapeType != SHAPE_TYPE_COMPOUND || (_geometryResource && _geometryResource->isLoaded()))) {
|
||||
uint64_t emitInterval = _particleProperties.emitIntervalUsecs();
|
||||
if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
|
||||
auto timeRemaining = interval;
|
||||
while (timeRemaining > _timeUntilNextEmit) {
|
||||
if (_shapeType == SHAPE_TYPE_COMPOUND && !_hasComputedTriangles) {
|
||||
computeTriangles(geometryResource->getHFMModel());
|
||||
computeTriangles(_geometryResource->getHFMModel());
|
||||
}
|
||||
// emit particle
|
||||
_cpuParticles.push_back(createParticle(modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo));
|
||||
_cpuParticles.push_back(createParticle(modelTransform, _particleProperties, _shapeType, _geometryResource, _triangleInfo));
|
||||
_timeUntilNextEmit = emitInterval;
|
||||
if (emitInterval < timeRemaining) {
|
||||
timeRemaining -= emitInterval;
|
||||
|
@ -435,14 +395,14 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
}
|
||||
|
||||
// Kill any particles that have expired or are over the max size
|
||||
while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration == 0)) {
|
||||
while (_cpuParticles.size() > _particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration == 0)) {
|
||||
_cpuParticles.pop_front();
|
||||
}
|
||||
|
||||
const float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
||||
// update the particles
|
||||
for (auto& particle : _cpuParticles) {
|
||||
if (_prevEmitterShouldTrail != particleProperties.emission.shouldTrail) {
|
||||
if (_prevEmitterShouldTrail != _particleProperties.emission.shouldTrail) {
|
||||
if (_prevEmitterShouldTrail) {
|
||||
particle.relativePosition = particle.relativePosition + particle.basePosition - modelTransform.getTranslation();
|
||||
}
|
||||
|
@ -451,14 +411,14 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
particle.expiration = particle.expiration >= interval ? particle.expiration - interval : 0;
|
||||
particle.integrate(deltaTime);
|
||||
}
|
||||
_prevEmitterShouldTrail = particleProperties.emission.shouldTrail;
|
||||
_prevEmitterShouldTrail = _particleProperties.emission.shouldTrail;
|
||||
|
||||
// Build particle primitives
|
||||
static GpuParticles gpuParticles;
|
||||
gpuParticles.clear();
|
||||
gpuParticles.reserve(_cpuParticles.size()); // Reserve space
|
||||
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform] (const CpuParticle& particle) {
|
||||
glm::vec3 position = particle.relativePosition + (particleProperties.emission.shouldTrail ? particle.basePosition : modelTransform.getTranslation());
|
||||
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [this, &modelTransform] (const CpuParticle& particle) {
|
||||
glm::vec3 position = particle.relativePosition + (_particleProperties.emission.shouldTrail ? particle.basePosition : modelTransform.getTranslation());
|
||||
return GpuParticle(position, glm::vec2(particle.lifetime, particle.seed));
|
||||
});
|
||||
|
||||
|
@ -487,13 +447,14 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
|
|||
// if the particles are marked rotateWithEntity
|
||||
withReadLock([&] {
|
||||
transform.setRotation(_renderTransform.getRotation());
|
||||
auto& color = _uniformBuffer.edit<ParticleUniforms>().color;
|
||||
color.start = EntityRenderer::calculatePulseColor(_particleProperties.getColorStart(), _pulseProperties, _created);
|
||||
color.middle = EntityRenderer::calculatePulseColor(_particleProperties.getColorMiddle(), _pulseProperties, _created);
|
||||
color.finish = EntityRenderer::calculatePulseColor(_particleProperties.getColorFinish(), _pulseProperties, _created);
|
||||
color.spread = EntityRenderer::calculatePulseColor(_particleProperties.getColorSpread(), _pulseProperties, _created);
|
||||
});
|
||||
|
||||
auto& color = _uniformBuffer.edit<ParticleUniforms>().color;
|
||||
color.start = EntityRenderer::calculatePulseColor(_particleProperties.getColorStart(), _pulseProperties, _created);
|
||||
color.middle = EntityRenderer::calculatePulseColor(_particleProperties.getColorMiddle(), _pulseProperties, _created);
|
||||
color.finish = EntityRenderer::calculatePulseColor(_particleProperties.getColorFinish(), _pulseProperties, _created);
|
||||
color.spread = EntityRenderer::calculatePulseColor(_particleProperties.getColorSpread(), _pulseProperties, _created);
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
batch.setUniformBuffer(0, _uniformBuffer);
|
||||
batch.setInputFormat(_vertexFormat);
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
ParticleEffectEntityRenderer(const EntityItemPointer& entity);
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
|
|
|
@ -121,16 +121,6 @@ ShapeKey PolyLineEntityRenderer::getShapeKey() {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
bool PolyLineEntityRenderer::needsRenderUpdate() const {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
return (!_textureLoaded && _texture && _texture->isLoaded());
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (entity->pointsChanged() || entity->widthsChanged() || entity->normalsChanged() || entity->texturesChanged() || entity->colorsChanged()) {
|
||||
return true;
|
||||
|
@ -140,6 +130,15 @@ bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityP
|
|||
}
|
||||
|
||||
void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] {
|
||||
withWriteLock([&] {
|
||||
_renderTransform = getModelTransform();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
auto pointsChanged = entity->pointsChanged();
|
||||
auto widthsChanged = entity->widthsChanged();
|
||||
auto normalsChanged = entity->normalsChanged();
|
||||
|
@ -159,13 +158,15 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
if (!textures.isEmpty()) {
|
||||
entityTextures = QUrl(textures);
|
||||
}
|
||||
withWriteLock([&] {
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(entityTextures);
|
||||
});
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(entityTextures);
|
||||
_textureAspectRatio = 1.0f;
|
||||
_textureLoaded = false;
|
||||
}
|
||||
|
||||
if (!_textureLoaded) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
bool textureChanged = false;
|
||||
if (!_textureLoaded && _texture && _texture->isLoaded()) {
|
||||
textureChanged = true;
|
||||
|
@ -175,13 +176,11 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
|
||||
// Data
|
||||
bool faceCameraChanged = faceCamera != _faceCamera;
|
||||
withWriteLock([&] {
|
||||
if (faceCameraChanged || glow != _glow) {
|
||||
_faceCamera = faceCamera;
|
||||
_glow = glow;
|
||||
updateData();
|
||||
}
|
||||
});
|
||||
if (faceCameraChanged || glow != _glow) {
|
||||
_faceCamera = faceCamera;
|
||||
_glow = glow;
|
||||
updateData();
|
||||
}
|
||||
|
||||
// Geometry
|
||||
if (pointsChanged) {
|
||||
|
@ -200,19 +199,10 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
|
||||
bool uvModeStretchChanged = _isUVModeStretch != isUVModeStretch;
|
||||
_isUVModeStretch = isUVModeStretch;
|
||||
|
||||
bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged;
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] {
|
||||
withWriteLock([&] {
|
||||
_renderTransform = getModelTransform();
|
||||
|
||||
if (geometryChanged) {
|
||||
updateGeometry();
|
||||
}
|
||||
});
|
||||
});
|
||||
if (uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) {
|
||||
updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void PolyLineEntityRenderer::updateGeometry() {
|
||||
|
@ -318,19 +308,16 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
|
|||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
size_t numVertices;
|
||||
Transform transform;
|
||||
gpu::TexturePointer texture;
|
||||
gpu::TexturePointer texture = _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get<TextureCache>()->getWhiteTexture();
|
||||
withReadLock([&] {
|
||||
numVertices = _numVertices;
|
||||
transform = _renderTransform;
|
||||
texture = _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get<TextureCache>()->getWhiteTexture();
|
||||
|
||||
batch.setResourceBuffer(0, _polylineGeometryBuffer);
|
||||
batch.setUniformBuffer(0, _polylineDataBuffer);
|
||||
});
|
||||
|
||||
if (numVertices < 2) {
|
||||
batch.setResourceBuffer(0, _polylineGeometryBuffer);
|
||||
batch.setUniformBuffer(0, _polylineDataBuffer);
|
||||
|
||||
if (_numVertices < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -341,5 +328,5 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
|
|||
batch.setPipeline(_pipelines[{args->_renderMethod, isTransparent()}]);
|
||||
batch.setModelTransform(transform);
|
||||
batch.setResourceTexture(0, texture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * _numVertices), 0);
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ public:
|
|||
virtual bool isTransparent() const override;
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
virtual ItemKey getKey() override;
|
||||
virtual ShapeKey getShapeKey() override;
|
||||
|
|
|
@ -1802,25 +1802,44 @@ ShapeKey PolyVoxEntityRenderer::getShapeKey() {
|
|||
}
|
||||
|
||||
bool PolyVoxEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (entity->voxelToWorldMatrix() != _lastVoxelToWorldMatrix) {
|
||||
if (resultWithReadLock<bool>([&] {
|
||||
if (entity->voxelToWorldMatrix() != _lastVoxelToWorldMatrix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity->_mesh != _mesh) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity->_mesh != _mesh) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Parent::needsRenderUpdateFromTypedEntity(entity);
|
||||
}
|
||||
|
||||
void PolyVoxEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
|
||||
#ifdef POLYVOX_ENTITY_USE_FADE_EFFECT
|
||||
if (!_hasTransitioned) {
|
||||
transaction.resetTransitionOnItem(_renderItemID, render::Transition::ELEMENT_ENTER_DOMAIN);
|
||||
_hasTransitioned = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PolyVoxEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
_lastVoxelToWorldMatrix = entity->voxelToWorldMatrix();
|
||||
_lastVoxelVolumeSize = entity->getVoxelVolumeSize();
|
||||
_params->setSubData(0, vec4(_lastVoxelVolumeSize, 0.0));
|
||||
graphics::MeshPointer newMesh;
|
||||
entity->withReadLock([&] {
|
||||
newMesh = entity->_mesh;
|
||||
});
|
||||
|
||||
if (newMesh && newMesh->getIndexBuffer()._buffer) {
|
||||
_mesh = newMesh;
|
||||
}
|
||||
|
||||
std::array<QString, 3> xyzTextureURLs{ {
|
||||
entity->getXTextureURL(),
|
||||
|
@ -1838,20 +1857,6 @@ void PolyVoxEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& s
|
|||
}
|
||||
}
|
||||
|
||||
void PolyVoxEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
_lastVoxelToWorldMatrix = entity->voxelToWorldMatrix();
|
||||
_lastVoxelVolumeSize = entity->getVoxelVolumeSize();
|
||||
_params->setSubData(0, vec4(_lastVoxelVolumeSize, 0.0));
|
||||
graphics::MeshPointer newMesh;
|
||||
entity->withReadLock([&] {
|
||||
newMesh = entity->_mesh;
|
||||
});
|
||||
|
||||
if (newMesh && newMesh->getIndexBuffer()._buffer) {
|
||||
_mesh = newMesh;
|
||||
}
|
||||
}
|
||||
|
||||
void PolyVoxEntityRenderer::doRender(RenderArgs* args) {
|
||||
if (!_mesh || !_mesh->getIndexBuffer()._buffer) {
|
||||
return;
|
||||
|
|
|
@ -51,24 +51,7 @@ bool ShapeEntityRenderer::needsRenderUpdate() const {
|
|||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (_dimensions != entity->getScaledDimensions()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_proceduralData != entity->getUserData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
withWriteLock([&] {
|
||||
_shape = entity->getShape();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
withWriteLock([&] {
|
||||
|
@ -86,47 +69,53 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
|
||||
void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
_shape = entity->getShape();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
bool materialChanged = false;
|
||||
glm::vec3 color = toGlm(entity->getColor());
|
||||
if (_color != color) {
|
||||
_color = color;
|
||||
_material->setAlbedo(color);
|
||||
materialChanged = true;
|
||||
}
|
||||
|
||||
float alpha = entity->getAlpha();
|
||||
if (_alpha != alpha) {
|
||||
_alpha = alpha;
|
||||
_material->setOpacity(alpha);
|
||||
materialChanged = true;
|
||||
}
|
||||
|
||||
auto userData = entity->getUserData();
|
||||
if (_proceduralData != userData) {
|
||||
_proceduralData = userData;
|
||||
_material->setProceduralData(_proceduralData);
|
||||
materialChanged = true;
|
||||
}
|
||||
|
||||
withReadLock([&] {
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) {
|
||||
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(mat->second.top().material);
|
||||
if (procedural->isFading()) {
|
||||
procedural->setIsFading(Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) < 1.0f);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
withWriteLock([&] {
|
||||
bool materialChanged = false;
|
||||
glm::vec3 color = toGlm(entity->getColor());
|
||||
if (_color != color) {
|
||||
_color = color;
|
||||
_material->setAlbedo(color);
|
||||
materialChanged = true;
|
||||
}
|
||||
|
||||
float alpha = entity->getAlpha();
|
||||
if (_alpha != alpha) {
|
||||
_alpha = alpha;
|
||||
_material->setOpacity(alpha);
|
||||
materialChanged = true;
|
||||
}
|
||||
|
||||
auto userData = entity->getUserData();
|
||||
if (_proceduralData != userData) {
|
||||
_proceduralData = userData;
|
||||
_material->setProceduralData(_proceduralData);
|
||||
materialChanged = true;
|
||||
}
|
||||
|
||||
auto materials = _materials.find("0");
|
||||
if (materials != _materials.end()) {
|
||||
if (materialChanged) {
|
||||
materials->second.setNeedsUpdate(true);
|
||||
}
|
||||
|
||||
bool requestUpdate = false;
|
||||
if (materials->second.top().material && materials->second.top().material->isProcedural() && materials->second.top().material->isReady()) {
|
||||
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(materials->second.top().material);
|
||||
if (procedural->isFading()) {
|
||||
procedural->setIsFading(Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) < 1.0f);
|
||||
requestUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (materials->second.shouldUpdate()) {
|
||||
RenderPipelines::updateMultiMaterial(materials->second);
|
||||
requestUpdate = true;
|
||||
}
|
||||
|
||||
if (requestUpdate) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -231,13 +220,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
|
||||
graphics::MultiMaterial materials;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
GeometryCache::Shape geometryShape;
|
||||
GeometryCache::Shape geometryShape = geometryCache->getShapeForEntityShape(_shape);
|
||||
PrimitiveMode primitiveMode;
|
||||
RenderLayer renderLayer;
|
||||
glm::vec4 outColor;
|
||||
Pipeline pipelineType;
|
||||
withReadLock([&] {
|
||||
geometryShape = geometryCache->getShapeForEntityShape(_shape);
|
||||
primitiveMode = _primitiveMode;
|
||||
renderLayer = _renderLayer;
|
||||
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
|
||||
|
@ -245,9 +233,10 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
pipelineType = getPipelineType(materials);
|
||||
auto& schema = materials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||
outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity);
|
||||
outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created);
|
||||
});
|
||||
|
||||
outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created);
|
||||
|
||||
if (outColor.a == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
@ -256,7 +245,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(materials.top().material);
|
||||
outColor = procedural->getColor(outColor);
|
||||
outColor.a *= procedural->isFading() ? Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) : 1.0f;
|
||||
procedural->prepare(batch, _position, _dimensions, _orientation, _created, ProceduralProgramKey(outColor.a < 1.0f));
|
||||
withReadLock([&] {
|
||||
procedural->prepare(batch, _position, _dimensions, _orientation, _created, ProceduralProgramKey(outColor.a < 1.0f));
|
||||
});
|
||||
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) {
|
||||
geometryCache->renderWireShape(batch, geometryShape, outColor);
|
||||
|
|
|
@ -30,7 +30,6 @@ protected:
|
|||
|
||||
private:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
|
|
@ -92,14 +92,6 @@ uint32_t TextEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const {
|
|||
return parentSubs;
|
||||
}
|
||||
|
||||
bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (_dimensions != entity->getScaledDimensions()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
|
||||
|
@ -112,26 +104,24 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
}
|
||||
|
||||
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
withWriteLock([&] {
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_text = entity->getText();
|
||||
_lineHeight = entity->getLineHeight();
|
||||
_textColor = toGlm(entity->getTextColor());
|
||||
_textAlpha = entity->getTextAlpha();
|
||||
_backgroundColor = toGlm(entity->getBackgroundColor());
|
||||
_backgroundAlpha = entity->getBackgroundAlpha();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
_leftMargin = entity->getLeftMargin();
|
||||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
_unlit = entity->getUnlit();
|
||||
_font = entity->getFont();
|
||||
_effect = entity->getTextEffect();
|
||||
_effectColor = toGlm(entity->getTextEffectColor());
|
||||
_effectThickness = entity->getTextEffectThickness();
|
||||
updateTextRenderItem();
|
||||
});
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_text = entity->getText();
|
||||
_lineHeight = entity->getLineHeight();
|
||||
_textColor = toGlm(entity->getTextColor());
|
||||
_textAlpha = entity->getTextAlpha();
|
||||
_backgroundColor = toGlm(entity->getBackgroundColor());
|
||||
_backgroundAlpha = entity->getBackgroundAlpha();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
_leftMargin = entity->getLeftMargin();
|
||||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
_unlit = entity->getUnlit();
|
||||
_font = entity->getFont();
|
||||
_effect = entity->getTextEffect();
|
||||
_effectColor = toGlm(entity->getTextEffectColor());
|
||||
_effectThickness = entity->getTextEffectThickness();
|
||||
updateTextRenderItem();
|
||||
}
|
||||
|
||||
void TextEntityRenderer::doRender(RenderArgs* args) {
|
||||
|
@ -141,25 +131,23 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
|
|||
|
||||
glm::vec4 backgroundColor;
|
||||
Transform modelTransform;
|
||||
BillboardMode billboardMode;
|
||||
PrimitiveMode primitiveMode;
|
||||
RenderLayer renderLayer;
|
||||
withReadLock([&] {
|
||||
modelTransform = _renderTransform;
|
||||
billboardMode = _billboardMode;
|
||||
primitiveMode = _primitiveMode;
|
||||
renderLayer = _renderLayer;
|
||||
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
});
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
|
||||
if (backgroundColor.a <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), billboardMode, args->getViewFrustum().getPosition()));
|
||||
modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), _billboardMode, args->getViewFrustum().getPosition()));
|
||||
batch.setModelTransform(modelTransform);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -323,53 +311,36 @@ void entities::TextPayload::render(RenderArgs* args) {
|
|||
glm::vec3 dimensions;
|
||||
BillboardMode billboardMode;
|
||||
|
||||
QString text;
|
||||
glm::vec4 textColor;
|
||||
QString font;
|
||||
TextEffect effect;
|
||||
glm::vec3 effectColor;
|
||||
float effectThickness;
|
||||
float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin;
|
||||
bool forward;
|
||||
textRenderable->withReadLock([&] {
|
||||
modelTransform = textRenderable->_renderTransform;
|
||||
dimensions = textRenderable->_dimensions;
|
||||
billboardMode = textRenderable->_billboardMode;
|
||||
|
||||
text = textRenderable->_text;
|
||||
font = textRenderable->_font;
|
||||
effect = textRenderable->_effect;
|
||||
effectThickness = textRenderable->_effectThickness;
|
||||
|
||||
lineHeight = textRenderable->_lineHeight;
|
||||
leftMargin = textRenderable->_leftMargin;
|
||||
rightMargin = textRenderable->_rightMargin;
|
||||
topMargin = textRenderable->_topMargin;
|
||||
bottomMargin = textRenderable->_bottomMargin;
|
||||
|
||||
float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f;
|
||||
textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha);
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
|
||||
effectColor = EntityRenderer::calculatePulseColor(textRenderable->_effectColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
|
||||
forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
|
||||
});
|
||||
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
glm::vec3 effectColor = EntityRenderer::calculatePulseColor(textRenderable->_effectColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
|
||||
if (textColor.a <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), billboardMode, args->getViewFrustum().getPosition()));
|
||||
|
||||
float scale = lineHeight / textRenderer->getFontSize();
|
||||
float scale = textRenderable->_lineHeight / textRenderer->getFontSize();
|
||||
modelTransform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z));
|
||||
modelTransform.setScale(scale);
|
||||
batch.setModelTransform(modelTransform);
|
||||
|
||||
glm::vec2 bounds = glm::vec2(dimensions.x - (leftMargin + rightMargin), dimensions.y - (topMargin + bottomMargin));
|
||||
textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, bounds / scale, scale,
|
||||
text, font, textColor, effectColor, effectThickness, effect,
|
||||
glm::vec2 bounds = glm::vec2(dimensions.x - (textRenderable->_leftMargin + textRenderable->_rightMargin), dimensions.y - (textRenderable->_topMargin + textRenderable->_bottomMargin));
|
||||
textRenderer->draw(batch, textRenderable->_leftMargin / scale, -textRenderable->_topMargin / scale, bounds / scale, scale,
|
||||
textRenderable->_text, textRenderable->_font, textColor, effectColor, textRenderable->_effectThickness, textRenderable->_effect,
|
||||
textRenderable->_unlit, forward);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ protected:
|
|||
void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
private:
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
|
|
@ -125,16 +125,6 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
return false;
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdate() const {
|
||||
if (resultWithReadLock<bool>([this] {
|
||||
return !_webSurface;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
void WebEntityRenderer::onTimeout() {
|
||||
uint64_t lastRenderTime;
|
||||
if (!resultWithReadLock<bool>([&] {
|
||||
|
@ -261,6 +251,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
});
|
||||
} else {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -339,8 +331,11 @@ void WebEntityRenderer::buildWebSurface(const EntityItemPointer& entity, const Q
|
|||
return;
|
||||
}
|
||||
|
||||
++_currentWebCount;
|
||||
WebEntityRenderer::acquireWebSurface(newSourceURL, _contentType == ContentType::HtmlContent, _webSurface, _cachedWebSurface);
|
||||
bool isHTML = _contentType == ContentType::HtmlContent;
|
||||
if (isHTML) {
|
||||
++_currentWebCount;
|
||||
}
|
||||
WebEntityRenderer::acquireWebSurface(newSourceURL, isHTML, _webSurface, _cachedWebSurface);
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
_webSurface->resume();
|
||||
|
||||
|
@ -358,12 +353,15 @@ void WebEntityRenderer::destroyWebSurface() {
|
|||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
withWriteLock([&] {
|
||||
webSurface.swap(_webSurface);
|
||||
_contentType = ContentType::NoContent;
|
||||
|
||||
if (webSurface) {
|
||||
--_currentWebCount;
|
||||
if (_contentType == ContentType::HtmlContent) {
|
||||
--_currentWebCount;
|
||||
}
|
||||
WebEntityRenderer::releaseWebSurface(webSurface, _cachedWebSurface, _connections);
|
||||
}
|
||||
|
||||
_contentType = ContentType::NoContent;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ public:
|
|||
virtual QObject* getEventHandler() override;
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
|
|
@ -189,7 +189,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
|
|||
CullTest::_containingZones.insert(_entityID);
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
auto position = entity->getWorldPosition();
|
||||
auto rotation = entity->getWorldOrientation();
|
||||
auto dimensions = entity->getScaledDimensions();
|
||||
|
@ -199,7 +199,11 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
auto visible = entity->getVisible();
|
||||
if (transformChanged || visible != _lastVisible) {
|
||||
_lastVisible = visible;
|
||||
DependencyManager::get<EntityTreeRenderer>()->updateZone(entity->getID());
|
||||
void* key = (void*)this;
|
||||
EntityItemID id = entity->getID();
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [id] {
|
||||
DependencyManager::get<EntityTreeRenderer>()->updateZone(id);
|
||||
});
|
||||
}
|
||||
|
||||
auto proceduralUserData = entity->getUserData();
|
||||
|
@ -267,10 +271,6 @@ ItemKey ZoneEntityRenderer::getKey() {
|
|||
}
|
||||
|
||||
bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (entity->getVisible() != _lastVisible) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity->keyLightPropertiesChanged() ||
|
||||
entity->ambientLightPropertiesChanged() ||
|
||||
entity->hazePropertiesChanged() ||
|
||||
|
@ -280,29 +280,11 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_skyboxTextureURL != entity->getSkyboxProperties().getURL()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity->getWorldPosition() != _lastPosition) {
|
||||
return true;
|
||||
}
|
||||
if (entity->getScaledDimensions() != _lastDimensions) {
|
||||
return true;
|
||||
}
|
||||
if (entity->getWorldOrientation() != _lastRotation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entity->getUserData() != _proceduralUserData) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity) {
|
||||
setKeyLightMode((ComponentMode)entity->getKeyLightMode());
|
||||
_keyLightMode = (ComponentMode)entity->getKeyLightMode();
|
||||
|
||||
const auto& sunLight = editSunLight();
|
||||
sunLight->setType(graphics::Light::SUN);
|
||||
|
@ -319,7 +301,7 @@ void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity
|
|||
}
|
||||
|
||||
void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& entity) {
|
||||
setAmbientLightMode((ComponentMode)entity->getAmbientLightMode());
|
||||
_ambientLightMode = (ComponentMode)entity->getAmbientLightMode();
|
||||
|
||||
const auto& ambientLight = editAmbientLight();
|
||||
ambientLight->setType(graphics::Light::AMBIENT);
|
||||
|
@ -339,11 +321,12 @@ void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer&
|
|||
}
|
||||
|
||||
void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) {
|
||||
setHazeMode((ComponentMode)entity->getHazeMode());
|
||||
const uint32_t hazeMode = entity->getHazeMode();
|
||||
|
||||
_hazeMode = (ComponentMode)hazeMode;
|
||||
|
||||
const auto& haze = editHaze();
|
||||
|
||||
const uint32_t hazeMode = entity->getHazeMode();
|
||||
haze->setHazeActive(hazeMode == COMPONENT_MODE_ENABLED);
|
||||
haze->setAltitudeBased(_hazeProperties.getHazeAltitudeEffect());
|
||||
|
||||
|
@ -367,7 +350,7 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity)
|
|||
}
|
||||
|
||||
void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) {
|
||||
setBloomMode((ComponentMode)entity->getBloomMode());
|
||||
_bloomMode = (ComponentMode)entity->getBloomMode();
|
||||
|
||||
const auto& bloom = editBloom();
|
||||
|
||||
|
@ -377,7 +360,7 @@ void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity)
|
|||
}
|
||||
|
||||
void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) {
|
||||
setSkyboxMode((ComponentMode)entity->getSkyboxMode());
|
||||
_skyboxMode = (ComponentMode)entity->getSkyboxMode();
|
||||
|
||||
editBackground();
|
||||
setSkyboxColor(toGlm(_skyboxProperties.getColor()));
|
||||
|
@ -478,26 +461,6 @@ void ZoneEntityRenderer::updateSkyboxMap() {
|
|||
}
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::setHazeMode(ComponentMode mode) {
|
||||
_hazeMode = mode;
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::setKeyLightMode(ComponentMode mode) {
|
||||
_keyLightMode = mode;
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::setAmbientLightMode(ComponentMode mode) {
|
||||
_ambientLightMode = mode;
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) {
|
||||
_skyboxMode = mode;
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::setBloomMode(ComponentMode mode) {
|
||||
_bloomMode = mode;
|
||||
}
|
||||
|
||||
void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) {
|
||||
editSkybox()->setColor(color);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ protected:
|
|||
virtual ItemKey getKey() override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
private:
|
||||
void updateKeyZoneItemFromEntity(const TypedEntityPointer& entity);
|
||||
|
@ -52,12 +52,6 @@ private:
|
|||
void setAmbientURL(const QString& ambientUrl);
|
||||
void setSkyboxURL(const QString& skyboxUrl);
|
||||
|
||||
void setHazeMode(ComponentMode mode);
|
||||
void setKeyLightMode(ComponentMode mode);
|
||||
void setAmbientLightMode(ComponentMode mode);
|
||||
void setSkyboxMode(ComponentMode mode);
|
||||
void setBloomMode(ComponentMode mode);
|
||||
|
||||
void setSkyboxColor(const glm::vec3& color);
|
||||
void setProceduralUserData(const QString& userData);
|
||||
|
||||
|
|
|
@ -1911,13 +1911,11 @@ void EntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
|||
if (glm::length2(getUnscaledDimensions() - newDimensions) > MIN_SCALE_CHANGE_SQUARED) {
|
||||
withWriteLock([&] {
|
||||
_unscaledDimensions = newDimensions;
|
||||
});
|
||||
locationChanged();
|
||||
dimensionsChanged();
|
||||
withWriteLock([&] {
|
||||
_flags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
_queryAACubeSet = false;
|
||||
});
|
||||
locationChanged();
|
||||
dimensionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1046,18 +1046,26 @@ QSizeF EntityScriptingInterface::textSize(const QUuid& id, const QString& text)
|
|||
return EntityTree::textSize(id, text);
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine) {
|
||||
void EntityScriptingInterface::setPersistentEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
_entitiesScriptEngine = engine;
|
||||
_persistentEntitiesScriptEngine = engine;
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::setNonPersistentEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
_nonPersistentEntitiesScriptEngine = engine;
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::callEntityMethod(const QUuid& id, const QString& method, const QStringList& params) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
if (_entitiesScriptEngine) {
|
||||
EntityItemID entityID{ id };
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, method, params);
|
||||
|
||||
auto entity = getEntityTree()->findEntityByEntityItemID(id);
|
||||
if (entity) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
if (scriptEngine) {
|
||||
scriptEngine->callEntityScriptMethod(id, method, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1099,9 +1107,13 @@ void EntityScriptingInterface::handleEntityScriptCallMethodPacket(QSharedPointer
|
|||
params << paramString;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, method, params, senderNode->getUUID());
|
||||
auto entity = getEntityTree()->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
auto& scriptEngine = (entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine;
|
||||
if (scriptEngine) {
|
||||
scriptEngine->callEntityScriptMethod(entityID, method, params, senderNode->getUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1332,7 +1344,7 @@ bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue h
|
|||
if (entitiesScriptEngine) {
|
||||
request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID));
|
||||
}
|
||||
});
|
||||
}, entityID);
|
||||
if (!request->isStarted()) {
|
||||
request->deleteLater();
|
||||
callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), QScriptValue());
|
||||
|
|
|
@ -181,7 +181,8 @@ public:
|
|||
|
||||
void setEntityTree(EntityTreePointer modelTree);
|
||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||
void setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
|
||||
void setPersistentEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
|
||||
void setNonPersistentEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
|
||||
|
||||
void resetActivityTracking();
|
||||
ActivityTracking getActivityTracking() const { return _activityTracking; }
|
||||
|
@ -2510,9 +2511,12 @@ signals:
|
|||
void webEventReceived(const EntityItemID& entityItemID, const QVariant& message);
|
||||
|
||||
protected:
|
||||
void withEntitiesScriptEngine(std::function<void(QSharedPointer<EntitiesScriptEngineProvider>)> function) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
function(_entitiesScriptEngine);
|
||||
void withEntitiesScriptEngine(std::function<void(QSharedPointer<EntitiesScriptEngineProvider>)> function, const EntityItemID& id) {
|
||||
auto entity = getEntityTree()->findEntityByEntityItemID(id);
|
||||
if (entity) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
function((entity->isLocalEntity() || entity->isMyAvatarEntity()) ? _persistentEntitiesScriptEngine : _nonPersistentEntitiesScriptEngine);
|
||||
}
|
||||
};
|
||||
|
||||
private slots:
|
||||
|
@ -2542,7 +2546,8 @@ private:
|
|||
EntityTreePointer _entityTree;
|
||||
|
||||
std::recursive_mutex _entitiesScriptEngineLock;
|
||||
QSharedPointer<EntitiesScriptEngineProvider> _entitiesScriptEngine;
|
||||
QSharedPointer<EntitiesScriptEngineProvider> _persistentEntitiesScriptEngine;
|
||||
QSharedPointer<EntitiesScriptEngineProvider> _nonPersistentEntitiesScriptEngine;
|
||||
|
||||
bool _bidOnSimulationOwnership { false };
|
||||
|
||||
|
|
|
@ -78,7 +78,14 @@ void LightEntityItem::setFalloffRadius(float value) {
|
|||
}
|
||||
|
||||
void LightEntityItem::setIsSpotlight(bool value) {
|
||||
if (value == getIsSpotlight()) {
|
||||
bool needsRenderUpdate;
|
||||
withWriteLock([&] {
|
||||
needsRenderUpdate = value != _isSpotlight;
|
||||
_needsRenderUpdate |= needsRenderUpdate;
|
||||
_isSpotlight = value;
|
||||
});
|
||||
|
||||
if (!needsRenderUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -92,25 +99,25 @@ void LightEntityItem::setIsSpotlight(bool value) {
|
|||
newDimensions = glm::vec3(glm::compMax(dimensions));
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = true;
|
||||
_isSpotlight = value;
|
||||
});
|
||||
setScaledDimensions(newDimensions);
|
||||
}
|
||||
|
||||
void LightEntityItem::setCutoff(float value) {
|
||||
value = glm::clamp(value, MIN_CUTOFF, MAX_CUTOFF);
|
||||
if (value == getCutoff()) {
|
||||
bool needsRenderUpdate;
|
||||
bool spotlight;
|
||||
withWriteLock([&] {
|
||||
needsRenderUpdate = value != _cutoff;
|
||||
_needsRenderUpdate |= needsRenderUpdate;
|
||||
_cutoff = value;
|
||||
spotlight = _isSpotlight;
|
||||
});
|
||||
|
||||
if (!needsRenderUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = true;
|
||||
_cutoff = value;
|
||||
});
|
||||
|
||||
if (getIsSpotlight()) {
|
||||
if (spotlight) {
|
||||
// If we are a spotlight, adjusting the cutoff will affect the area we encapsulate,
|
||||
// so update the dimensions to reflect this.
|
||||
const float length = getScaledDimensions().z;
|
||||
|
|
|
@ -254,26 +254,30 @@ void ModelEntityItem::debugDump() const {
|
|||
}
|
||||
|
||||
void ModelEntityItem::setShapeType(ShapeType type) {
|
||||
bool changed = false;
|
||||
uint32_t flags = 0;
|
||||
withWriteLock([&] {
|
||||
if (type != _shapeType) {
|
||||
if (type == SHAPE_TYPE_STATIC_MESH && _dynamic) {
|
||||
// dynamic and STATIC_MESH are incompatible
|
||||
// since the shape is being set here we clear the dynamic bit
|
||||
_dynamic = false;
|
||||
_flags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
flags = Simulation::DIRTY_MOTION_TYPE;
|
||||
}
|
||||
_shapeType = type;
|
||||
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
markDirtyFlags(flags);
|
||||
locationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
ShapeType ModelEntityItem::getShapeType() const {
|
||||
return computeTrueShapeType();
|
||||
}
|
||||
|
||||
ShapeType ModelEntityItem::computeTrueShapeType() const {
|
||||
ShapeType type = _shapeType;
|
||||
ShapeType type = resultWithReadLock<ShapeType>([&] { return _shapeType; });
|
||||
if (type == SHAPE_TYPE_STATIC_MESH && _dynamic) {
|
||||
// dynamic is incompatible with STATIC_MESH
|
||||
// shouldn't fall in here but just in case --> fall back to COMPOUND
|
||||
|
@ -290,7 +294,6 @@ void ModelEntityItem::setModelURL(const QString& url) {
|
|||
withWriteLock([&] {
|
||||
if (_modelURL != url) {
|
||||
_modelURL = url;
|
||||
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
_needsRenderUpdate = true;
|
||||
}
|
||||
});
|
||||
|
@ -325,18 +328,10 @@ void ModelEntityItem::setCompoundShapeURL(const QString& url) {
|
|||
withWriteLock([&] {
|
||||
if (_compoundShapeURL.get() != url) {
|
||||
_compoundShapeURL.set(url);
|
||||
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationURL(const QString& url) {
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setURL(url);
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationSettings(const QString& value) {
|
||||
// NOTE: this method only called for old bitstream format
|
||||
|
||||
|
@ -399,20 +394,6 @@ void ModelEntityItem::setAnimationSettings(const QString& value) {
|
|||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationIsPlaying(bool value) {
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setRunning(value);
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationFPS(float value) {
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setFPS(value);
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::resizeJointArrays(int newSize) {
|
||||
if (newSize < 0) {
|
||||
return;
|
||||
|
@ -588,10 +569,6 @@ QString ModelEntityItem::getCompoundShapeURL() const {
|
|||
return _compoundShapeURL.get();
|
||||
}
|
||||
|
||||
QString ModelEntityItem::getCollisionShapeURL() const {
|
||||
return getShapeType() == SHAPE_TYPE_COMPOUND ? getCompoundShapeURL() : getModelURL();
|
||||
}
|
||||
|
||||
void ModelEntityItem::setColor(const glm::u8vec3& value) {
|
||||
withWriteLock([&] {
|
||||
_color = value;
|
||||
|
@ -629,61 +606,18 @@ void ModelEntityItem::setAnimationCurrentFrame(float value) {
|
|||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationAllowTranslation(bool value) {
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setAllowTranslation(value);
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationAllowTranslation() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getAllowTranslation();
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationLoop(bool loop) {
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setLoop(loop);
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationLoop() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getLoop();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void ModelEntityItem::setAnimationHold(bool hold) {
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setHold(hold);
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationHold() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getHold();
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationIsPlaying() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getRunning();
|
||||
});
|
||||
}
|
||||
|
||||
float ModelEntityItem::getAnimationCurrentFrame() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _animationProperties.getCurrentFrame();
|
||||
});
|
||||
}
|
||||
|
||||
float ModelEntityItem::getAnimationFPS() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _animationProperties.getFPS();
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::isAnimatingSomething() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.isValidAndRunning();
|
||||
|
@ -722,6 +656,7 @@ bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProp
|
|||
bool somethingChanged = newProperties != _animationProperties;
|
||||
if (somethingChanged) {
|
||||
_animationProperties = newProperties;
|
||||
_needsRenderUpdate = true;
|
||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||
}
|
||||
return somethingChanged;
|
||||
|
|
|
@ -76,33 +76,18 @@ public:
|
|||
static const QString DEFAULT_COMPOUND_SHAPE_URL;
|
||||
QString getCompoundShapeURL() const;
|
||||
|
||||
// Returns the URL used for the collision shape
|
||||
QString getCollisionShapeURL() const;
|
||||
|
||||
// model related properties
|
||||
virtual void setModelURL(const QString& url);
|
||||
virtual void setCompoundShapeURL(const QString& url);
|
||||
|
||||
// Animation related items...
|
||||
AnimationPropertyGroup getAnimationProperties() const;
|
||||
|
||||
// TODO: audit and remove unused Animation accessors
|
||||
bool hasAnimation() const;
|
||||
QString getAnimationURL() const;
|
||||
virtual void setAnimationURL(const QString& url);
|
||||
|
||||
void setAnimationCurrentFrame(float value);
|
||||
void setAnimationIsPlaying(bool value);
|
||||
void setAnimationFPS(float value);
|
||||
|
||||
void setAnimationAllowTranslation(bool value);
|
||||
float getAnimationCurrentFrame() const;
|
||||
bool getAnimationAllowTranslation() const;
|
||||
|
||||
void setAnimationLoop(bool loop);
|
||||
bool getAnimationLoop() const;
|
||||
|
||||
void setAnimationHold(bool hold);
|
||||
bool getAnimationHold() const;
|
||||
bool isAnimatingSomething() const;
|
||||
|
||||
void setRelayParentJoints(bool relayJoints);
|
||||
bool getRelayParentJoints() const;
|
||||
|
@ -110,11 +95,6 @@ public:
|
|||
void setGroupCulled(bool value);
|
||||
bool getGroupCulled() const;
|
||||
|
||||
bool getAnimationIsPlaying() const;
|
||||
float getAnimationCurrentFrame() const;
|
||||
float getAnimationFPS() const;
|
||||
bool isAnimatingSomething() const;
|
||||
|
||||
static const QString DEFAULT_TEXTURES;
|
||||
const QString getTextures() const;
|
||||
void setTextures(const QString& textures);
|
||||
|
@ -142,7 +122,6 @@ public:
|
|||
private:
|
||||
void setAnimationSettings(const QString& value); // only called for old bitstream format
|
||||
bool applyNewAnimationProperties(AnimationPropertyGroup newProperties);
|
||||
ShapeType computeTrueShapeType() const;
|
||||
|
||||
protected:
|
||||
void resizeJointArrays(int newSize);
|
||||
|
|
|
@ -266,21 +266,21 @@ glm::u8vec3 PolyLineEntityItem::getColor() const {
|
|||
|
||||
void PolyLineEntityItem::setIsUVModeStretch(bool isUVModeStretch) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = _isUVModeStretch != isUVModeStretch;
|
||||
_needsRenderUpdate |= _isUVModeStretch != isUVModeStretch;
|
||||
_isUVModeStretch = isUVModeStretch;
|
||||
});
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::setGlow(bool glow) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = _glow != glow;
|
||||
_needsRenderUpdate |= _glow != glow;
|
||||
_glow = glow;
|
||||
});
|
||||
}
|
||||
|
||||
void PolyLineEntityItem::setFaceCamera(bool faceCamera) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate = _faceCamera != faceCamera;
|
||||
_needsRenderUpdate |= _faceCamera != faceCamera;
|
||||
_faceCamera = faceCamera;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -442,4 +442,11 @@ PulsePropertyGroup ShapeEntityItem::getPulseProperties() const {
|
|||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
||||
|
||||
void ShapeEntityItem::setUserData(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _userData != value;
|
||||
_userData = value;
|
||||
});
|
||||
}
|
|
@ -101,6 +101,8 @@ public:
|
|||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
void setUserData(const QString& value) override;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
|
|
|
@ -84,12 +84,12 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
|
|||
|
||||
// Contains a QString property, must be synchronized
|
||||
withWriteLock([&] {
|
||||
_keyLightPropertiesChanged = _keyLightProperties.setProperties(properties);
|
||||
_ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties);
|
||||
_skyboxPropertiesChanged = _skyboxProperties.setProperties(properties);
|
||||
_keyLightPropertiesChanged |= _keyLightProperties.setProperties(properties);
|
||||
_ambientLightPropertiesChanged |= _ambientLightProperties.setProperties(properties);
|
||||
_skyboxPropertiesChanged |= _skyboxProperties.setProperties(properties);
|
||||
});
|
||||
_hazePropertiesChanged = _hazeProperties.setProperties(properties);
|
||||
_bloomPropertiesChanged = _bloomProperties.setProperties(properties);
|
||||
_hazePropertiesChanged |= _hazeProperties.setProperties(properties);
|
||||
_bloomPropertiesChanged |= _bloomProperties.setProperties(properties);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
|
||||
|
@ -125,7 +125,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, _keyLightPropertiesChanged);
|
||||
});
|
||||
somethingChanged = somethingChanged || _keyLightPropertiesChanged;
|
||||
somethingChanged |= _keyLightPropertiesChanged;
|
||||
bytesRead += bytesFromKeylight;
|
||||
dataAt += bytesFromKeylight;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged);
|
||||
});
|
||||
somethingChanged = somethingChanged || _ambientLightPropertiesChanged;
|
||||
somethingChanged |= _ambientLightPropertiesChanged;
|
||||
bytesRead += bytesFromAmbientlight;
|
||||
dataAt += bytesFromAmbientlight;
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, _skyboxPropertiesChanged);
|
||||
});
|
||||
somethingChanged = somethingChanged || _skyboxPropertiesChanged;
|
||||
somethingChanged |= _skyboxPropertiesChanged;
|
||||
bytesRead += bytesFromSkybox;
|
||||
dataAt += bytesFromSkybox;
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
{
|
||||
int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, _hazePropertiesChanged);
|
||||
somethingChanged = somethingChanged || _hazePropertiesChanged;
|
||||
somethingChanged |= _hazePropertiesChanged;
|
||||
bytesRead += bytesFromHaze;
|
||||
dataAt += bytesFromHaze;
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
{
|
||||
int bytesFromBloom = _bloomProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, _bloomPropertiesChanged);
|
||||
somethingChanged = somethingChanged || _bloomPropertiesChanged;
|
||||
somethingChanged |= _bloomPropertiesChanged;
|
||||
bytesRead += bytesFromBloom;
|
||||
dataAt += bytesFromBloom;
|
||||
}
|
||||
|
@ -444,6 +444,13 @@ uint32_t ZoneEntityItem::getSkyboxMode() const {
|
|||
return _skyboxMode;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setUserData(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
_needsRenderUpdate |= _userData != value;
|
||||
_userData = value;
|
||||
});
|
||||
}
|
||||
|
||||
void ZoneEntityItem::fetchCollisionGeometryResource() {
|
||||
QUrl hullURL(getCompoundShapeURL());
|
||||
if (hullURL.isEmpty()) {
|
||||
|
|
|
@ -104,6 +104,8 @@ public:
|
|||
uint32_t getScreenshare() const { return _screenshare; }
|
||||
void setScreenshare(uint32_t value) { _screenshare = value; }
|
||||
|
||||
void setUserData(const QString& value) override;
|
||||
|
||||
bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
|
||||
bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
|
||||
bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; }
|
||||
|
|
|
@ -96,7 +96,7 @@ void gl::initModuleGl() {
|
|||
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB");
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
QueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)getGlProcessAddress("glXQueryCurrentRendererIntegerMESA");
|
||||
#endif
|
||||
|
||||
|
@ -134,7 +134,7 @@ void gl::setSwapInterval(int interval) {
|
|||
}
|
||||
|
||||
bool gl::queryCurrentRendererIntegerMESA(int attr, unsigned int *value) {
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
if (QueryCurrentRendererIntegerMESA) {
|
||||
return QueryCurrentRendererIntegerMESA(attr, value);
|
||||
}
|
||||
|
|
|
@ -35,11 +35,6 @@ class GLPaintEngine : public QPaintEngine {
|
|||
};
|
||||
|
||||
GLWidget::GLWidget() {
|
||||
#ifdef Q_OS_LINUX
|
||||
// Cause GLWidget::eventFilter to be called.
|
||||
// It wouldn't hurt to do this on Mac and PC too; but apparently it's only needed on linux.
|
||||
qApp->installEventFilter(this);
|
||||
#endif
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
|
@ -118,39 +113,6 @@ bool GLWidget::event(QEvent* event) {
|
|||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
|
||||
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to
|
||||
// receive keyPress events for the Alt (and Meta) key in a reliable manner.
|
||||
//
|
||||
// This filter catches events before QMenuBar can steal the keyboard focus.
|
||||
// The idea was borrowed from
|
||||
// http://www.archivum.info/qt-interest@trolltech.com/2006-09/00053/Re-(Qt4)-Alt-key-focus-QMenuBar-(solved).html
|
||||
|
||||
bool GLWidget::eventFilter(QObject*, QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::ShortcutOverride:
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
keyPressEvent(keyEvent);
|
||||
} else if (event->type() == QEvent::KeyRelease) {
|
||||
keyReleaseEvent(keyEvent);
|
||||
} else {
|
||||
QWidget::event(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GLWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) {
|
||||
#ifdef Q_OS_WIN32
|
||||
MSG* win32message = static_cast<MSG*>(message);
|
||||
|
|
|
@ -42,9 +42,6 @@ protected:
|
|||
virtual bool event(QEvent* event) override;
|
||||
gl::Context* _context { nullptr };
|
||||
|
||||
private slots:
|
||||
virtual bool eventFilter(QObject*, QEvent* event) override;
|
||||
|
||||
private:
|
||||
QPaintEngine* _paintEngine { nullptr };
|
||||
bool _vsyncSupported { false };
|
||||
|
|
|
@ -153,9 +153,11 @@ void GLBackend::init() {
|
|||
|
||||
if (vendor.contains("NVIDIA") ) {
|
||||
qCDebug(gpugllogging) << "NVIDIA card detected";
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
|
||||
#endif
|
||||
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: " << GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: " << GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX;
|
||||
|
@ -168,7 +170,9 @@ void GLBackend::init() {
|
|||
|
||||
} else if (vendor.contains("ATI")) {
|
||||
qCDebug(gpugllogging) << "ATI card detected";
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
GL_GET_INTEGER(TEXTURE_FREE_MEMORY_ATI);
|
||||
#endif
|
||||
|
||||
_totalMemory = TEXTURE_FREE_MEMORY_ATI * BYTES_PER_KIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
|
@ -215,15 +219,21 @@ void GLBackend::init() {
|
|||
}
|
||||
|
||||
size_t GLBackend::getAvailableMemory() {
|
||||
GLint mem;
|
||||
// GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX returns 1 value
|
||||
// GL_TEXTURE_FREE_MEMORY_ATI returns 4 values, we only need the first
|
||||
GLint mem[4] = {0,0,0,0};
|
||||
|
||||
switch( _videoCard ) {
|
||||
case NVIDIA:
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &mem);
|
||||
return mem * BYTES_PER_KIB;
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &mem[0]);
|
||||
#endif
|
||||
return mem[0] * BYTES_PER_KIB;
|
||||
case ATI:
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &mem);
|
||||
return mem * BYTES_PER_KIB;
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &mem[0]);
|
||||
#endif
|
||||
return mem[0] * BYTES_PER_KIB;
|
||||
case MESA:
|
||||
return 0; // Don't know the current value
|
||||
case Unknown:
|
||||
|
@ -994,4 +1004,4 @@ void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRend
|
|||
|
||||
void GLBackend::syncProgram(const gpu::ShaderPointer& program) {
|
||||
gpu::gl::GLShader::sync(*this, *program);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace NetworkingConstants {
|
|||
const QUrl HELP_FORUM_URL { "https://forums.vircadia.dev" };
|
||||
const QUrl HELP_SCRIPTING_REFERENCE_URL{ "https://apidocs.vircadia.dev/" };
|
||||
const QUrl HELP_RELEASE_NOTES_URL{ "https://docs.vircadia.dev/release-notes.html" };
|
||||
const QUrl HELP_BUG_REPORT_URL{ "https://github.com/kasenvr/project-athena/issues" };
|
||||
const QUrl HELP_BUG_REPORT_URL{ "https://github.com/vircadia/vircadia/issues" };
|
||||
|
||||
const QString DEFAULT_VIRCADIA_ADDRESS = "file:///~/serverless/tutorial.json";
|
||||
const QString DEFAULT_HOME_ADDRESS = "file:///~/serverless/tutorial.json";
|
||||
|
|
|
@ -31,9 +31,9 @@ static inline void for_each_eye(const std::function<void(ovrEye)>& f) {
|
|||
f(VRAPI_EYE_RIGHT);
|
||||
}
|
||||
|
||||
static inline void for_each_hand(const std::function<void(ovrHandedness)>& f) {
|
||||
f(VRAPI_HAND_LEFT);
|
||||
f(VRAPI_HAND_RIGHT);
|
||||
static inline void for_each_hand(const std::function<void(ovrTrackedDeviceTypeId)>& f) {
|
||||
f(VRAPI_TRACKED_DEVICE_HAND_LEFT);
|
||||
f(VRAPI_TRACKED_DEVICE_HAND_RIGHT);
|
||||
}
|
||||
|
||||
static inline glm::mat4 toGlm(const ovrMatrix4f& om) {
|
||||
|
|
|
@ -324,7 +324,7 @@ struct VrSurface : public TaskQueue {
|
|||
vrapi_SetTrackingSpace( session, VRAPI_TRACKING_SPACE_LOCAL);
|
||||
vrapi_SetPerfThread(session, VRAPI_PERF_THREAD_TYPE_RENDERER, gettid());
|
||||
vrapi_SetClockLevels(session, 2, 4);
|
||||
vrapi_SetExtraLatencyMode(session, VRAPI_EXTRA_LATENCY_MODE_DYNAMIC);
|
||||
vrapi_SetExtraLatencyMode(session, VRAPI_EXTRA_LATENCY_MODE_ON);
|
||||
// Generates a warning on the quest: "vrapi_SetDisplayRefreshRate: Dynamic Display Refresh Rate not supported"
|
||||
// vrapi_SetDisplayRefreshRate(session, 72);
|
||||
});
|
||||
|
|
|
@ -31,7 +31,7 @@ const quint64 LOST_TRACKING_DELAY = 3000000;
|
|||
|
||||
namespace ovr {
|
||||
|
||||
controller::Pose toControllerPose(ovrHandedness hand, const ovrRigidBodyPosef& handPose) {
|
||||
controller::Pose toControllerPose(ovrTrackedDeviceTypeId hand, const ovrRigidBodyPosef& handPose) {
|
||||
// When the sensor-to-world rotation is identity the coordinate axes look like this:
|
||||
//
|
||||
// user
|
||||
|
@ -111,7 +111,7 @@ namespace ovr {
|
|||
return pose;
|
||||
}
|
||||
|
||||
controller::Pose toControllerPose(ovrHandedness hand,
|
||||
controller::Pose toControllerPose(ovrTrackedDeviceTypeId hand,
|
||||
const ovrRigidBodyPosef& handPose,
|
||||
const ovrRigidBodyPosef& lastHandPose) {
|
||||
static const glm::quat yFlip = glm::angleAxis(PI, Vectors::UNIT_Y);
|
||||
|
@ -165,9 +165,9 @@ public:
|
|||
|
||||
private:
|
||||
void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
|
||||
ovrHandedness hand, const ovrRigidBodyPosef& handPose);
|
||||
ovrTrackedDeviceTypeId hand, const ovrRigidBodyPosef& handPose);
|
||||
void handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData,
|
||||
ovrHandedness hand, const ovrRigidBodyPosef& handPose);
|
||||
ovrTrackedDeviceTypeId hand, const ovrRigidBodyPosef& handPose);
|
||||
void handleHeadPose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
|
||||
const ovrRigidBodyPosef& headPose);
|
||||
|
||||
|
@ -379,9 +379,9 @@ void OculusMobileInputDevice::update(float deltaTime, const controller::InputCal
|
|||
handleHeadPose(deltaTime, inputCalibrationData, _headTracking.HeadPose);
|
||||
|
||||
static const auto REQUIRED_HAND_STATUS = VRAPI_TRACKING_STATUS_ORIENTATION_TRACKED | VRAPI_TRACKING_STATUS_POSITION_TRACKED;
|
||||
ovr::for_each_hand([&](ovrHandedness hand) {
|
||||
size_t handIndex = (hand == VRAPI_HAND_LEFT) ? 0 : 1;
|
||||
int controller = (hand == VRAPI_HAND_LEFT) ? controller::LEFT_HAND : controller::RIGHT_HAND;
|
||||
ovr::for_each_hand([&](ovrTrackedDeviceTypeId hand) {
|
||||
size_t handIndex = (hand == VRAPI_TRACKED_DEVICE_HAND_LEFT) ? 0 : 1;
|
||||
int controller = (hand == VRAPI_TRACKED_DEVICE_HAND_LEFT) ? controller::LEFT_HAND : controller::RIGHT_HAND;
|
||||
auto& handData = _hands[handIndex];
|
||||
const auto& tracking = handData.tracking;
|
||||
++numTrackedControllers;
|
||||
|
@ -476,7 +476,7 @@ void OculusMobileInputDevice::focusOutEvent() {
|
|||
|
||||
void OculusMobileInputDevice::handlePose(float deltaTime,
|
||||
const controller::InputCalibrationData& inputCalibrationData,
|
||||
ovrHandedness hand, const ovrRigidBodyPosef& handPose) {
|
||||
ovrTrackedDeviceTypeId hand, const ovrRigidBodyPosef& handPose) {
|
||||
auto poseId = (hand == VRAPI_HAND_LEFT) ? controller::LEFT_HAND : controller::RIGHT_HAND;
|
||||
auto& pose = _poseStateMap[poseId];
|
||||
pose = ovr::toControllerPose(hand, handPose);
|
||||
|
@ -507,7 +507,7 @@ void OculusMobileInputDevice::handleHeadPose(float deltaTime,
|
|||
}
|
||||
|
||||
void OculusMobileInputDevice::handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData,
|
||||
ovrHandedness hand, const ovrRigidBodyPosef& handPose) {
|
||||
ovrTrackedDeviceTypeId hand, const ovrRigidBodyPosef& handPose) {
|
||||
auto poseId = (hand == VRAPI_HAND_LEFT ? controller::LEFT_HAND : controller::RIGHT_HAND);
|
||||
auto& pose = _poseStateMap[poseId];
|
||||
const auto& lastHandPose = (hand == VRAPI_HAND_LEFT) ? _hands[0].lastPose : _hands[1].lastPose;
|
||||
|
|
|
@ -425,12 +425,12 @@ void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transactio
|
|||
continue;
|
||||
}
|
||||
|
||||
bool needsNewShape = object->needsNewShape();
|
||||
bool needsNewShape = object->needsNewShape() && object->_entity->isReadyToComputeShape();
|
||||
if (needsNewShape) {
|
||||
ShapeType shapeType = object->getShapeType();
|
||||
if (shapeType == SHAPE_TYPE_STATIC_MESH) {
|
||||
ShapeRequest shapeRequest(object->_entity);
|
||||
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||
if (requestItr == _shapeRequests.end()) {
|
||||
ShapeInfo shapeInfo;
|
||||
object->_entity->computeShapeInfo(shapeInfo);
|
||||
|
|
|
@ -338,7 +338,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
bool intersectedSomething = false;
|
||||
|
||||
// if we aren't active, we can't pick yet...
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
return intersectedSomething;
|
||||
}
|
||||
|
||||
|
@ -493,7 +493,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co
|
|||
bool intersectedSomething = false;
|
||||
|
||||
// if we aren't active, we can't pick yet...
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
return intersectedSomething;
|
||||
}
|
||||
|
||||
|
@ -958,16 +958,42 @@ void Model::setCauterized(bool cauterized, const render::ScenePointer& scene) {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setPrimitiveMode(PrimitiveMode primitiveMode) {
|
||||
void Model::setPrimitiveMode(PrimitiveMode primitiveMode, const render::ScenePointer& scene) {
|
||||
if (_primitiveMode != primitiveMode) {
|
||||
_primitiveMode = primitiveMode;
|
||||
updateRenderItemsKey(nullptr);
|
||||
if (!scene) {
|
||||
_needsFixupInScene = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||
std::unordered_map<int, bool> shouldInvalidatePayloadShapeKeyMap;
|
||||
|
||||
for (auto& shape : _modelMeshRenderItemShapes) {
|
||||
shouldInvalidatePayloadShapeKeyMap[shape.meshIndex] = shouldInvalidatePayloadShapeKey(shape.meshIndex);
|
||||
}
|
||||
|
||||
render::Transaction transaction;
|
||||
|
||||
for (int i = 0; i < (int)_modelMeshRenderItemIDs.size(); i++) {
|
||||
auto itemID = _modelMeshRenderItemIDs[i];
|
||||
auto meshIndex = _modelMeshRenderItemShapes[i].meshIndex;
|
||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning] (ModelMeshPartPayload& data) {
|
||||
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setCullWithParent(bool cullWithParent) {
|
||||
void Model::setCullWithParent(bool cullWithParent, const render::ScenePointer& scene) {
|
||||
if (_cullWithParent != cullWithParent) {
|
||||
_cullWithParent = cullWithParent;
|
||||
if (!scene) {
|
||||
_needsFixupInScene = true;
|
||||
return;
|
||||
}
|
||||
|
||||
render::Transaction transaction;
|
||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
|
@ -977,14 +1003,27 @@ void Model::setCullWithParent(bool cullWithParent) {
|
|||
data.updateKey(renderItemsKey);
|
||||
});
|
||||
}
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setRenderWithZones(const QVector<QUuid>& renderWithZones) {
|
||||
void Model::setRenderWithZones(const QVector<QUuid>& renderWithZones, const render::ScenePointer& scene) {
|
||||
if (_renderWithZones != renderWithZones) {
|
||||
_renderWithZones = renderWithZones;
|
||||
setRenderItemsNeedUpdate();
|
||||
|
||||
if (!scene) {
|
||||
_needsFixupInScene = true;
|
||||
return;
|
||||
}
|
||||
|
||||
render::Transaction transaction;
|
||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
for (auto item : _modelMeshRenderItemIDs) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [renderWithZones, renderItemsKey](ModelMeshPartPayload& data) {
|
||||
data.setRenderWithZones(renderWithZones);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1129,7 +1168,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch, bool forward) {
|
|||
}
|
||||
|
||||
Extents Model::getBindExtents() const {
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
return Extents();
|
||||
}
|
||||
const Extents& bindExtents = getHFMModel().bindExtents;
|
||||
|
@ -1143,7 +1182,7 @@ glm::vec3 Model::getNaturalDimensions() const {
|
|||
}
|
||||
|
||||
Extents Model::getMeshExtents() const {
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
return Extents();
|
||||
}
|
||||
const Extents& extents = getHFMModel().meshExtents;
|
||||
|
@ -1157,7 +1196,7 @@ Extents Model::getMeshExtents() const {
|
|||
}
|
||||
|
||||
Extents Model::getUnscaledMeshExtents() const {
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
return Extents();
|
||||
}
|
||||
|
||||
|
@ -1189,7 +1228,7 @@ void Model::setJointTranslation(int index, bool valid, const glm::vec3& translat
|
|||
}
|
||||
|
||||
int Model::getParentJointIndex(int jointIndex) const {
|
||||
return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).parentIndex : -1;
|
||||
return (isLoaded() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).parentIndex : -1;
|
||||
}
|
||||
|
||||
void Model::setTextures(const QVariantMap& textures) {
|
||||
|
@ -1290,7 +1329,7 @@ QStringList Model::getJointNames() const {
|
|||
Q_RETURN_ARG(QStringList, result));
|
||||
return result;
|
||||
}
|
||||
return isActive() ? getHFMModel().getJointNames() : QStringList();
|
||||
return isLoaded() ? getHFMModel().getJointNames() : QStringList();
|
||||
}
|
||||
|
||||
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) {
|
||||
|
@ -1306,7 +1345,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRes
|
|||
// mesh, and so we can't do the needed calculations for scaling to fit to a single largest dimension. In this
|
||||
// case we will record that we do want to do this, but we will stick our desired single dimension into the
|
||||
// first element of the vec3 for the non-fixed aspect ration dimensions
|
||||
if (!isActive()) {
|
||||
if (!isLoaded()) {
|
||||
_scaleToFit = scaleToFit;
|
||||
if (scaleToFit) {
|
||||
_scaleToFitDimensions = glm::vec3(largestDimension, FAKE_DIMENSION_PLACEHOLDER, FAKE_DIMENSION_PLACEHOLDER);
|
||||
|
@ -1387,7 +1426,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
|||
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|
||||
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
|
||||
|
||||
if (isActive() && fullUpdate) {
|
||||
if (isLoaded() && fullUpdate) {
|
||||
onInvalidate();
|
||||
|
||||
// check for scale to fit
|
||||
|
@ -1454,7 +1493,6 @@ void Model::updateBlendshapes() {
|
|||
}
|
||||
|
||||
void Model::deleteGeometry() {
|
||||
_deleteGeometryCounter++;
|
||||
_meshStates.clear();
|
||||
_rig.destroyAnimGraph();
|
||||
_blendedBlendshapeCoefficients.clear();
|
||||
|
|
|
@ -116,14 +116,14 @@ public:
|
|||
void setHifiRenderLayer(render::hifi::Layer layer, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
bool isCauterized() const { return _cauterized; }
|
||||
void setCauterized(bool value, const render::ScenePointer& scene);
|
||||
void setCauterized(bool value, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
void setPrimitiveMode(PrimitiveMode primitiveMode);
|
||||
void setPrimitiveMode(PrimitiveMode primitiveMode, const render::ScenePointer& scene = nullptr);
|
||||
PrimitiveMode getPrimitiveMode() const { return _primitiveMode; }
|
||||
|
||||
void setCullWithParent(bool value);
|
||||
void setCullWithParent(bool value, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
void setRenderWithZones(const QVector<QUuid>& renderWithZones);
|
||||
void setRenderWithZones(const QVector<QUuid>& renderWithZones, const render::ScenePointer& scene = nullptr);
|
||||
const QVector<QUuid>& getRenderWithZones() const { return _renderWithZones; }
|
||||
|
||||
// Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model.
|
||||
|
@ -183,10 +183,7 @@ public:
|
|||
const HFMModel& getHFMModel() const { assert(isLoaded()); return _renderGeometry->getHFMModel(); }
|
||||
const MaterialMapping& getMaterialMapping() const { assert(isLoaded()); return _renderGeometry->getMaterialMapping(); }
|
||||
|
||||
bool isActive() const { return isLoaded(); }
|
||||
|
||||
bool didVisualGeometryRequestFail() const { return _visualGeometryRequestFailed; }
|
||||
bool didCollisionGeometryRequestFail() const { return _collisionGeometryRequestFailed; }
|
||||
|
||||
glm::mat4 getWorldToHFMMatrix() const;
|
||||
|
||||
|
@ -338,7 +335,6 @@ public:
|
|||
|
||||
const MeshState& getMeshState(int index) { return _meshStates.at(index); }
|
||||
|
||||
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
||||
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItemsMap; }
|
||||
BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; }
|
||||
|
||||
|
@ -474,10 +470,7 @@ protected:
|
|||
friend class ModelMeshPartPayload;
|
||||
Rig _rig;
|
||||
|
||||
uint32_t _deleteGeometryCounter { 0 };
|
||||
|
||||
bool _visualGeometryRequestFailed { false };
|
||||
bool _collisionGeometryRequestFailed { false };
|
||||
|
||||
bool _renderItemsNeedUpdate { false };
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ public slots:
|
|||
* @param {number} roll - The roll angle in degrees.
|
||||
* @returns {Quat} A quaternion created using the <code>pitch</code>, <code>yaw</code>, and <code>roll</code> Euler angles.
|
||||
* @example <caption>Create a rotation of 180 degrees about the y axis.</caption>
|
||||
* var rotation = Quat.fromPitchYawRollDgrees(0, 180, 0 );
|
||||
* var rotation = Quat.fromPitchYawRollDegrees(0, 180, 0 );
|
||||
*/
|
||||
glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll);
|
||||
|
||||
|
|
|
@ -451,7 +451,7 @@ void ScriptEngine::executeOnScriptThread(std::function<void()> function, const Q
|
|||
function();
|
||||
}
|
||||
|
||||
void ScriptEngine::waitTillDoneRunning() {
|
||||
void ScriptEngine::waitTillDoneRunning(bool shutdown) {
|
||||
// Engine should be stopped already, but be defensive
|
||||
stop();
|
||||
|
||||
|
@ -520,12 +520,14 @@ void ScriptEngine::waitTillDoneRunning() {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: This will be called on the main application thread (among other threads) from stopAllScripts.
|
||||
// The thread will need to continue to process events, because
|
||||
// the scripts will likely need to marshall messages across to the main thread, e.g.
|
||||
// if they access Settings or Menu in any of their shutdown code. So:
|
||||
// Process events for this thread, allowing invokeMethod calls to pass between threads.
|
||||
QCoreApplication::processEvents();
|
||||
if (shutdown) {
|
||||
// NOTE: This will be called on the main application thread (among other threads) from stopAllScripts.
|
||||
// The thread will need to continue to process events, because
|
||||
// the scripts will likely need to marshall messages across to the main thread, e.g.
|
||||
// if they access Settings or Menu in any of their shutdown code. So:
|
||||
// Process events for this thread, allowing invokeMethod calls to pass between threads.
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
// Avoid a pure busy wait
|
||||
QThread::yieldCurrentThread();
|
||||
|
@ -1941,9 +1943,12 @@ QScriptValue ScriptEngine::require(const QString& moduleId) {
|
|||
// modules get cached in `Script.require.cache` and (similar to Node.js) users can access it
|
||||
// to inspect particular entries and invalidate them by deleting the key:
|
||||
// `delete Script.require.cache[Script.require.resolve(moduleId)];`
|
||||
|
||||
// Check to see if we should invalidate the cache based on a user setting.
|
||||
Setting::Handle<bool> getCachebustSetting {"cachebustScriptRequire", false };
|
||||
|
||||
// cacheMeta is just used right now to tell deleted keys apart from undefined ones
|
||||
bool invalidateCache = module.isUndefined() && cacheMeta.property(moduleId).isValid();
|
||||
bool invalidateCache = getCachebustSetting.get() || (module.isUndefined() && cacheMeta.property(moduleId).isValid());
|
||||
|
||||
// reset the cacheMeta record so invalidation won't apply next time, even if the module fails to load
|
||||
cacheMeta.setProperty(modulePath, QScriptValue());
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Brad Hefta-Gaub on 12/14/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -196,7 +197,7 @@ public:
|
|||
Q_INVOKABLE void stop(bool marshal = false);
|
||||
|
||||
// Stop any evaluating scripts and wait for the scripting thread to finish.
|
||||
void waitTillDoneRunning();
|
||||
void waitTillDoneRunning(bool shutdown = false);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
|
||||
|
|
|
@ -182,7 +182,7 @@ void ScriptEngines::shutdownScripting() {
|
|||
// want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing
|
||||
// any application state after we leave this stopAllScripts() method
|
||||
qCDebug(scriptengine) << "waiting on script:" << scriptName;
|
||||
scriptEngine->waitTillDoneRunning();
|
||||
scriptEngine->waitTillDoneRunning(true);
|
||||
qCDebug(scriptengine) << "done waiting on script:" << scriptName;
|
||||
}
|
||||
// Once the script is stopped, we can remove it from our set
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Collection of scripts to create server distribution packages. Most of these scripts assume
|
||||
use of the build script at https://github.com/kasenvr/vircadia-builder, specifically that
|
||||
use of the build script at https://github.com/vircadia/vircadia-builder, specifically that
|
||||
the following directory structure exists
|
||||
|
||||
base folder/
|
||||
|
|
|
@ -5,8 +5,8 @@ Maintainer: Heather Anderson <heath@odysseus.anderson.name>
|
|||
Build-Depends: debhelper (>= 10)
|
||||
Standards-Version: 4.1.2
|
||||
Homepage: https://vircadia.com
|
||||
Vcs-Git: https://github.com/kasenvr/project-athena.git
|
||||
Vcs-Browser: https://github.com/kasenvr/project-athena
|
||||
Vcs-Git: https://github.com/vircadia/vircadia.git
|
||||
Vcs-Browser: https://github.com/vircadia/vircadia
|
||||
|
||||
Package: vircadia-server
|
||||
Architecture: any
|
||||
|
|
|
@ -9,7 +9,7 @@ Summary: Vircadia metaverse platform, based on the High Fidelity Engine.
|
|||
|
||||
License: ASL 2.0
|
||||
URL: https://vircadia.com
|
||||
Source0: https://github.com/kasenvr/vircadia-builder/blob/master/vircadia-builder
|
||||
Source0: https://github.com/vircadia/vircadia-builder/blob/master/vircadia-builder
|
||||
|
||||
#BuildRequires: systemd-rpm-macros
|
||||
BuildRequires: chrpath
|
||||
|
|
|
@ -53,5 +53,5 @@ set(DIR "opusCodec")
|
|||
add_subdirectory(${DIR})
|
||||
|
||||
# example plugins
|
||||
set(DIR "KasenAPIExample")
|
||||
set(DIR "JSAPIExample")
|
||||
add_subdirectory(${DIR})
|
||||
|
|
3
plugins/JSAPIExample/CMakeLists.txt
Normal file
3
plugins/JSAPIExample/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
set(TARGET_NAME JSAPIExample)
|
||||
setup_hifi_client_server_plugin(scripting)
|
||||
link_hifi_libraries(shared plugins)
|
251
plugins/JSAPIExample/src/JSAPIExample.cpp
Normal file
251
plugins/JSAPIExample/src/JSAPIExample.cpp
Normal file
|
@ -0,0 +1,251 @@
|
|||
//
|
||||
// JSAPIExample.cpp
|
||||
// plugins/JSAPIExample/src
|
||||
//
|
||||
// Copyright (c) 2019-2020 humbletim (humbletim@gmail.com)
|
||||
// Copyright (c) 2019 Kalila L. (somnilibertas@gmail.com)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// Example of prototyping new JS APIs by leveraging the existing plugin system.
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
#include <SettingHelpers.h> // for ::settingsFilename()
|
||||
#include <SharedUtil.h> // for ::usecTimestampNow()
|
||||
#include <shared/ScriptInitializerMixin.h>
|
||||
|
||||
// NOTE: replace this with your own namespace when starting a new plugin (to avoid .so/.dll symbol clashes)
|
||||
namespace REPLACE_ME_WITH_UNIQUE_NAME {
|
||||
|
||||
static constexpr auto JSAPI_SEMANTIC_VERSION = "0.0.1";
|
||||
static constexpr auto JSAPI_EXPORT_NAME = "JSAPIExample";
|
||||
|
||||
QLoggingCategory logger { "jsapiexample" };
|
||||
|
||||
inline QVariant raiseScriptingError(QScriptContext* context, const QString& message, const QVariant& returnValue = QVariant()) {
|
||||
if (context) {
|
||||
// when a QScriptContext is available throw an actual JS Exception (which can be caught using try/catch on JS side)
|
||||
context->throwError(message);
|
||||
} else {
|
||||
// otherwise just log the error
|
||||
qCWarning(logger) << "error:" << message;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
QObject* createScopedSettings(const QString& scope, QObject* parent, QString& error);
|
||||
|
||||
class JSAPIExample : public QObject, public QScriptable {
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "JSAPIExample" FILE "plugin.json")
|
||||
Q_PROPERTY(QString version MEMBER _version CONSTANT)
|
||||
public:
|
||||
JSAPIExample() {
|
||||
setObjectName(JSAPI_EXPORT_NAME);
|
||||
auto scriptInit = DependencyManager::get<ScriptInitializers>();
|
||||
if (!scriptInit) {
|
||||
qCWarning(logger) << "COULD NOT INITIALIZE (ScriptInitializers unavailable)" << qApp << this;
|
||||
return;
|
||||
}
|
||||
qCWarning(logger) << "registering w/ScriptInitializerMixin..." << scriptInit.data();
|
||||
scriptInit->registerScriptInitializer([this](QScriptEngine* engine) {
|
||||
auto value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater);
|
||||
engine->globalObject().setProperty(objectName(), value);
|
||||
// qCDebug(logger) << "setGlobalInstance" << objectName() << engine->property("fileName");
|
||||
});
|
||||
// qCInfo(logger) << "plugin loaded" << qApp << toString() << QThread::currentThread();
|
||||
}
|
||||
|
||||
// NOTES: everything within the "public slots:" section below will be available from JS via overall plugin QObject
|
||||
// also, to demonstrate future-proofing JS API code, QVariant's are used throughout most of these examples --
|
||||
// which still makes them very Qt-specific, but avoids depending directly on deprecated QtScript/QScriptValue APIs.
|
||||
// (as such this plugin class and its methods remain forward-compatible with other engines like QML's QJSEngine)
|
||||
|
||||
public slots:
|
||||
// pretty-printed representation for logging eg: print(JSAPIExample)
|
||||
// (note: Qt script engines automatically look for a ".toString" method on native classes when coercing values to strings)
|
||||
QString toString() const { return QString("[%1 version=%2]").arg(objectName()).arg(_version); }
|
||||
|
||||
/**jsdoc
|
||||
* Returns current microseconds (usecs) since Epoch. note: 1000usecs == 1ms
|
||||
* @example <caption>Measure current setTimeout accuracy.</caption>
|
||||
* var expected = 1000;
|
||||
* var start = JSAPIExample.now();
|
||||
* Script.setTimeout(function () {
|
||||
* var elapsed = (JSAPIExample.now() - start)/1000;
|
||||
* print("expected (ms):", expected, "actual (ms):", elapsed);
|
||||
* }, expected);
|
||||
*/
|
||||
QVariant now() const { return usecTimestampNow(); }
|
||||
|
||||
/**jsdoc
|
||||
* Example of returning a JS Object key-value map
|
||||
* @example <caption>"zip" a list of keys and corresponding values to form key-value map</caption>
|
||||
* print(JSON.stringify(JSAPIExample.zip(["a","b"], [1,2])); // { "a": 1, "b": 2 }
|
||||
*/
|
||||
QVariant zip(const QStringList& keys, const QVariantList& values) const {
|
||||
QVariantMap out;
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
out[keys[i]] = i < values.size() ? values[i] : QVariant();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Example of returning a JS Array result
|
||||
* @example <caption>emulate Object.values(keyValues)</caption>
|
||||
* print(JSON.stringify(JSAPIExample.values({ "a": 1, "b": 2 }))); // [1,2]
|
||||
*/
|
||||
QVariant values(const QVariantMap& keyValues) const {
|
||||
QVariantList values = keyValues.values();
|
||||
return values;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Another example of returning JS Array data
|
||||
* @example <caption>generate an integer sequence (inclusive of [from, to])</caption>
|
||||
* print(JSON.stringify(JSAPIExample.seq(1,5)));// [1,2,3,4,5]
|
||||
*/
|
||||
QVariant seq(int from, int to) const {
|
||||
QVariantList out;
|
||||
for (int i = from; i <= to; i++) {
|
||||
out.append(i);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Example of returning arbitrary binary data from C++ (resulting in a JS ArrayBuffer)
|
||||
* see also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer#Examples
|
||||
* @example <caption>return compressed/decompressed versions of the input data</caption>
|
||||
* var data = "testing 1 2 3";
|
||||
* var z = JSAPIExample.qCompressString(data); // z will be an ArrayBuffer
|
||||
* var u = JSAPIExample.qUncompressString(z); // u will be a String value
|
||||
* print(JSON.stringify({ input: data, compressed: z.byteLength, output: u, uncompressed: u.length }));
|
||||
*/
|
||||
QVariant qCompressString(const QString& jsString, int compress_level = -1) const {
|
||||
QByteArray arrayBuffer = qCompress(jsString.toUtf8(), compress_level);
|
||||
return arrayBuffer;
|
||||
}
|
||||
QVariant qUncompressString(const QByteArray& arrayBuffer) const {
|
||||
QString jsString = QString::fromUtf8(qUncompress(arrayBuffer));
|
||||
return jsString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example of exposing a custom "managed" C++ QObject to JS
|
||||
* The lifecycle of the created QObject* instance becomes managed by the invoking QScriptEngine --
|
||||
* it will be automatically cleaned up once no longer reachable from any JS variables/closures.
|
||||
* @example <caption>access persistent settings stored in separate .json files</caption>
|
||||
* var settings = JSAPIExample.getScopedSettings("example");
|
||||
* print("example settings stored in:", settings.fileName());
|
||||
* print("(before) example::timestamp", settings.getValue("timestamp"));
|
||||
* settings.setValue("timestamp", Date.now());
|
||||
* print("(after) example::timestamp", settings.getValue("timestamp"));
|
||||
* print("all example::* keys", settings.allKeys());
|
||||
* settings = null; // optional best pratice; allows the object to be reclaimed ASAP by the JS garbage collector
|
||||
*/
|
||||
QScriptValue getScopedSettings(const QString& scope) {
|
||||
auto engine = QScriptable::engine();
|
||||
if (!engine) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
QString error;
|
||||
auto cppValue = createScopedSettings(scope, engine, error);
|
||||
if (!cppValue) {
|
||||
raiseScriptingError(context(), "error creating scoped settings instance: " + error);
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
return engine->newQObject(cppValue, QScriptEngine::ScriptOwnership, QScriptEngine::ExcludeDeleteLater);
|
||||
}
|
||||
|
||||
private:
|
||||
const QString _version { JSAPI_SEMANTIC_VERSION };
|
||||
};
|
||||
|
||||
// JSSettingsHelper wraps a scoped (prefixed/separate) QSettings and exposes a subset of QSetting APIs as slots
|
||||
class JSSettingsHelper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
JSSettingsHelper(const QString& scope, QObject* parent = nullptr);
|
||||
~JSSettingsHelper();
|
||||
operator bool() const;
|
||||
public slots:
|
||||
QString fileName() const;
|
||||
QString toString() const;
|
||||
QVariant getValue(const QString& key, const QVariant& defaultValue = QVariant());
|
||||
bool setValue(const QString& key, const QVariant& value);
|
||||
QStringList allKeys() const;
|
||||
protected:
|
||||
QString _scope;
|
||||
QString _fileName;
|
||||
QSharedPointer<QSettings> _settings;
|
||||
QString getLocalSettingsPath(const QString& scope) const;
|
||||
};
|
||||
|
||||
// verifies the requested scope is sensible and creates/returns a scoped JSSettingsHelper instance
|
||||
QObject* createScopedSettings(const QString& scope, QObject* parent, QString& error) {
|
||||
const QRegExp VALID_SETTINGS_SCOPE { "[-_A-Za-z0-9]{1,64}" };
|
||||
if (!VALID_SETTINGS_SCOPE.exactMatch(scope)) {
|
||||
error = QString("invalid scope (expected alphanumeric <= 64 chars not '%1')").arg(scope);
|
||||
return nullptr;
|
||||
}
|
||||
return new JSSettingsHelper(scope, parent);
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// ----- inline JSSettingsHelper implementation -----
|
||||
JSSettingsHelper::operator bool() const {
|
||||
return (bool)_settings;
|
||||
}
|
||||
JSSettingsHelper::JSSettingsHelper(const QString& scope, QObject* parent) :
|
||||
QObject(parent), _scope(scope), _fileName(getLocalSettingsPath(scope)),
|
||||
_settings(_fileName.isEmpty() ? nullptr : new QSettings(_fileName, JSON_FORMAT)) {
|
||||
}
|
||||
JSSettingsHelper::~JSSettingsHelper() {
|
||||
qCDebug(logger) << "~JSSettingsHelper" << _scope << _fileName << this;
|
||||
}
|
||||
QString JSSettingsHelper::fileName() const {
|
||||
return _settings ? _settings->fileName() : "";
|
||||
}
|
||||
QString JSSettingsHelper::toString() const {
|
||||
return QString("[JSSettingsHelper scope=%1 valid=%2]").arg(_scope).arg((bool)_settings);
|
||||
}
|
||||
QVariant JSSettingsHelper::getValue(const QString& key, const QVariant& defaultValue) {
|
||||
return _settings ? _settings->value(key, defaultValue) : defaultValue;
|
||||
}
|
||||
bool JSSettingsHelper::setValue(const QString& key, const QVariant& value) {
|
||||
if (_settings) {
|
||||
if (value.isValid()) {
|
||||
_settings->setValue(key, value);
|
||||
} else {
|
||||
_settings->remove(key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
QStringList JSSettingsHelper::allKeys() const {
|
||||
return _settings ? _settings->allKeys() : QStringList{};
|
||||
}
|
||||
QString JSSettingsHelper::getLocalSettingsPath(const QString& scope) const {
|
||||
// generate a prefixed filename (relative to the main application's Interface.json file)
|
||||
const QString fileName = QString("jsapi_%1.json").arg(scope);
|
||||
return QFileInfo(::settingsFilename()).dir().filePath(fileName);
|
||||
}
|
||||
// ----- /inline JSSettingsHelper implementation -----
|
||||
|
||||
} // namespace REPLACE_ME_WITH_UNIQUE_NAME
|
||||
|
||||
#include "JSAPIExample.moc"
|
4
plugins/JSAPIExample/src/plugin.json
Normal file
4
plugins/JSAPIExample/src/plugin.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name":"JS API Example",
|
||||
"version": 1
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue