diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index d1ecb1bb02..f28cf1e66b 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -9,7 +9,8 @@ env: BUILD_TYPE: Release CI_BUILD: Github GIT_COMMIT: ${{ github.sha }} - HIFI_VCPKG_BOOTSTRAP: true + # VCPKG did not build well on OSX disabling HIFI_VCPKG_BOOTSTRAP, which invokes a download to a working version of vcpkg + # HIFI_VCPKG_BOOTSTRAP: true RELEASE_TYPE: PR RELEASE_NUMBER: ${{ github.event.number }} VERSION_CODE: ${{ github.event.number }} @@ -28,8 +29,12 @@ jobs: build: strategy: matrix: - os: [windows-latest, macOS-latest] + os: [windows-latest, macOS-latest, ubuntu-18.04] build_type: [full] + include: + - os: ubuntu-18.04 + build_type: full + apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 fail-fast: false runs-on: ${{matrix.os}} if: github.event.action != 'labeled' || github.event.label.name == 'rebuild' @@ -39,10 +44,14 @@ jobs: 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}})" + # Linux build variables - if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then + if [[ "${{ matrix.os }}" = "ubuntu-"* ]]; then echo ::set-env name=PYTHON_EXEC::python3 - echo ::set-env name=INSTALLER_EXT::tgz + echo ::set-env name=INSTALLER_EXT::* + echo ::set-env name=CMAKE_BUILD_EXTRA::"-- -j3" + echo ::set-env name=CMAKE_EXTRA::"-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" fi # Mac build variables if [ "${{ matrix.os }}" = "macOS-latest" ]; then @@ -61,11 +70,11 @@ jobs: shell: bash run: | echo "${{ steps.buildenv1.outputs.symbols_archive }}" - echo ::set-env name=ARTIFACT_PATTERN::HighFidelity-Beta-*.$INSTALLER_EXT + echo ::set-env name=ARTIFACT_PATTERN::Vircadia-Alpha-PR${{ github.event.number }}-*.$INSTALLER_EXT # Build type variables - echo ::set-env name=INSTALLER::HighFidelity-Beta-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT + echo ::set-env name=INSTALLER::Vircadia-Alpha-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT - name: Clear Working Directory - if: matrix.os[1] == 'windows' + if: startsWith(matrix.os, 'windows') shell: bash working-directory: ${{runner.workspace}} run: rm -rf ./* @@ -73,61 +82,83 @@ jobs: with: submodules: true fetch-depth: 1 + - name: Install dependencies + shell: bash + if: startsWith(matrix.os, 'ubuntu') + run: | + echo "Installing Python Modules:" + pip3 install distro || exit 1 + + echo "Updating apt repository index" + sudo apt update || exit 1 + + echo "Installing apt packages" + sudo apt install -y ${{ matrix.apt-dependencies }} || exit 1 - name: Create Build Environment shell: bash run: cmake -E make_directory "${{runner.workspace}}/build" - name: Configure CMake working-directory: ${{runner.workspace}}/build shell: bash - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE $CMAKE_EXTRA + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release $CMAKE_EXTRA - name: Build Application working-directory: ${{runner.workspace}}/build shell: bash - run: cmake --build . --config $BUILD_TYPE --target $APP_NAME + run: cmake --build . --config $BUILD_TYPE --target $APP_NAME $CMAKE_BUILD_EXTRA - name: Build Domain Server working-directory: ${{runner.workspace}}/build shell: bash - run: cmake --build . --config $BUILD_TYPE --target domain-server + run: cmake --build . --config $BUILD_TYPE --target domain-server $CMAKE_BUILD_EXTRA - name: Build Assignment Client working-directory: ${{runner.workspace}}/build shell: bash - run: cmake --build . --config $BUILD_TYPE --target assignment-client + run: cmake --build . --config $BUILD_TYPE --target assignment-client $CMAKE_BUILD_EXTRA - name: Build Console working-directory: ${{runner.workspace}}/build shell: bash - run: cmake --build . --config $BUILD_TYPE --target packaged-server-console + run: cmake --build . --config $BUILD_TYPE --target packaged-server-console $CMAKE_BUILD_EXTRA - name: Build Installer working-directory: ${{runner.workspace}}/build shell: bash - run: cmake --build . --config $BUILD_TYPE --target package + run: | + echo "Retry code from https://unix.stackexchange.com/a/137639" + function fail { + echo $1 >&2 + exit 1 + } + + function retry { + local n=1 + local max=5 + local delay=15 + while true; do + "$@" && break || { + if [[ $n -lt $max ]]; then + ((n++)) + echo "Command failed. Attempt $n/$max:" + sleep $delay; + else + fail "The command has failed after $n attempts." + fi + } + done + } + retry cmake --build . --config $BUILD_TYPE --target package $CMAKE_BUILD_EXTRA + - name: Output system stats + if: ${{ always() }} + working-directory: ${{runner.workspace}}/build + shell: bash + run: | + echo "Disk usage:" + df -h - name: Output Installer Logs if: failure() && matrix.os == 'windows-latest' shell: bash working-directory: ${{runner.workspace}}/build run: cat ./_CPack_Packages/win64/NSIS/NSISOutput.log - - build_full_linux: - runs-on: ubuntu-latest - if: github.event.action != 'labeled' || github.event.label.name == 'rebuild' - steps: - - uses: actions/checkout@v1 - with: - submodules: true - fetch-depth: 1 - - name: Update apt repository index - run: sudo apt update - - name: Install apt packages - run: sudo apt install -y mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 - - name: Install python modules - shell: bash - run: pip install boto3 distro PyGithub - - name: Create Build Environment - run: cmake -E make_directory ${{runner.workspace}}/build - - name: Configure CMake - working-directory: ${{runner.workspace}}/build - shell: bash - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TOOLS:BOOLEAN=FALSE - - name: + - name: Upload Artifact shell: bash working-directory: ${{runner.workspace}}/build - run: cmake --build . -- -j3 + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: $PYTHON_EXEC "$GITHUB_WORKSPACE/tools/ci-scripts/upload_to_publish_server.py" diff --git a/BUILD.md b/BUILD.md index bc032d4ec7..b30160e7e4 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,6 +1,6 @@ # General Build Information -*Last Updated on December 21, 2019* +*Last Updated on May 17, 2020* ### OS Specific Build Guides @@ -22,7 +22,7 @@ These dependencies need not be installed manually. They are automatically downlo - [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83 - [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8 - [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac) -- [OpenVR](https://github.com/ValveSoftware/openvr): 1.0.6 (Win32 only) +- [OpenVR](https://github.com/ValveSoftware/openvr): 1.11.11 (Win32 only) - [Polyvox](http://www.volumesoffun.com/): 0.2.1 - [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3 - [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3 @@ -38,7 +38,7 @@ These are not placed in your normal build tree when doing an out of source build #### CMake -Athena uses CMake to generate build files and project files for your platform. +Vircadia uses CMake to generate build files and project files for your platform. #### Qt CMake will download Qt 5.12.3 using vcpkg. @@ -51,9 +51,9 @@ This can either be entered directly into your shell session before you build or export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.12.3/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake -#### Vcpkg +#### VCPKG -Athena uses vcpkg to download and build dependencies. +Vircadia uses vcpkg to download and build dependencies. You do not need to install vcpkg. Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory. @@ -63,7 +63,33 @@ export HIFI_VCPKG_BASE=/path/to/directory Where /path/to/directory is the path to a directory where you wish the build files to get stored. -#### Generating build files +#### Generating Build Files + +##### Possible Environment Variables + + // The URL to post the dump to. + CMAKE_BACKTRACE_URL + // The identifying tag of the release. + CMAKE_BACKTRACE_TOKEN + + // The release version. + RELEASE_NUMBER + // The build commit. + BUILD_NUMBER + + // The type of release. + RELEASE_TYPE=PRODUCTION|PR + RELEASE_BUILD=PRODUCTION|PR + + // TODO: What do these do? + PRODUCTION_BUILD=0|1 + STABLE_BUILD=0|1 + + // TODO: What do these do? + USE_STABLE_GLOBAL_SERVICES=1 + BUILD_GLOBAL_SERVICES=STABLE + +##### Generate Files Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean. @@ -71,7 +97,19 @@ Create a build directory in the root of your checkout and then run the CMake bui cd build cmake .. -If cmake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`. +If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`. + +##### Generating a release/debug only vcpkg build + +In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your cmake generate command. Building a release only vcpkg can drastically decrease the total build time. + +For release only vcpkg: + +`cmake .. -DVCPKG_BUILD_TYPE=release` + +For debug only vcpkg: + +`cmake .. -DVCPKG_BUILD_TYPE=debug` #### Variables @@ -85,13 +123,13 @@ For example, to pass the QT_CMAKE_PREFIX_PATH variable (if not using the vcpkg'e The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies). -You can point our [Cmake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency. +You can point our [CMake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency. In the examples below the variable $NAME would be replaced by the name of the dependency in uppercase, and $name would be replaced by the name of the dependency in lowercase (ex: OPENSSL_ROOT_DIR, openssl). * $NAME_ROOT_DIR - pass this variable to Cmake with the -DNAME_ROOT_DIR= flag when running Cmake to generate build files * $NAME_ROOT_DIR - set this variable in your ENV -* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name' +* HIFI_LIB_DIR - set this variable in your ENV to your Vircadia lib folder, should contain a folder '$name' ### Optional Components diff --git a/BUILD_LINUX_CHEATSHEET.md b/BUILD_LINUX_CHEATSHEET.md index 9e7534418a..9297f9fd7d 100644 --- a/BUILD_LINUX_CHEATSHEET.md +++ b/BUILD_LINUX_CHEATSHEET.md @@ -1,4 +1,7 @@ ## This guide is specific to Ubuntu 16.04. + +THIS DOCUMENT IS OUTDATED. + Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com ``` diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 69cfed7de9..b39aadb287 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -6,7 +6,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies ### Homebrew -[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some High Fidelity dependencies very simple. +[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some Vircadia dependencies very simple. brew install cmake openssl diff --git a/BUILD_WIN.md b/BUILD_WIN.md index bdab7e6e6d..c057d9ca5d 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,6 +1,6 @@ # Build Windows -*Last Updated on January 13, 2020* +*Last Updated on May 17, 2020* This is a stand-alone guide for creating your first Vircadia build for Windows 64-bit. @@ -47,7 +47,7 @@ Download and install the latest version of CMake 3.15. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.15 Version page](https://cmake.org/files/v3.15/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted. ### Step 4. Create VCPKG environment variable -In the next step, you will use CMake to build Project Athena. By default, the CMake process builds dependency files in Windows' `%TEMP%` directory, which is periodically cleared by the operating system. To prevent you from having to re-build the dependencies in the event that Windows clears that directory, we recommend that you create a `HIFI_VCPKG_BASE` environment variable linked to a directory somewhere on your machine. That directory will contain all dependency files until you manually remove them. +In the next step, you will use CMake to build Vircadia. By default, the CMake process builds dependency files in Windows' `%TEMP%` directory, which is periodically cleared by the operating system. To prevent you from having to re-build the dependencies in the event that Windows clears that directory, we recommend that you create a `HIFI_VCPKG_BASE` environment variable linked to a directory somewhere on your machine. That directory will contain all dependency files until you manually remove them. To create this variable: * Naviagte to 'Edit the System Environment Variables' Through the start menu. @@ -68,7 +68,7 @@ To create this variable: ### Step 5. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: -`cd "%HIFI_DIR%"` +`cd "%VIRCADIA_DIR%"` `mkdir build` `cd build` @@ -78,11 +78,11 @@ Run `cmake .. -G "Visual Studio 15 Win64"`. #### If you're using Visual Studio 2019, Run `cmake .. -G "Visual Studio 16 2019" -A x64`. -Where `%HIFI_DIR%` is the directory for the highfidelity repository. +Where `%VIRCADIA_DIR%` is the directory for the Vircadia repository. ### Step 6. Making a Build -Open `%HIFI_DIR%\build\athena.sln` using Visual Studio. +Open `%VIRCADIA_DIR%\build\vircadia.sln` using Visual Studio. Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance. @@ -98,22 +98,22 @@ Restart Visual Studio again. In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run from the menu bar `Debug > Start Debugging`. -Now, you should have a full build of Project Athena and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow. +Now, you should have a full build of Vircadia and be able to run the Interface using Visual Studio. -Note: You can also run Interface by launching it from command line or File Explorer from `%HIFI_DIR%\build\interface\Release\interface.exe` +Note: You can also run Interface by launching it from command line or File Explorer from `%VIRCADIA_DIR%\build\interface\Release\interface.exe` ## Troubleshooting For any problems after Step #6, first try this: -* Delete your locally cloned copy of the highfidelity repository +* Delete your locally cloned copy of the Vircadia repository * Restart your computer * Redownload the [repository](https://github.com/kasenvr/project-athena) * Restart directions from Step #6 #### CMake gives you the same error message repeatedly after the build fails -Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. +Remove `CMakeCache.txt` found in the `%VIRCADIA_DIR%\build` directory. #### CMake can't find OpenSSL -Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. Verify that your HIFI_VCPKG_BASE environment variable is set and pointing to the correct location. Verify that the file `${HIFI_VCPKG_BASE}/installed/x64-windows/include/openssl/ssl.h` exists. +Remove `CMakeCache.txt` found in the `%VIRCADIA_DIR%\build` directory. Verify that your HIFI_VCPKG_BASE environment variable is set and pointing to the correct location. Verify that the file `${HIFI_VCPKG_BASE}/installed/x64-windows/include/openssl/ssl.h` exists. diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bf7b8bc56..eac12d5ae7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,8 +67,12 @@ if (HIFI_ANDROID) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) else() + set(VCPKG_BUILD_TYPE_PARAM "") + if (VCPKG_BUILD_TYPE) + set(VCPKG_BUILD_TYPE_PARAM --vcpkg-build-type ${VCPKG_BUILD_TYPE}) + endif() execute_process( - COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --release-type ${RELEASE_TYPE} --build-root ${CMAKE_BINARY_DIR} + COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --release-type ${RELEASE_TYPE} --build-root ${CMAKE_BINARY_DIR} ${VCPKG_BUILD_TYPE_PARAM} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) # squelch the Policy CMP0074 warning without requiring an update to cmake 3.12. @@ -93,7 +97,7 @@ endif() option(VCPKG_APPLOCAL_DEPS OFF) -project(athena) +project(vircadia) include("cmake/init.cmake") include("cmake/compiler.cmake") option(VCPKG_APPLOCAL_DEPS OFF) @@ -266,7 +270,6 @@ find_package( Threads ) add_definitions(-DGLM_FORCE_RADIANS) add_definitions(-DGLM_ENABLE_EXPERIMENTAL) add_definitions(-DGLM_FORCE_CTOR_INIT) -add_definitions(-DGLM_LANG_STL11_FORCED) # Workaround for GLM not detecting support for C++11 templates on Android if (WIN32) # Deal with fakakta Visual Studo 2017 bug diff --git a/CODING_STANDARD.md b/CODING_STANDARD.md index fd1843e981..1c2f3cb27e 100644 --- a/CODING_STANDARD.md +++ b/CODING_STANDARD.md @@ -976,18 +976,17 @@ while (true) { #### [4.3.4] Source files (header and implementation) must include a boilerplate. -Boilerplates should include the filename, location, creator, copyright Project Athena contributors, and Apache 2.0 License -information. This should be placed at the top of the file. If editing an existing file that is copyright High Fidelity, add a -second copyright line, copyright Project Athena contributors. +Boilerplates should include the filename, creator, copyright Vircadia contributors, and Apache 2.0 License information. +This should be placed at the top of the file. If editing an existing file that is copyright High Fidelity, add a second +copyright line, copyright Vircadia contributors. ```cpp // // NodeList.h -// libraries/shared/src // // Created by Stephen Birarda on 15 Feb 2013. // Copyright 2013 High Fidelity, Inc. -// Copyright 2020 Project Athena contributors. +// Copyright 2020 Vircadia contributors. // // This is where you could place an optional one line comment about the file. // diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a62e4b2825..aeb6f49280 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Contributing ``` git remote add upstream https://github.com/kasenvr/project-athena - git pull upstream kasen/core + git pull upstream master ``` Resolve any conflicts that arise with this step. @@ -29,7 +29,7 @@ Contributing 7. Push to your fork ``` - git push origin kasen/core + git push origin new_branch_name ``` 8. Submit a pull request diff --git a/INSTALL.md b/INSTALL.md index 10858200e7..bbaaafd6ef 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,10 +1,10 @@ # Creating an Installer -Follow the [build guide](BUILD.md) to figure out how to build Project Athena for your platform. +Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. During generation, CMake should produce an `install` target and a `package` target. -The `install` target will copy the Project Athena targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. +The `install` target will copy the Vircadia targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt` ### Packaging @@ -15,7 +15,7 @@ To produce an installer, run the `package` target. To produce an executable installer on Windows, the following are required: -1. [7-zip]() +1. [7-zip]() 1. [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.04 Install using defaults (will install to `C:\Program Files (x86)\NSIS`) @@ -56,22 +56,23 @@ To produce an executable installer on Windows, the following are required: 1. Copy `Release\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\` 1. Copy `ReleaseUnicode\ApplicationID.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\` -1. [npm]() +1. [Node.JS and NPM]() 1. Install version 10.15.0 LTS 1. Perform a clean cmake from a new terminal. -1. Open the `athena.sln` solution and select the Release configuration. +1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration. 1. Build the solution. +1. Build CMakeTargets->INSTALL 1. Build `packaged-server-console-npm-install` (found under **hidden/Server Console**) 1. Build `packaged-server-console` (found under **Server Console**) This will add 2 folders to `build\server-console\` - `server-console-win32-x64` and `x64` -1. Build CMakeTargets->PACKAGE - Installer is now available in `build\_CPack_Packages\win64\NSIS` +1. Build CMakeTargets->PACKAGE + The installer is now available in `build\_CPack_Packages\win64\NSIS` #### OS X 1. [npm]() - Install version 10.15.0 LTS + Install version 12.16.3 LTS 1. Perform a clean CMake. 1. Perform a Release build of ALL_BUILD @@ -80,3 +81,9 @@ To produce an executable installer on Windows, the following are required: Sandbox-darwin-x64 1. Perform a Release build of `package` Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop + +### FAQ + +1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process``` + 1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files. + 1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage. diff --git a/LICENSE b/LICENSE index f88e751de5..8dfe384174 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Copyright (c) 2013-2019, High Fidelity, Inc. -Copyright (c) 2019-2020, Project Athena Contributors. +Copyright (c) 2019-2020, Vircadia contributors. All rights reserved. -https://projectathena.io +https://vircadia.com Licensed under the Apache License version 2.0 (the "License"); You may not use this software except in compliance with the License. diff --git a/README.md b/README.md index f61a60d431..baf333d81f 100644 --- a/README.md +++ b/README.md @@ -12,23 +12,31 @@ Vircadia is a 3D social software project seeking to incrementally bring about a ### How to build the Interface -[For Windows](https://github.com/kasenvr/project-athena/blob/kasen/core/BUILD_WIN.md) +[For Windows](https://github.com/kasenvr/project-athena/blob/master/BUILD_WIN.md) -[For Linux](https://github.com/kasenvr/project-athena/blob/kasen/core/BUILD_LINUX.md) +[For Mac](https://github.com/kasenvr/project-athena/blob/master/BUILD_OSX.md) -[For Linux - Athena Builder](https://github.com/daleglass/athena-builder) +[For Linux](https://github.com/kasenvr/project-athena/blob/master/BUILD_LINUX.md) + +[For Linux - Vircadia Builder](https://github.com/kasenvr/vircadia-builder) ### How to deploy a Server -[For Windows and Linux](https://vircadia.com/download-vircadia/#server) +[For Windows and Linux](https://vircadia.com/deploy-a-server/) ### How to build a Server -[For Linux - Athena Builder](https://github.com/daleglass/athena-builder) +[For Linux - Vircadia Builder](https://github.com/kasenvr/vircadia-builder) + +### How to generate an Installer + +[For Windows](https://github.com/kasenvr/project-athena/blob/master/INSTALL.md) + +[For Linux - AppImage - Vircadia Builder](https://github.com/kasenvr/vircadia-builder/blob/master/README.md#building-appimages) ### Boot to Metaverse: The Goal -Having a place to experience adventure, a place to relax with calm breath, that's a world to live in. An engine to support infinite combinations and possibilities of worlds without censorship and interruption, that's a metaverse. Finding a way to make infinite realities our reality, that's the dream. +Having a place to experience adventure, a place to relax with calm breath, that's a world to live in. An engine to support infinite combinations and possibilities of worlds without censorship and interruption, that's a metaverse. Finding a way to make infinite realities our reality is the dream. ### Boot to Metaverse: The Technicals diff --git a/README_hifi.md b/README_hifi.md index 46433a155c..325cba8d42 100644 --- a/README_hifi.md +++ b/README_hifi.md @@ -1,3 +1,5 @@ +# THIS DOCUMENT IS OUTDATED + High Fidelity (hifi) is an early-stage technology lab experimenting with Virtual Worlds and VR. This repository contains the source to many of the components in our @@ -15,7 +17,7 @@ Come chat with us in [our Gitter](https://gitter.im/highfidelity/hifi) if you ha Documentation ========= -Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.com), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project). +Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.com/), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project). There is also detailed [documentation on our coding standards](CODING_STANDARD.md). diff --git a/android/apps/interface/src/main/res/values/strings.xml b/android/apps/interface/src/main/res/values/strings.xml index b60caf1f2c..c1d7303f36 100644 --- a/android/apps/interface/src/main/res/values/strings.xml +++ b/android/apps/interface/src/main/res/values/strings.xml @@ -27,9 +27,9 @@ Online Sign Up SIGN UP - Creating your High Fidelity account + Creating your Vircadia account Email, username or password incorrect. - You are now signed into High Fidelity + You are now signed into Vircadia You are now logged in! Welcome Cancel diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 3e91cd05e8..529ad4b387 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -144,10 +144,10 @@ void ScriptableAvatar::update(float deltatime) { } _animationDetails.currentFrame = currentFrame; - const std::vector& modelJoints = _bind->getHFMModel().joints; + const QVector& modelJoints = _bind->getHFMModel().joints; QStringList animationJointNames = _animation->getJointNames(); - const auto nJoints = (int)modelJoints.size(); + const int nJoints = modelJoints.size(); if (_jointData.size() != nJoints) { _jointData.resize(nJoints); } diff --git a/cmake/installer/installer-header.bmp b/cmake/installer/installer-header.bmp index 2d262d3ef9..de8448ed44 100644 Binary files a/cmake/installer/installer-header.bmp and b/cmake/installer/installer-header.bmp differ diff --git a/cmake/installer/installer.ico b/cmake/installer/installer.ico index 63a3eff482..5c09071822 100644 Binary files a/cmake/installer/installer.ico and b/cmake/installer/installer.ico differ diff --git a/cmake/installer/uninstaller-header.bmp b/cmake/installer/uninstaller-header.bmp index 2d262d3ef9..de8448ed44 100644 Binary files a/cmake/installer/uninstaller-header.bmp and b/cmake/installer/uninstaller-header.bmp differ diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 83bdefeada..0442df55cf 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -18,20 +18,20 @@ macro(GENERATE_INSTALLERS) if (CLIENT_ONLY) set(_PACKAGE_NAME_EXTRA "-Interface") set(INSTALLER_TYPE "client_only") - string(REGEX REPLACE "Project Athena" "Project Athena Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION}) + string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION}) elseif (SERVER_ONLY) set(_PACKAGE_NAME_EXTRA "-Sandbox") set(INSTALLER_TYPE "server_only") - string(REGEX REPLACE "Project Athena" "Project Athena Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION}) + string(REGEX REPLACE "Vircadia" "Vircadia Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION}) else () set(_DISPLAY_NAME ${BUILD_ORGANIZATION}) set(INSTALLER_TYPE "full") endif () set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME}) - set(CPACK_PACKAGE_VENDOR "Project Athena") + set(CPACK_PACKAGE_VENDOR "Vircadia") set(CPACK_PACKAGE_VERSION ${BUILD_VERSION}) - set(CPACK_PACKAGE_FILE_NAME "ProjectAthena-Alpha${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "Vircadia-Alpha${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) if (PR_BUILD) @@ -118,11 +118,11 @@ macro(GENERATE_INSTALLERS) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") if (BUILD_CLIENT) - cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "Project Athena Interface") + cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "Vircadia Interface") endif () if (BUILD_SERVER) - cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Project Athena Sandbox") + cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Sandbox") endif () include(CPack) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 935996850b..1b7b3dbe8e 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -35,7 +35,7 @@ macro(SET_PACKAGING_PARAMETERS) set(DEPLOY_PACKAGE TRUE) set(PRODUCTION_BUILD 1) set(BUILD_VERSION ${RELEASE_NUMBER}) - set(BUILD_ORGANIZATION "Project Athena") + set(BUILD_ORGANIZATION "Vircadia") set(HIGH_FIDELITY_PROTOCOL "hifi") set(HIGH_FIDELITY_APP_PROTOCOL "hifiapp") set(INTERFACE_BUNDLE_NAME "interface") @@ -60,7 +60,7 @@ macro(SET_PACKAGING_PARAMETERS) set(DEPLOY_PACKAGE TRUE) set(PR_BUILD 1) set(BUILD_VERSION "PR${RELEASE_NUMBER}") - set(BUILD_ORGANIZATION "Project Athena - PR${RELEASE_NUMBER}") + set(BUILD_ORGANIZATION "Vircadia - PR${RELEASE_NUMBER}") set(INTERFACE_BUNDLE_NAME "interface") set(INTERFACE_ICON_PREFIX "interface-beta") @@ -69,7 +69,7 @@ macro(SET_PACKAGING_PARAMETERS) else () set(DEV_BUILD 1) set(BUILD_VERSION "dev") - set(BUILD_ORGANIZATION "Project Athena - ${BUILD_VERSION}") + set(BUILD_ORGANIZATION "Vircadia - ${BUILD_VERSION}") set(INTERFACE_BUNDLE_NAME "interface") set(INTERFACE_ICON_PREFIX "interface-beta") @@ -171,21 +171,21 @@ macro(SET_PACKAGING_PARAMETERS) # shortcut names if (PRODUCTION_BUILD) - set(INTERFACE_SHORTCUT_NAME "Project Athena") + set(INTERFACE_SHORTCUT_NAME "Vircadia") set(CONSOLE_SHORTCUT_NAME "Console") set(SANDBOX_SHORTCUT_NAME "Sandbox") set(APP_USER_MODEL_ID "com.highfidelity.console") else () - set(INTERFACE_SHORTCUT_NAME "Project Athena - ${BUILD_VERSION_NO_SHA}") + set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}") set(CONSOLE_SHORTCUT_NAME "Console - ${BUILD_VERSION_NO_SHA}") set(SANDBOX_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}") endif () set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}") - set(CONSOLE_HF_SHORTCUT_NAME "Project Athena ${CONSOLE_SHORTCUT_NAME}") - set(SANDBOX_HF_SHORTCUT_NAME "Project Athena ${SANDBOX_SHORTCUT_NAME}") + set(CONSOLE_HF_SHORTCUT_NAME "Vircadia ${CONSOLE_SHORTCUT_NAME}") + set(SANDBOX_HF_SHORTCUT_NAME "Vircadia ${SANDBOX_SHORTCUT_NAME}") - set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "Project Athena") + set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "Vircadia") set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "Server Console") # check if we need to find signtool diff --git a/cmake/macros/TargetDraco.cmake b/cmake/macros/TargetDraco.cmake index 520786d4c3..e23069d1d3 100755 --- a/cmake/macros/TargetDraco.cmake +++ b/cmake/macros/TargetDraco.cmake @@ -1,6 +1,5 @@ macro(TARGET_DRACO) set(LIBS draco dracodec dracoenc) - find_library(LIBPATH ${LIB} PATHS ) if (ANDROID) set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/draco) set(DRACO_INCLUDE_DIRS "${INSTALL_DIR}/include" CACHE STRING INTERNAL) @@ -12,6 +11,8 @@ macro(TARGET_DRACO) else() set(LIB_SEARCH_PATH_RELEASE ${VCPKG_INSTALL_ROOT}/lib/) set(LIB_SEARCH_PATH_DEBUG ${VCPKG_INSTALL_ROOT}/debug/lib/) + set(DRACO_LIBRARY_RELEASE "") + set(DRACO_LIBRARY_DEBUG "") foreach(LIB ${LIBS}) find_library(${LIB}_LIBPATH ${LIB} PATHS ${LIB_SEARCH_PATH_RELEASE} NO_DEFAULT_PATH) list(APPEND DRACO_LIBRARY_RELEASE ${${LIB}_LIBPATH}) diff --git a/cmake/macros/TargetOpenEXR.cmake b/cmake/macros/TargetOpenEXR.cmake index 8d61f216e7..9d63ba3ef4 100644 --- a/cmake/macros/TargetOpenEXR.cmake +++ b/cmake/macros/TargetOpenEXR.cmake @@ -28,6 +28,8 @@ macro(TARGET_OPENEXR) string(REGEX MATCHALL "[0-9]" OPENEXR_MINOR_VERSION ${TMP}) endif() + set(OPENEXR_LIBRARY_RELEASE "") + set(OPENEXR_LIBRARY_DEBUG "") foreach(OPENEXR_LIB IlmImf IlmImfUtil diff --git a/cmake/macros/TargetPython.cmake b/cmake/macros/TargetPython.cmake index 2c055cf8bc..c9089c17bd 100644 --- a/cmake/macros/TargetPython.cmake +++ b/cmake/macros/TargetPython.cmake @@ -19,4 +19,5 @@ macro(TARGET_PYTHON) message(FATAL_ERROR "Unable to locate Python interpreter 3.5 or higher") endif() endif() -endmacro() \ No newline at end of file + message("Using the Python interpreter located at: " ${HIFI_PYTHON_EXEC}) +endmacro() diff --git a/cmake/ports/glad/portfile.cmake b/cmake/ports/glad/portfile.cmake index 56ee59e0d4..54b1d91c89 100644 --- a/cmake/ports/glad/portfile.cmake +++ b/cmake/ports/glad/portfile.cmake @@ -1,17 +1,19 @@ include(vcpkg_common_functions) vcpkg_check_linkage(ONLY_STATIC_LIBRARY) +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + if (ANDROID) vcpkg_download_distfile( SOURCE_ARCHIVE - URLS https://athena-public.s3.amazonaws.com/dependencies/glad/glad32es.zip + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad32es.zip SHA512 2e02ac633eed8f2ba2adbf96ea85d08998f48dd2e9ec9a88ec3c25f48eaf1405371d258066327c783772fcb3793bdb82bd7375fdabb2ba5e2ce0835468b17f65 ) else() # else Linux desktop vcpkg_download_distfile( SOURCE_ARCHIVE - URLS https://athena-public.s3.amazonaws.com/dependencies/glad/glad45.zip + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/glad/glad45.zip SHA512 653a7b873f9fbc52e0ab95006cc3143bc7b6f62c6e032bc994e87669273468f37978525c9af5efe36f924cb4acd221eb664ad9af0ce4bf711b4f1be724c0065e FILENAME glad45.zip ) @@ -33,4 +35,3 @@ vcpkg_install_cmake() file(COPY ${CMAKE_CURRENT_LIST_DIR}/copyright DESTINATION ${CURRENT_PACKAGES_DIR}/share/glad) file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) - diff --git a/cmake/ports/glm/disable_warnings_as_error.patch b/cmake/ports/glm/disable_warnings_as_error.patch index f87616b1ec..5dabaa6323 100644 --- a/cmake/ports/glm/disable_warnings_as_error.patch +++ b/cmake/ports/glm/disable_warnings_as_error.patch @@ -1,7 +1,16 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 756673a3..5fbc8906 100644 +index 756673a..9b3aa07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt +@@ -194,7 +194,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message("GLM: Clang - ${CMAKE_CXX_COMPILER_ID} compiler") + endif() + +- add_compile_options(-Werror -Weverything) ++ add_compile_options(-Weverything) + add_compile_options(-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-c++11-long-long -Wno-padded -Wno-gnu-anonymous-struct -Wno-nested-anon-types) + add_compile_options(-Wno-undefined-reinterpret-cast -Wno-sign-conversion -Wno-unused-variable -Wno-missing-prototypes -Wno-unreachable-code -Wno-missing-variable-declarations -Wno-sign-compare -Wno-global-constructors -Wno-unused-macros -Wno-format-nonliteral) + @@ -216,7 +216,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") message("GLM: Visual C++ - ${CMAKE_CXX_COMPILER_ID} compiler") endif() diff --git a/cmake/ports/hifi-deps/CONTROL b/cmake/ports/hifi-deps/CONTROL index d4b4acd4c5..ff689c7a2a 100644 --- a/cmake/ports/hifi-deps/CONTROL +++ b/cmake/ports/hifi-deps/CONTROL @@ -1,4 +1,4 @@ Source: hifi-deps -Version: 0.1.4-github-actions +Version: 0.1.5-github-actions Description: Collected dependencies for High Fidelity applications Build-Depends: bullet3, draco, etc2comp, glad, glm, nvtt, openexr (!android), openssl (windows), opus, polyvox, tbb (!android), vhacd, webrtc (!android), zlib diff --git a/cmake/ports/openvr/portfile.cmake b/cmake/ports/openvr/portfile.cmake index 5e4211907d..b91bdd1d54 100644 --- a/cmake/ports/openvr/portfile.cmake +++ b/cmake/ports/openvr/portfile.cmake @@ -3,8 +3,8 @@ include(vcpkg_common_functions) vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO ValveSoftware/openvr - REF v1.0.16 - SHA512 967356563ba4232da5361510c7519d3058e09eced4571aadc00d8a75ab1f299a0aebda2b0b10b0ffb6c6a443fd718634d0c0103964e289961449c93e8d7b9d02 + REF v1.11.11 + SHA512 25bddb0e82eea091fe5101d0d3de1de7bb81b4504adc0c8d8e687d2502c0167bc5a11e68bc343d7563fb4db7c917e9d0e2ea99bc1d8016d479874b0c6bd7f121 HEAD_REF master ) diff --git a/cmake/ports/opus/portfile.cmake b/cmake/ports/opus/portfile.cmake index bf23718df8..e5518351f8 100644 --- a/cmake/ports/opus/portfile.cmake +++ b/cmake/ports/opus/portfile.cmake @@ -6,9 +6,9 @@ vcpkg_from_github( REPO xiph/opus REF - e85ed7726db5d677c9c0677298ea0cb9c65bdd23 + 72a3a6c13329869000b34a12ba27d8bfdfbc22b3 SHA512 - a8c7e5bf383c06f1fdffd44d9b5f658f31eb4800cb59d12da95ddaeb5646f7a7b03025f4663362b888b1374d4cc69154f006ba07b5840ec61ddc1a1af01d6c54 + 590b852e966a497e33d129b58bc07d1205fe8fea9b158334cd8a3c7f539332ef9702bba4a37bd0be83eb5f04a218cef87645251899f099695d01c1eb8ea6e2fd HEAD_REF master) diff --git a/cmake/ports/polyvox/portfile.cmake b/cmake/ports/polyvox/portfile.cmake index 54cc74d4dd..9204b26dbd 100644 --- a/cmake/ports/polyvox/portfile.cmake +++ b/cmake/ports/polyvox/portfile.cmake @@ -1,9 +1,11 @@ include(vcpkg_common_functions) +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + # else Linux desktop vcpkg_download_distfile( SOURCE_ARCHIVE - URLS https://athena-public.s3.amazonaws.com/dependencies/polyvox-master-2015-7-15.zip + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/polyvox-master-2015-7-15.zip SHA512 cc04cd43ae74b9c7bb065953540c0048053fcba6b52dc4218b3d9431fba178d65ad4f6c53cc1122ba61d0ab4061e99a7ebbb15db80011d607c5070ebebf8eddc FILENAME polyvox.zip ) @@ -23,33 +25,45 @@ vcpkg_install_cmake() file(INSTALL ${SOURCE_PATH}/LICENSE.TXT DESTINATION ${CURRENT_PACKAGES_DIR}/share/polyvox RENAME copyright) file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/include) -file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/lib) -file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/lib) +if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/lib) +endif() +if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/lib) +endif() if(WIN32) - file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxCore/lib/Release/PolyVoxCore.lib ${CURRENT_PACKAGES_DIR}/lib/PolyVoxCore.lib) - file(RENAME ${CURRENT_PACKAGES_DIR}/debug/PolyVoxCore/lib/Debug/PolyVoxCore.lib ${CURRENT_PACKAGES_DIR}/debug/lib/PolyVoxCore.lib) - file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxUtil/lib/PolyVoxUtil.lib ${CURRENT_PACKAGES_DIR}/lib/PolyVoxUtil.lib) - file(RENAME ${CURRENT_PACKAGES_DIR}/debug/PolyVoxUtil/lib/PolyVoxUtil.lib ${CURRENT_PACKAGES_DIR}/debug/lib/PolyVoxUtil.lib) + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxCore/lib/Release/PolyVoxCore.lib ${CURRENT_PACKAGES_DIR}/lib/PolyVoxCore.lib) + file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxUtil/lib/PolyVoxUtil.lib ${CURRENT_PACKAGES_DIR}/lib/PolyVoxUtil.lib) + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/PolyVoxCore/lib/Debug/PolyVoxCore.lib ${CURRENT_PACKAGES_DIR}/debug/lib/PolyVoxCore.lib) + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/PolyVoxUtil/lib/PolyVoxUtil.lib ${CURRENT_PACKAGES_DIR}/debug/lib/PolyVoxUtil.lib) + endif() file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxCore/include/PolyVoxCore ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore) file(RENAME ${CURRENT_PACKAGES_DIR}/PolyVoxUtil/include/PolyVoxUtil ${CURRENT_PACKAGES_DIR}/include/PolyVoxUtil) file(RENAME ${CURRENT_PACKAGES_DIR}/cmake/PolyVoxConfig.cmake ${CURRENT_PACKAGES_DIR}/share/polyvox/polyvoxConfig.cmake) file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/cmake) - file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/cmake) file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/PolyVoxCore) file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/PolyVoxUtil) - file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/PolyVoxUtil) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/cmake) file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/PolyVoxCore) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/PolyVoxUtil) else() - file(GLOB LIBS ${CURRENT_PACKAGES_DIR}/lib/Release/*) - foreach(_lib ${LIBS}) - file(RELATIVE_PATH _libName ${CURRENT_PACKAGES_DIR}/lib/Release ${_lib}) - file(RENAME ${_lib} ${CURRENT_PACKAGES_DIR}/lib/${_libName}) - endforeach() - file(GLOB LIBS ${CURRENT_PACKAGES_DIR}/debug/lib/Debug/*) - foreach(_lib ${LIBS}) - file(RELATIVE_PATH _libName ${CURRENT_PACKAGES_DIR}/debug/lib/Debug ${_lib}) - file(RENAME ${_lib} ${CURRENT_PACKAGES_DIR}/debug/lib/${_libName}) - endforeach() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(GLOB LIBS ${CURRENT_PACKAGES_DIR}/lib/Release/*) + foreach(_lib ${LIBS}) + file(RELATIVE_PATH _libName ${CURRENT_PACKAGES_DIR}/lib/Release ${_lib}) + file(RENAME ${_lib} ${CURRENT_PACKAGES_DIR}/lib/${_libName}) + endforeach() + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(GLOB LIBS ${CURRENT_PACKAGES_DIR}/debug/lib/Debug/*) + foreach(_lib ${LIBS}) + file(RELATIVE_PATH _libName ${CURRENT_PACKAGES_DIR}/debug/lib/Debug ${_lib}) + file(RENAME ${_lib} ${CURRENT_PACKAGES_DIR}/debug/lib/${_libName}) + endforeach() + endif() file(RENAME ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore.temp) file(RENAME ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore.temp/PolyVoxCore ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore) file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/include/PolyVoxCore.temp) diff --git a/cmake/ports/quazip/portfile.cmake b/cmake/ports/quazip/portfile.cmake index 07a8ebc3c3..0789062892 100644 --- a/cmake/ports/quazip/portfile.cmake +++ b/cmake/ports/quazip/portfile.cmake @@ -1,10 +1,11 @@ include(vcpkg_common_functions) file(READ "${VCPKG_ROOT_DIR}/_env/QT_CMAKE_PREFIX_PATH.txt" QT_CMAKE_PREFIX_PATH) +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) vcpkg_download_distfile( SOURCE_ARCHIVE - URLS https://athena-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/quazip-0.7.3.zip SHA512 b2d812b6346317fd6d8f4f1344ad48b721d697c429acc8b7e7cb776ce5cba15a59efd64b2c5ae1f31b5a3c928014f084aa1379fd55d8a452a6cf4fd510b3afcc FILENAME quazip.zip ) @@ -24,10 +25,14 @@ vcpkg_configure_cmake( vcpkg_install_cmake() if (WIN32) - file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/bin) - file(RENAME ${CURRENT_PACKAGES_DIR}/lib/quazip5.dll ${CURRENT_PACKAGES_DIR}/bin/quazip5.dll) - file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/bin) - file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/quazip5d.dll ${CURRENT_PACKAGES_DIR}/debug/bin/quazip5.dll) + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/bin) + file(RENAME ${CURRENT_PACKAGES_DIR}/lib/quazip5.dll ${CURRENT_PACKAGES_DIR}/bin/quazip5.dll) + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/bin) + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/quazip5d.dll ${CURRENT_PACKAGES_DIR}/debug/bin/quazip5.dll) + endif() elseif(DEFINED VCPKG_TARGET_IS_LINUX) # We only want static libs. file(GLOB QUAZIP5_DYNAMIC_LIBS ${CURRENT_PACKAGES_DIR}/lib/libquazip5.so* ${CURRENT_PACKAGES_DIR}/debug/lib/libquazip5d.so*) diff --git a/cmake/ports/tbb/CONTROL b/cmake/ports/tbb/CONTROL index da29e48794..73569fe661 100644 --- a/cmake/ports/tbb/CONTROL +++ b/cmake/ports/tbb/CONTROL @@ -1,4 +1,4 @@ Source: tbb Version: 2019_U8-1 -Homepage: https://github.com/01org/tbb +Homepage: https://github.com/oneapi-src/oneTBB Description: Intel's Threading Building Blocks. diff --git a/cmake/ports/vhacd/portfile.cmake b/cmake/ports/vhacd/portfile.cmake index 02d90cab18..4c74bb6532 100644 --- a/cmake/ports/vhacd/portfile.cmake +++ b/cmake/ports/vhacd/portfile.cmake @@ -1,10 +1,12 @@ include(vcpkg_common_functions) vcpkg_check_linkage(ONLY_STATIC_LIBRARY) +file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS) + # else Linux desktop vcpkg_download_distfile( SOURCE_ARCHIVE - URLS https://athena-public.s3.amazonaws.com/dependencies/v-hacd-master.zip + URLS ${EXTERNAL_BUILD_ASSETS}/dependencies/v-hacd-master.zip SHA512 5d9bd4872ead9eb3574e4806d6c4f490353a04036fd5f571e1e44f47cb66b709e311abcd53af30bae0015a690152170aeed93209a626c28ebcfd6591f3bb036f FILENAME vhacd.zip ) @@ -22,10 +24,17 @@ vcpkg_configure_cmake( vcpkg_install_cmake() file(COPY ${CMAKE_CURRENT_LIST_DIR}/copyright DESTINATION ${CURRENT_PACKAGES_DIR}/share/vhacd) -file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +endif() + if (WIN32) - file(RENAME ${CURRENT_PACKAGES_DIR}/lib/Release/VHACD_LIB.lib ${CURRENT_PACKAGES_DIR}/lib/VHACD.lib) - file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/Release) - file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/Debug/VHACD_LIB.lib ${CURRENT_PACKAGES_DIR}/debug/lib/VHACD.lib) - file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/Debug) -endif() \ No newline at end of file + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(RENAME ${CURRENT_PACKAGES_DIR}/lib/Release/VHACD_LIB.lib ${CURRENT_PACKAGES_DIR}/lib/VHACD.lib) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/Release) + endif() + if (NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/Debug/VHACD_LIB.lib ${CURRENT_PACKAGES_DIR}/debug/lib/VHACD.lib) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/Debug) + endif() +endif() diff --git a/cmake/ports/zlib/CONTROL b/cmake/ports/zlib/CONTROL index aa7c7b6e92..601fb1bc0e 100644 --- a/cmake/ports/zlib/CONTROL +++ b/cmake/ports/zlib/CONTROL @@ -1,4 +1,4 @@ Source: zlib -Version: 1.2.11-5 +Version: 1.2.11-6 Homepage: https://www.zlib.net/ Description: A compression library diff --git a/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch b/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch index 229a2d055b..a374f76d62 100644 --- a/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch +++ b/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch @@ -1,5 +1,5 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 0fe939d..8d2f5f1 100644 +index 0fe939d..a1291d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(VERSION "1.2.11") @@ -10,24 +10,56 @@ index 0fe939d..8d2f5f1 100644 set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables") set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries") -@@ -211,7 +212,15 @@ elseif(BUILD_SHARED_LIBS AND WIN32) +@@ -124,9 +125,11 @@ set(ZLIB_SRCS + ) + + if(NOT MINGW) +- set(ZLIB_DLL_SRCS +- win32/zlib1.rc # If present will override custom build rule below. +- ) ++ if(BUILD_SHARED_LIBS) ++ set(ZLIB_DLL_SRCS ++ win32/zlib1.rc # If present will override custom build rule below. ++ ) ++ endif() + endif() + + if(CMAKE_COMPILER_IS_GNUCC) +@@ -180,11 +183,12 @@ if(MINGW) + -I ${CMAKE_CURRENT_BINARY_DIR} + -o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj + -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc) +- set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) ++ if(BUILD_SHARED_LIBS) ++ set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) ++ endif() + endif(MINGW) + +-add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) +-add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) ++add_library(zlib ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) + set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) + set_target_properties(zlib PROPERTIES SOVERSION 1) + +@@ -201,7 +205,7 @@ endif() + + if(UNIX) + # On unix-like platforms the library is almost always called libz +- set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z) ++ set_target_properties(zlib PROPERTIES OUTPUT_NAME z) + if(NOT APPLE) + set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"") + endif() +@@ -211,7 +215,7 @@ elseif(BUILD_SHARED_LIBS AND WIN32) endif() if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) - install(TARGETS zlib zlibstatic -+ if (BUILD_SHARED_LIBS) -+ set(ZLIB_TARGETS zlib) -+ set_target_properties(zlibstatic PROPERTIES EXCLUDE_FROM_ALL ON) -+ else() -+ set(ZLIB_TARGETS zlibstatic) -+ set_target_properties(zlib PROPERTIES EXCLUDE_FROM_ALL ON) -+ endif() -+ -+ install(TARGETS ${ZLIB_TARGETS} ++ install(TARGETS zlib RUNTIME DESTINATION "${INSTALL_BIN_DIR}" ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ) -@@ -230,6 +239,7 @@ endif() +@@ -230,6 +234,7 @@ endif() # Example binaries #============================================================================ @@ -35,7 +67,7 @@ index 0fe939d..8d2f5f1 100644 add_executable(example test/example.c) target_link_libraries(example zlib) add_test(example example) -@@ -247,3 +257,4 @@ if(HAVE_OFF64_T) +@@ -247,3 +252,4 @@ if(HAVE_OFF64_T) target_link_libraries(minigzip64 zlib) set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") endif() diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0e4c2f3579..8e621b8f2d 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -720,7 +720,7 @@ Function InstallTypesPage StrCpy $OffsetUnits u StrCpy $Express "0" - ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Project Athena Interface and Project Athena Sandbox" + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface and Vircadia Sandbox" pop $ExpressInstallRadioButton ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel IntOp $CurrentOffset $CurrentOffset + 15 @@ -973,7 +973,7 @@ Function ReadPostInstallOptions ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${LogText} "Option: Install Client" - ; check if the user asked for a desktop shortcut to Project Athena + ; check if the user asked for a desktop shortcut to Vircadia ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState ${LogText} "Option: Create Client Desktop Shortcut: $DesktopClientState" ${EndIf} @@ -1027,7 +1027,7 @@ Function HandlePostInstallOptions ${EndIf} ${If} @CLIENT_COMPONENT_CONDITIONAL@ - ; check if the user asked for a desktop shortcut to Project Athena + ; check if the user asked for a desktop shortcut to Vircadia ${If} $DesktopClientState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" !insertmacro WriteInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES @@ -1088,7 +1088,7 @@ Function HandlePostInstallOptions ClearErrors ; copy the data from production build to this PR build - CopyFiles "$APPDATA\Project Athena\*" $0 + CopyFiles "$APPDATA\Vircadia\*" $0 ; handle an error in copying files IfErrors 0 NoError diff --git a/domain-server/resources/web/wizard/index.shtml b/domain-server/resources/web/wizard/index.shtml index 3bc7503b44..37f9d9e813 100644 --- a/domain-server/resources/web/wizard/index.shtml +++ b/domain-server/resources/web/wizard/index.shtml @@ -24,7 +24,7 @@
- Place names are similar to web addresses. Users who want to visit your domain can + Place names are similar to web addresses. Users who want to visit your domain can enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.
Your domain may also be reachable by IP address.
diff --git a/hifi_qt.py b/hifi_qt.py index 6cae3bf59d..10708e4bc9 100644 --- a/hifi_qt.py +++ b/hifi_qt.py @@ -29,7 +29,7 @@ endif() self.configFilePath = os.path.join(args.build_root, 'qt.cmake') self.version = os.getenv('VIRCADIA_USE_QT_VERSION', '5.12.3') - self.assets_url = self.readVar('EXTERNAL_BUILD_ASSETS') + self.assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS') defaultBasePath = os.path.expanduser('~/hifi/qt') self.basePath = os.getenv('HIFI_QT_BASE', defaultBasePath) @@ -74,10 +74,10 @@ endif() 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): print("We don't support " + distro.name(pretty=True) + " yet. Perhaps consider helping us out?") - raise Exception('UNSUPPORTED LINUX VERSION!!!') + raise Exception('LINUX DISTRO IS NOT SUPPORTED YET!!!') else: print("Sorry, " + distro.name(pretty=True) + " is old and won't be officially supported. Please consider upgrading."); - raise Exception('UNSUPPORTED LINUX VERSION!!!') + raise Exception('UNKNOWN LINUX DISTRO VERSION!!!') 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:") @@ -89,10 +89,6 @@ endif() print("Machine : " + platform.machine()) raise Exception('UNKNOWN OPERATING SYSTEM!!!') - def readVar(self, var): - with open(os.path.join(self.args.build_root, '_env', var + ".txt")) as fp: - return fp.read() - def writeConfig(self): print("Writing cmake config to {}".format(self.configFilePath)) # Write out the configuration for use by CMake diff --git a/hifi_utils.py b/hifi_utils.py index 3a49f6d52b..157e5858a8 100644 --- a/hifi_utils.py +++ b/hifi_utils.py @@ -121,3 +121,7 @@ def downloadAndExtract(url, destPath, hash=None, hasher=hashlib.sha512(), isZip= with tarfile.open(tempFileName, 'r:*') as tgz: tgz.extractall(destPath) os.remove(tempFileName) + +def readEnviromentVariableFromFile(buildRootDir, var): + with open(os.path.join(buildRootDir, '_env', var + ".txt")) as fp: + return fp.read() diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py index 9846d386c4..1b9976da6e 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -21,6 +21,7 @@ get_filename_component(CMAKE_TOOLCHAIN_FILE "{}" ABSOLUTE CACHE) get_filename_component(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}" ABSOLUTE) set(VCPKG_INSTALL_ROOT "{}") set(VCPKG_TOOLS_DIR "{}") +set(VCPKG_TARGET_TRIPLET "{}") """ CMAKE_TEMPLATE_NON_ANDROID = """ @@ -34,7 +35,11 @@ endif() self.args = args # our custom ports, relative to the script location self.sourcePortsPath = args.ports_path - self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] + self.vcpkgBuildType = args.vcpkg_build_type + if (self.vcpkgBuildType): + self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] + "-" + self.vcpkgBuildType + else: + self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] self.configFilePath = os.path.join(args.build_root, 'vcpkg.cmake') self.assets_url = self.readVar('EXTERNAL_BUILD_ASSETS') @@ -78,27 +83,31 @@ endif() self.bootstrapEnv = os.environ.copy() self.buildEnv = os.environ.copy() self.prebuiltArchive = None - usePrebuilt = ('CI_BUILD' in os.environ) and os.environ["CI_BUILD"] == "Github" and (not self.noClean) + usePrebuilt = False + # usePrebuild Disabled, to re-enabled using the prebuilt archives for GitHub action builds uncomment the following line: + # usePrebuilt = ('CI_BUILD' in os.environ) and os.environ["CI_BUILD"] == "Github" and (not self.noClean) if 'Windows' == system: self.exe = os.path.join(self.path, 'vcpkg.exe') - self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.bat') ] + self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.bat'), '-disableMetrics' ] self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/builds/vcpkg-win32-client.zip%3FversionId=tSFzbw01VkkVFeRQ6YuAY4dro2HxJR9U' self.vcpkgHash = 'a650db47a63ccdc9904b68ddd16af74772e7e78170b513ea8de5a3b47d032751a3b73dcc7526d88bcb500753ea3dd9880639ca842bb176e2bddb1710f9a58cd3' self.hostTriplet = 'x64-windows' if usePrebuilt: - self.prebuiltArchive = self.assets_url + "/dependencies/vcpkg/builds/vcpkg-win32.zip%3FversionId=3SF3mDC8dkQH1JP041m88xnYmWNzZflx" + self.prebuiltArchive = self.assets_url + "/dependencies/vcpkg/builds/vcpkg-win32.zip%3FversionId=3SF3mDC8dkQH1JP041m88xnYmWNzZflx" elif 'Darwin' == system: self.exe = os.path.join(self.path, 'vcpkg') - self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '--allowAppleClang' ] + self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '--allowAppleClang', '-disableMetrics' ] self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/builds/vcpkg-osx-client.tgz%3FversionId=j0b4azo_zTlH_Q9DElEWOz1UMYZ2nqQw' self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d' self.hostTriplet = 'x64-osx' + # Potential fix for a vcpkg build issue on OSX (see https://github.com/microsoft/vcpkg/issues/9029) + self.bootstrapEnv['CXXFLAGS'] = '-D_CTERMID_H_' if usePrebuilt: self.prebuiltArchive = self.assets_url + "/dependencies/vcpkg/builds/vcpkg-osx.tgz%3FversionId=6JrIMTdvpBF3MAsjA92BMkO79Psjzs6Z" else: self.exe = os.path.join(self.path, 'vcpkg') - self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh') ] + self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.sh'), '-disableMetrics' ] self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/builds/vcpkg-linux-client.tgz%3FversionId=y7mct0gFicEXz5hJy3KROBugcLR56YWf' self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d' self.hostTriplet = 'x64-linux' @@ -206,6 +215,19 @@ endif() print(actualCommands) hifi_utils.executeSubprocess(actualCommands, folder=self.path, env=self.buildEnv) + def copyTripletForBuildType(self, triplet): + print('Copying triplet ' + triplet + ' to have build type ' + self.vcpkgBuildType) + tripletPath = os.path.join(self.path, 'triplets', triplet + '.cmake') + tripletForBuildTypePath = os.path.join(self.path, 'triplets', self.getTripletWithBuildType(triplet) + '.cmake') + shutil.copy(tripletPath, tripletForBuildTypePath) + with open(tripletForBuildTypePath, "a") as tripletForBuildTypeFile: + tripletForBuildTypeFile.write("set(VCPKG_BUILD_TYPE " + self.vcpkgBuildType + ")\n") + + def getTripletWithBuildType(self, triplet): + if (not self.vcpkgBuildType): + return triplet + return triplet + '-' + self.vcpkgBuildType + def setupDependencies(self, qt=None): if self.prebuiltArchive: if not os.path.isfile(self.prebuildTagFile): @@ -224,12 +246,16 @@ endif() self.setupAndroidDependencies() print("Installing host tools") - self.run(['install', '--triplet', self.hostTriplet, 'hifi-host-tools']) + if (self.vcpkgBuildType): + self.copyTripletForBuildType(self.hostTriplet) + self.run(['install', '--triplet', self.getTripletWithBuildType(self.hostTriplet), 'hifi-host-tools']) # If not android, install the hifi-client-deps libraries if not self.args.android: print("Installing build dependencies") - self.run(['install', '--triplet', self.triplet, 'hifi-client-deps']) + if (self.vcpkgBuildType): + self.copyTripletForBuildType(self.triplet) + self.run(['install', '--triplet', self.getTripletWithBuildType(self.triplet), 'hifi-client-deps']) def cleanBuilds(self): if self.noClean: @@ -240,6 +266,12 @@ endif() print("Wiping build trees") shutil.rmtree(builddir, ignore_errors=True) + # Removes large files used to build the vcpkg, for CI purposes. + def cleanupDevelopmentFiles(self): + shutil.rmtree(os.path.join(self.path, "downloads"), ignore_errors=True) + shutil.rmtree(os.path.join(self.path, "packages"), ignore_errors=True) + + def setupAndroidDependencies(self): # vcpkg prebuilt if not os.path.isdir(os.path.join(self.path, 'installed', 'arm64-android')): @@ -276,12 +308,32 @@ endif() with open(self.prebuildTagFile, 'w') as f: f.write(self.tagContents) + def fixupCmakeScript(self): + cmakeScript = os.path.join(self.path, 'scripts/buildsystems/vcpkg.cmake') + newCmakeScript = cmakeScript + '.new' + isFileChanged = False + removalPrefix = "set(VCPKG_TARGET_TRIPLET " + # Open original file in read only mode and dummy file in write mode + with open(cmakeScript, 'r') as read_obj, open(newCmakeScript, 'w') as write_obj: + # Line by line copy data from original file to dummy file + for line in read_obj: + if not line.startswith(removalPrefix): + write_obj.write(line) + else: + isFileChanged = True + + if isFileChanged: + shutil.move(newCmakeScript, cmakeScript) + else: + os.remove(newCmakeScript) + + def writeConfig(self): print("Writing cmake config to {}".format(self.configFilePath)) # Write out the configuration for use by CMake cmakeScript = os.path.join(self.path, 'scripts/buildsystems/vcpkg.cmake') - installPath = os.path.join(self.path, 'installed', self.triplet) - toolsPath = os.path.join(self.path, 'installed', self.hostTriplet, 'tools') + installPath = os.path.join(self.path, 'installed', self.getTripletWithBuildType(self.triplet)) + toolsPath = os.path.join(self.path, 'installed', self.getTripletWithBuildType(self.hostTriplet), 'tools') cmakeTemplate = VcpkgRepo.CMAKE_TEMPLATE if self.args.android: @@ -289,7 +341,7 @@ endif() cmakeTemplate += 'set(HIFI_ANDROID_PRECOMPILED "{}")\n'.format(precompiled) else: cmakeTemplate += VcpkgRepo.CMAKE_TEMPLATE_NON_ANDROID - cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath).replace('\\', '/') + cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath, self.getTripletWithBuildType(self.hostTriplet)).replace('\\', '/') with open(self.configFilePath, 'w') as f: f.write(cmakeConfig) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index dc9e5254df..1a0e279674 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -159,7 +159,7 @@ elseif (WIN32) set(CONFIGURE_ICON_RC_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Icon.rc") configure_file("${HF_CMAKE_DIR}/templates/Icon.rc.in" ${CONFIGURE_ICON_RC_OUTPUT}) - set(APP_FULL_NAME "Project Athena") + set(APP_FULL_NAME "Vircadia") set(CONFIGURE_VERSION_INFO_RC_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.rc") configure_file("${HF_CMAKE_DIR}/templates/VersionInfo.rc.in" ${CONFIGURE_VERSION_INFO_RC_OUTPUT}) diff --git a/interface/icon/interface-beta.icns b/interface/icon/interface-beta.icns index 1fe162f88e..9044dbfbf7 100644 Binary files a/interface/icon/interface-beta.icns and b/interface/icon/interface-beta.icns differ diff --git a/interface/icon/interface-beta.ico b/interface/icon/interface-beta.ico index 63a3eff482..5c279fd5c1 100644 Binary files a/interface/icon/interface-beta.ico and b/interface/icon/interface-beta.ico differ diff --git a/interface/icon/interface.icns b/interface/icon/interface.icns index 1fe162f88e..4e4df39d69 100644 Binary files a/interface/icon/interface.icns and b/interface/icon/interface.icns differ diff --git a/interface/icon/interface.ico b/interface/icon/interface.ico index 63a3eff482..d0d5c4ac96 100644 Binary files a/interface/icon/interface.ico and b/interface/icon/interface.ico differ diff --git a/interface/resources/html/commerce/backup_instructions.html b/interface/resources/html/commerce/backup_instructions.html index 4056b81896..034844e705 100644 --- a/interface/resources/html/commerce/backup_instructions.html +++ b/interface/resources/html/commerce/backup_instructions.html @@ -596,7 +596,7 @@

Want to learn more?

You can find out much more about the blockchain and about commerce in High Fidelity by visiting our Docs site:

-

Visit High Fidelity's Docs

+

Visit High Fidelity's Docs


diff --git a/interface/resources/html/img/tablet-help-gamepad.jpg b/interface/resources/html/img/tablet-help-gamepad.jpg index 3f246e5cc0..49fd99b141 100644 Binary files a/interface/resources/html/img/tablet-help-gamepad.jpg and b/interface/resources/html/img/tablet-help-gamepad.jpg differ diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index ec4c0ec60d..7e706187f7 100644 Binary files a/interface/resources/html/img/tablet-help-keyboard.jpg and b/interface/resources/html/img/tablet-help-keyboard.jpg differ diff --git a/interface/resources/html/img/tablet-help-oculus.jpg b/interface/resources/html/img/tablet-help-oculus.jpg index 64b57aaccb..5ebc66b75f 100644 Binary files a/interface/resources/html/img/tablet-help-oculus.jpg and b/interface/resources/html/img/tablet-help-oculus.jpg differ diff --git a/interface/resources/html/img/tablet-help-vive.jpg b/interface/resources/html/img/tablet-help-vive.jpg index 75ccf601e2..c9a5e0da7a 100644 Binary files a/interface/resources/html/img/tablet-help-vive.jpg and b/interface/resources/html/img/tablet-help-vive.jpg differ diff --git a/interface/resources/html/img/tablet-help-windowsMR.jpg b/interface/resources/html/img/tablet-help-windowsMR.jpg index 848e3deff6..2db23a0b89 100644 Binary files a/interface/resources/html/img/tablet-help-windowsMR.jpg and b/interface/resources/html/img/tablet-help-windowsMR.jpg differ diff --git a/interface/resources/html/tabletHelp.html b/interface/resources/html/tabletHelp.html index a414417239..17b41a1979 100644 --- a/interface/resources/html/tabletHelp.html +++ b/interface/resources/html/tabletHelp.html @@ -77,9 +77,9 @@ var handControllerImageURL = null; var index = 0; var count = 3; - var handControllerRefURL = "https://docs.projectathena.dev/explore/get-started/vr-controls.html#vr-controls"; - var keyboardRefURL = "https://docs.projectathena.dev/explore/get-started/desktop.html#movement-controls"; - var gamepadRefURL = "https://docs.projectathena.dev/explore/get-started/vr-controls.html#gamepad"; + var handControllerRefURL = "https://docs.vircadia.dev/explore/get-started/vr-controls.html#vr-controls"; + var keyboardRefURL = "https://docs.vircadia.dev/explore/get-started/desktop.html#movement-controls"; + var gamepadRefURL = "https://docs.vircadia.dev/explore/get-started/vr-controls.html#gamepad"; function showKbm() { document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg"); @@ -189,7 +189,7 @@
- Report Problem + Report Problem diff --git a/interface/resources/images/about-vircadia.png b/interface/resources/images/about-vircadia.png new file mode 100644 index 0000000000..14ae73b226 Binary files /dev/null and b/interface/resources/images/about-vircadia.png differ diff --git a/interface/resources/images/vircadia-logo.svg b/interface/resources/images/vircadia-logo.svg new file mode 100644 index 0000000000..b53dd79040 --- /dev/null +++ b/interface/resources/images/vircadia-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml index 7f2136ec4f..fcf4b39a2e 100644 --- a/interface/resources/qml/+webengine/BrowserWebView.qml +++ b/interface/resources/qml/+webengine/BrowserWebView.qml @@ -6,7 +6,7 @@ import controlsUit 1.0 WebView { id: webview - url: "https://projectathena.io/" + url: "https://vircadia.com/" profile: FileTypeProfile; property var parentRoot: null diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index fbc8f9495a..375d68ad26 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -85,7 +85,9 @@ FocusScope { Image { id: banner anchors.centerIn: parent - source: "../images/project-athena-banner-color2.svg" + sourceSize.width: 500 + sourceSize.height: 91 + source: "../images/vircadia-logo.svg" horizontalAlignment: Image.AlignHCenter } } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 526d1da665..4f0ea2e607 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -402,7 +402,7 @@ Item { font.pixelSize: linkAccountBody.textFieldFontSize font.bold: linkAccountBody.fontBold - text: " Can't access your account?" + text: " Can't access your account?" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -527,7 +527,7 @@ Item { leftMargin: hifi.dimensions.contentSpacing.x } - text: "Sign Up" + text: "Sign Up" linkColor: hifi.colors.blueAccent onLinkActivated: { diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index 7347464f4e..fcb47c3534 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -23,7 +23,7 @@ Item { clip: true height: root.height width: root.width - readonly property string termsContainerText: qsTr("By signing up, you agree to Project Athena's Terms of Service") + readonly property string termsContainerText: qsTr("By signing up, you agree to Vircadia's Terms of Service") property int textFieldHeight: 31 property string fontFamily: "Raleway" property int fontSize: 15 @@ -395,7 +395,7 @@ Item { text: signUpBody.termsContainerText Component.onCompleted: { // with the link. - termsText.text = qsTr("By signing up, you agree to Project Athena's Terms of Service") + termsText.text = qsTr("By signing up, you agree to Vircadia's Terms of Service") } } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 8b3c878d46..9710723bed 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -19,7 +19,7 @@ import TabletScriptingInterface 1.0 Item { id: usernameCollisionBody clip: true - readonly property string termsContainerText: qsTr("By creating this user profile, you agree to Project Athena's Terms of Service") + readonly property string termsContainerText: qsTr("By creating this user profile, you agree to Vircadia's Terms of Service") width: root.width height: root.height readonly property string fontFamily: "Raleway" @@ -218,7 +218,7 @@ Item { text: usernameCollisionBody.termsContainerText Component.onCompleted: { // with the link. - termsText.text = qsTr("By creating this user profile, you agree to Project Athena's Terms of Service") + termsText.text = qsTr("By creating this user profile, you agree to Vircadia's Terms of Service") } } diff --git a/interface/resources/qml/OverlayLoginDialog.qml b/interface/resources/qml/OverlayLoginDialog.qml index 0ad2c57e5f..466585f045 100644 --- a/interface/resources/qml/OverlayLoginDialog.qml +++ b/interface/resources/qml/OverlayLoginDialog.qml @@ -81,7 +81,9 @@ FocusScope { Image { id: banner anchors.centerIn: parent - source: "../images/high-fidelity-banner.svg" + sourceSize.width: 500 + sourceSize.height: 91 + source: "../images/vircadia-logo.svg" horizontalAlignment: Image.AlignHCenter } } diff --git a/interface/resources/qml/controlsUit/SpinBox.qml b/interface/resources/qml/controlsUit/SpinBox.qml index 564157efb0..a888bbd07c 100644 --- a/interface/resources/qml/controlsUit/SpinBox.qml +++ b/interface/resources/qml/controlsUit/SpinBox.qml @@ -109,7 +109,7 @@ SpinBox { id: spinboxText z: 2 color: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray) + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight @@ -130,7 +130,7 @@ SpinBox { } color: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.faintGray) + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) text: suffix verticalAlignment: Qt.AlignVCenter diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index a7e5c236ca..533fd1197c 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -129,7 +129,9 @@ FocusScope { Image { id: banner anchors.centerIn: parent - source: "../../images/project-athena-banner-color2.svg" + sourceSize.width: 400 + sourceSize.height: 73 + source: "../../images/vircadia-logo.svg" horizontalAlignment: Image.AlignHCenter } } diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml index 9158e25f75..00d8a29561 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml @@ -229,7 +229,7 @@ Item { } function openDocs() { - Qt.openUrlExternally("https://docs.projectathena.dev/create/avatars/package-avatar.html"); + Qt.openUrlExternally("https://docs.vircadia.dev/create/avatars/package-avatar.html"); } function openVideo() { diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml index a0c98b0821..9b59303a7e 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml @@ -318,7 +318,7 @@ Item { text: "This item is not for sale yet, learn more." onLinkActivated: { - Qt.openUrlExternally("https://docs.projectathena.dev/sell/add-item/upload-avatar.html"); + Qt.openUrlExternally("https://docs.vircadia.dev/sell/add-item/upload-avatar.html"); } } diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 15a20b491a..dfee6e60f7 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -403,7 +403,6 @@ Rectangle { Vector3 { id: positionVector - backgroundColor: "lightgray" enabled: getCurrentWearable() !== null function set(localPosition) { @@ -463,7 +462,6 @@ Rectangle { Vector3 { id: rotationVector - backgroundColor: "lightgray" enabled: getCurrentWearable() !== null function set(localRotationAngles) { @@ -550,7 +548,7 @@ Rectangle { realFrom: 0.1 realTo: 3.0 realValue: 1.0 - backgroundColor: "lightgray" + backgroundColor: activeFocus ? "white" : "lightgray" width: positionVector.spinboxWidth colorScheme: hifi.colorSchemes.light diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index ac09f14d9e..62ceee1b63 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -7,7 +7,7 @@ MessageBox { popup.onButton2Clicked = callback; popup.titleText = 'Specify Avatar URL' popup.bodyText = 'This will not overwrite your existing favorite if you are wearing one.
' + - '' + + '' + 'Learn to make a custom avatar by opening this link on your desktop.' + '' popup.inputText.visible = true; diff --git a/interface/resources/qml/hifi/avatarapp/Vector3.qml b/interface/resources/qml/hifi/avatarapp/Vector3.qml index 698123104f..54f8d087e5 100644 --- a/interface/resources/qml/hifi/avatarapp/Vector3.qml +++ b/interface/resources/qml/hifi/avatarapp/Vector3.qml @@ -29,7 +29,7 @@ Row { id: xspinner width: parent.spinboxWidth labelInside: "X:" - backgroundColor: parent.backgroundColor + backgroundColor: activeFocus ? "white" : "lightgray" colorLabelInside: hifi.colors.redHighlight colorScheme: hifi.colorSchemes.light decimals: root.decimals; @@ -43,7 +43,7 @@ Row { id: yspinner width: parent.spinboxWidth labelInside: "Y:" - backgroundColor: parent.backgroundColor + backgroundColor: activeFocus ? "white" : "lightgray" colorLabelInside: hifi.colors.greenHighlight colorScheme: hifi.colorSchemes.light decimals: root.decimals; @@ -57,7 +57,7 @@ Row { id: zspinner width: parent.spinboxWidth labelInside: "Z:" - backgroundColor: parent.backgroundColor + backgroundColor: activeFocus ? "white" : "lightgray" colorLabelInside: hifi.colors.primaryHighlight colorScheme: hifi.colorSchemes.light decimals: root.decimals; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b1b367f2b2..424b4d616d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -778,7 +778,7 @@ Rectangle { lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + "For more information about backing up and restoring content, " + - "" + + "" + "click here to open info on your desktop browser."; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 66113985e8..8ccfceb03c 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -602,7 +602,7 @@ Rectangle { lightboxPopup.bodyText = "Rezzing this content set will replace the existing environment and all of the items in this domain. " + "If you want to save the state of the content in this domain, create a backup before proceeding.

" + "For more information about backing up and restoring content, " + - "
" + + "" + "click here to open info on your desktop browser."; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index af1e1b5ab9..dbdefc4c79 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -207,7 +207,7 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta if (link === "#privateKeyPath") { Qt.openUrlExternally("file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/'))); } else if (link === "#blockchain") { - Qt.openUrlExternally("https://docs.projectathena.dev/explore/shop.html"); + Qt.openUrlExternally("https://docs.vircadia.dev/explore/shop.html"); } else if (link === "#bank") { if ((Account.metaverseServerURL).toString().indexOf("staging") >= 0) { Qt.openUrlExternally("hifi://hifiqa-master-metaverse-staging"); // So that we can test in staging. diff --git a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml index 2be66442ce..cbaa5c7b08 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml @@ -23,9 +23,9 @@ Rectangle { spacing: 5 Image { - sourceSize.width: 295 - sourceSize.height: 75 - source: "../../../images/about-projectathena.png" + width: 400; height: 73 + fillMode: Image.PreserveAspectFit + source: "../../../images/vircadia-logo.svg" } Item { height: 30; width: 1 } Column { @@ -53,7 +53,7 @@ Rectangle { textFormat: Text.StyledText linkColor: "#00B4EF" color: "white" - text: "Project Athena Github." + text: "Vircadia Github." size: 20 onLinkActivated: { HiFiAbout.openUrl("https:/github.com/kasenvr/project-athena"); @@ -116,7 +116,7 @@ Rectangle { Item { height: 20; width: 1 } RalewayRegular { color: "white" - text: "© 2019 - 2020 Project Athena Contributors." + text: "© 2019-2020 Vircadia contributors." size: 14 } RalewayRegular { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1b22dbd329..85c2d4abe0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -656,8 +656,8 @@ private: /**jsdoc *

The Controller.Hardware.Application object has properties representing Interface's state. The property * values are integer IDs, uniquely identifying each output. Read-only.

- *

These states can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject} - * mapping (e.g., using the {@link RouteObject#when} method). Each data value is either 1.0 for "true" or + *

These states can be mapped to actions or functions or Controller.Standard items in a {@link RouteObject} + * mapping (e.g., using the {@link RouteObject#when} method). Each data value is either 1.0 for "true" or * 0.0 for "false".

* * @@ -679,7 +679,7 @@ private: * * * - * * * @@ -829,7 +829,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { audioDLLPath += "/audioWin7"; } QCoreApplication::addLibraryPath(audioDLLPath); -#endif +#endif QString defaultScriptsOverrideOption = getCmdOption(argc, constArgv, "--defaultScriptsOverride"); @@ -949,7 +949,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); PlatformHelper::setup(); - + QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] { QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); @@ -1092,8 +1092,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo { // identify gpu as early as possible to help identify OpenGL initialization errors. auto gpuIdent = GPUIdent::getInstance(); - setCrashAnnotation("gpu_name", gpuIdent->getName().toStdString()); - setCrashAnnotation("gpu_driver", gpuIdent->getDriver().toStdString()); + setCrashAnnotation("sentry[contexts][gpu][name]", gpuIdent->getName().toStdString()); + setCrashAnnotation("sentry[contexts][gpu][version]", gpuIdent->getDriver().toStdString()); setCrashAnnotation("gpu_memory", std::to_string(gpuIdent->getMemory())); } @@ -1139,7 +1139,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-SemiBold.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-Regular.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Graphik-Medium.ttf"); - _window->setWindowTitle("Project Athena"); + _window->setWindowTitle("Vircadia"); Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us @@ -1162,7 +1162,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId()); deadlockWatchdogThread->start(); - // Pause the deadlock watchdog when we sleep, or it might + // Pause the deadlock watchdog when we sleep, or it might // trigger a false positive when we wake back up auto platformHelper = PlatformHelper::instance(); @@ -3166,7 +3166,7 @@ void Application::showLoginScreen() { QJsonObject loginData = {}; loginData["action"] = "login dialog popped up"; UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - _window->setWindowTitle("Project Athena"); + _window->setWindowTitle("Vircadia"); } else { resumeAfterLoginDialogActionTaken(); } @@ -3177,7 +3177,7 @@ void Application::showLoginScreen() { #endif } -static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://content.highfidelity.com/Experiences/Releases" }; +static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://cdn.vircadia.com/community-apps/applications" }; void Application::initializeUi() { @@ -3196,14 +3196,16 @@ void Application::initializeUi() { safeURLS += settingsSafeURLS; // END PULL SAFEURLS FROM INTERFACE.JSON Settings - - bool isInWhitelist = false; // assume unsafe - for (const auto& str : safeURLS) { - if (!str.isEmpty() && str.endsWith(".qml") && url.toString().endsWith(".qml") && - url.toString().startsWith(str)) { - qCDebug(interfaceapp) << "Found matching url!" << url.host(); - isInWhitelist = true; - return true; + + if (AUTHORIZED_EXTERNAL_QML_SOURCE.isParentOf(url)) { + return true; + } else { + for (const auto& str : safeURLS) { + if (!str.isEmpty() && str.endsWith(".qml") && url.toString().endsWith(".qml") && + url.toString().startsWith(str)) { + qCDebug(interfaceapp) << "Found matching url!" << url.host(); + return true; + } } } @@ -3788,9 +3790,8 @@ void Application::setPreferredCursor(const QString& cursorName) { if (_displayPlugin && _displayPlugin->isHmd()) { _preferredCursor.set(cursorName.isEmpty() ? DEFAULT_CURSOR_NAME : cursorName); - } - else { - _preferredCursor.set(cursorName.isEmpty() ? Cursor::Manager::getIconName(Cursor::Icon::SYSTEM) : cursorName); + } else { + _preferredCursor.set(cursorName.isEmpty() ? Cursor::Manager::getIconName(Cursor::Icon::SYSTEM) : cursorName); } showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get())); @@ -3977,7 +3978,7 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { DependencyManager::get()->loadSettings(addressLookupString); sentTo = SENT_TO_PREVIOUS_LOCATION; } - + UserActivityLogger::getInstance().logAction("startup_sent_to", { { "sent_to", sentTo }, { "sandbox_is_running", sandboxIsRunning }, @@ -4212,7 +4213,7 @@ bool Application::event(QEvent* event) { idle(); #ifdef DEBUG_EVENT_QUEUE_DEPTH - // The event queue may very well grow beyond 400, so + // The event queue may very well grow beyond 400, so // this code should only be enabled on local builds { int count = ::hifi::qt::getEventQueueSize(QThread::currentThread()); @@ -4251,7 +4252,7 @@ bool Application::event(QEvent* event) { { //testing to see if we can set focus when focus is not set to root window. _glWidget->activateWindow(); _glWidget->setFocus(); - return true; + return true; } case QEvent::TouchBegin: @@ -5236,7 +5237,7 @@ void Application::idle() { } } #endif - + checkChangeCursor(); #if !defined(DISABLE_QML) @@ -5489,7 +5490,7 @@ void Application::loadSettings() { RenderScriptingInterface::getInstance()->loadSettings(); // Setup the PerformanceManager which will enforce the several settings to match the Preset - // On the first run, the Preset is evaluated from the + // On the first run, the Preset is evaluated from the getPerformanceManager().setupPerformancePresetSettings(_firstRun.get()); // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings @@ -5535,7 +5536,7 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse _entityClipboard->withWriteLock([&] { _entityClipboard->eraseAllOctreeElements(); - // FIXME: readFromURL() can take over the main event loop which may cause problems, especially if downloading the JSON + // FIXME: readFromURL() can take over the main event loop which may cause problems, especially if downloading the JSON // from the Web. success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId); if (success) { @@ -7063,7 +7064,7 @@ void Application::updateWindowTitle() const { auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); - QString buildVersion = " - Project Athena v0.86.0 K2 - " + QString buildVersion = " - Vircadia - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + " " + applicationVersion(); @@ -7073,7 +7074,7 @@ void Application::updateWindowTitle() const { nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; QString username = accountManager->getAccountInfo().getUsername(); - setCrashAnnotation("username", username.toStdString()); + setCrashAnnotation("sentry[user][username]", username.toStdString()); QString currentPlaceName; if (isServerlessMode()) { @@ -7747,7 +7748,7 @@ bool Application::askToReplaceDomainContent(const QString& url) { static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. " "If you want to save what you have now, create a backup before proceeding. For more information about backing up " "and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) + - "\nhttps://docs.projectathena.dev/host/maintain-domain/backup-domain.html"; + "\nhttps://docs.vircadia.dev/host/maintain-domain/backup-domain.html"; ModalDialogListener* dig = OffscreenUi::asyncQuestion("Are you sure you want to replace this domain's content set?", infoText, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); @@ -8735,7 +8736,7 @@ bool Application::isThrottleRendering() const { bool Application::hasFocus() const { bool result = (QApplication::activeWindow() != nullptr); - + #if defined(Q_OS_WIN) // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't // take user focus away from their current window. So also check whether the application is the user's current foreground diff --git a/interface/src/CrashHandler_Crashpad.cpp b/interface/src/CrashHandler_Crashpad.cpp index d1b5103990..900a296955 100644 --- a/interface/src/CrashHandler_Crashpad.cpp +++ b/interface/src/CrashHandler_Crashpad.cpp @@ -84,10 +84,9 @@ bool startCrashHandler(std::string appPath) { std::vector arguments; std::map annotations; - annotations["token"] = BACKTRACE_TOKEN; - annotations["format"] = "minidump"; - annotations["version"] = BuildInfo::VERSION.toStdString(); - annotations["build_number"] = BuildInfo::BUILD_NUMBER.toStdString(); + annotations["sentry[release]"] = BACKTRACE_TOKEN; + annotations["sentry[contexts][app][app_version]"] = BuildInfo::VERSION.toStdString(); + annotations["sentry[contexts][app][app_build]"] = BuildInfo::BUILD_NUMBER.toStdString(); annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING.toStdString(); auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index f7be71e053..2a131d991f 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -20,7 +20,7 @@ class FancyCamera : public Camera { /**jsdoc * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * * @namespace Camera * diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index b5cacd662b..a3ef39f1e9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -223,9 +223,9 @@ Menu::Menu() { MenuWrapper* startupLocationMenu = navigateMenu->addMenu(MenuOption::StartUpLocation); QActionGroup* startupLocatiopnGroup = new QActionGroup(startupLocationMenu); startupLocatiopnGroup->setExclusive(true); - startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0, + startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0, false)); - startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0, + startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0, true)); // Settings menu ---------------------------------- @@ -288,13 +288,13 @@ Menu::Menu() { hmd->toggleShouldShowTablet(); } }); - + // Settings > Entity Script / QML Whitelist action = addActionToQMenuAndActionHash(settingsMenu, "Entity Script / QML Whitelist"); connect(action, &QAction::triggered, [] { auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); auto hmd = DependencyManager::get(); - + tablet->pushOntoStack("hifi/dialogs/security/EntityScriptQMLWhitelist.qml"); if (!hmd->getShouldShowTablet()) { @@ -310,10 +310,10 @@ Menu::Menu() { // Developer menu ---------------------------------- MenuWrapper* developerMenu = addMenu("Developer", "Developer"); - + // Developer > Scripting >>> MenuWrapper* scriptingOptionsMenu = developerMenu->addMenu("Scripting"); - + // Developer > Scripting > Console... addActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, DependencyManager::get().data(), @@ -328,7 +328,7 @@ Menu::Menu() { defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js"); DependencyManager::get()->loadScript(defaultScriptsLoc.toString()); }); - + // Developer > Scripting > Entity Script Server Log auto essLogAction = addActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::EntityScriptServerLog, 0, qApp, SLOT(toggleEntityScriptServerLogDialog())); @@ -348,7 +348,7 @@ Menu::Menu() { // Developer > Scripting > Verbose Logging addCheckableActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::VerboseLogging, 0, false, qApp, SLOT(updateVerboseLogging())); - + // Developer > Scripting > Enable Speech Control API #if defined(Q_OS_MAC) || defined(Q_OS_WIN) auto speechRecognizer = DependencyManager::get(); @@ -360,20 +360,20 @@ Menu::Menu() { UNSPECIFIED_POSITION); connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif - + // Developer > UI >>> MenuWrapper* uiOptionsMenu = developerMenu->addMenu("UI"); action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::DesktopTabletToToolbar, 0, qApp->getDesktopTabletBecomesToolbarSetting()); - + // Developer > UI > Show Overlays addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Overlays, 0, true); - + // Developer > UI > Desktop Tablet Becomes Toolbar connect(action, &QAction::triggered, [action] { qApp->setDesktopTabletBecomesToolbarSetting(action->isChecked()); }); - + // Developer > UI > HMD Tablet Becomes Toolbar action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::HMDTabletToToolbar, 0, qApp->getHmdTabletBecomesToolbarSetting()); @@ -614,9 +614,15 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, - false, + true, &UserActivityLogger::getInstance(), SLOT(disable(bool))); + addCheckableActionToQMenuAndActionHash(networkMenu, + MenuOption::DisableCrashLogger, + 0, + true, + &UserActivityLogger::getInstance(), + SLOT(crashMonitorDisable(bool))); addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0, qApp, SLOT(loadDomainConnectionDialog())); @@ -702,7 +708,7 @@ Menu::Menu() { result = QProcessEnvironment::systemEnvironment().contains(HIFI_SHOW_DEVELOPER_CRASH_MENU); if (result) { MenuWrapper* crashMenu = developerMenu->addMenu("Crash"); - + // Developer > Crash > Display Crash Options addCheckableActionToQMenuAndActionHash(crashMenu, MenuOption::DisplayCrashOptions, 0, true); @@ -741,7 +747,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOnShutdown, 0, qApp, SLOT(crashOnShutdown())); } - + // Developer > Show Statistics addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats, 0, true); @@ -782,21 +788,21 @@ Menu::Menu() { // Help/Application menu ---------------------------------- MenuWrapper * helpMenu = addMenu("Help"); - // Help > About Project Athena - action = addActionToQMenuAndActionHash(helpMenu, "About Project Athena"); + // Help > About Vircadia + action = addActionToQMenuAndActionHash(helpMenu, "About Vircadia"); connect(action, &QAction::triggered, [] { qApp->showDialog(QString("hifi/dialogs/AboutDialog.qml"), QString("hifi/dialogs/TabletAboutDialog.qml"), "AboutDialog"); }); helpMenu->addSeparator(); - // Help > Athena Docs + // Help > Vircadia Docs action = addActionToQMenuAndActionHash(helpMenu, "Online Documentation"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://docs.projectathena.dev/")); + QDesktopServices::openUrl(QUrl("https://docs.vircadia.dev/")); }); - // Help > Athena Forum + // Help > Vircadia Forum /* action = addActionToQMenuAndActionHash(helpMenu, "Online Forums"); connect(action, &QAction::triggered, qApp, [] { QDesktopServices::openUrl(QUrl("https://forums.highfidelity.com/")); @@ -805,7 +811,7 @@ Menu::Menu() { // Help > Scripting Reference action = addActionToQMenuAndActionHash(helpMenu, "Online Script Reference"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://apidocs.projectathena.dev/")); + QDesktopServices::openUrl(QUrl("https://apidocs.vircadia.dev/")); }); addActionToQMenuAndActionHash(helpMenu, "Controls Reference", 0, qApp, SLOT(showHelp())); @@ -815,7 +821,7 @@ Menu::Menu() { // Help > Release Notes action = addActionToQMenuAndActionHash(helpMenu, "Release Notes"); connect(action, &QAction::triggered, qApp, [] { - QDesktopServices::openUrl(QUrl("https://docs.projectathena.dev/release-notes.html")); + QDesktopServices::openUrl(QUrl("https://docs.vircadia.dev/release-notes.html")); }); // Help > Report a Bug! diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 3fcb261705..a3da179ad3 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -86,6 +86,7 @@ namespace MenuOption { const QString DeleteAvatarEntitiesBookmark = "Delete Avatar Entities Bookmark"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; + const QString DisableCrashLogger = "Disable Crash Reporter"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; const QString DisplayCrashOptions = "Display Crash Options"; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index bf7fd26b08..d67341990d 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -80,7 +80,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { // update the joint indices QVariantHash jointIndices; - for (size_t i = 0; i < _hfmModel.joints.size(); i++) { + for (int i = 0; i < _hfmModel.joints.size(); i++) { jointIndices.insert(_hfmModel.joints.at(i).name, QString::number(i)); } mapping.insert(JOINT_INDEX_FIELD, jointIndices); diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index 3c56170825..d84383cf4f 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -55,7 +55,7 @@ static QStringList HAND_MAPPING_SUFFIXES = { "HandThumb1", }; -const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.projectathena.dev/create/avatars/package-avatar.html"); +const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.vircadia.dev/create/avatars/package-avatar.html"); AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) : _avatarFSTFileUrl(avatarFSTFileUrl) { @@ -79,7 +79,7 @@ void AvatarDoctor::startDiagnosing() { _missingTextureCount = 0; _unsupportedTextureCount = 0; - const auto resource = DependencyManager::get()->getModelResource(_avatarFSTFileUrl); + const auto resource = DependencyManager::get()->getGeometryResource(_avatarFSTFileUrl); resource->refresh(); const auto resourceLoaded = [this, resource](bool success) { @@ -99,12 +99,12 @@ void AvatarDoctor::startDiagnosing() { } // RIG - if (avatarModel.joints.empty()) { + if (avatarModel.joints.isEmpty()) { addError("Avatar has no rig.", "no-rig"); } else { auto jointNames = avatarModel.getJointNames(); - if (avatarModel.joints.size() > NETWORKED_JOINTS_LIMIT) { + if (avatarModel.joints.length() > NETWORKED_JOINTS_LIMIT) { addError(tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), "maximum-bone-limit"); } // Avatar does not have Hips bone mapped @@ -297,7 +297,7 @@ void AvatarDoctor::startDiagnosing() { if (resource->isLoaded()) { resourceLoaded(!resource->isFailed()); } else { - connect(resource.data(), &ModelResource::finished, this, resourceLoaded); + connect(resource.data(), &GeometryResource::finished, this, resourceLoaded); } } else { addError("Model file cannot be opened", "missing-file"); diff --git a/interface/src/avatar/AvatarDoctor.h b/interface/src/avatar/AvatarDoctor.h index 1e3c84e02f..1465a5defc 100644 --- a/interface/src/avatar/AvatarDoctor.h +++ b/interface/src/avatar/AvatarDoctor.h @@ -53,7 +53,7 @@ private: int _materialMappingCount = 0; int _materialMappingLoadedCount = 0; - ModelResource::Pointer _model; + GeometryResource::Pointer _model; bool _isDiagnosing = false; }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4736362fcb..570ac5fce2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -972,7 +972,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { recorder->recordFrame(FRAME_TYPE, toFrame(*this)); } - locationChanged(true, false); + locationChanged(true, true); // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; @@ -981,16 +981,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { entityTree->withWriteLock([&] { zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - forEachDescendant([&](SpatiallyNestablePointer object) { - locationChanged(true, false); - // we need to update attached queryAACubes in our own local tree so point-select always works - // however we don't want to flood the update pipeline with AvatarEntity updates, so we assume - // others have all info required to properly update queryAACube of AvatarEntities on their end - EntityItemPointer entity = std::dynamic_pointer_cast(object); - bool iShouldTellServer = !(entity && entity->isAvatarEntity()); - const bool force = false; - entityTree->updateEntityQueryAACube(object, packetSender, force, iShouldTellServer); - }); + entityTree->updateEntityQueryAACube(shared_from_this(), packetSender, false, true); }); bool isPhysicsEnabled = qApp->isPhysicsEnabled(); bool zoneAllowsFlying = zoneInteractionProperties.first; @@ -1988,7 +1979,7 @@ void MyAvatar::loadData() { // Flying preferences must be loaded before calling setFlyingEnabled() Setting::Handle firstRunVal { Settings::firstRun, true }; - setFlyingHMDPref(firstRunVal.get() ? false : _flyingHMDSetting.get()); + setFlyingHMDPref(firstRunVal.get() ? true : _flyingHMDSetting.get()); setMovementReference(firstRunVal.get() ? false : _movementReferenceSetting.get()); setDriveGear1(firstRunVal.get() ? DEFAULT_GEAR_1 : _driveGear1Setting.get()); setDriveGear2(firstRunVal.get() ? DEFAULT_GEAR_2 : _driveGear2Setting.get()); @@ -2483,7 +2474,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (_fullAvatarModelName.isEmpty()) { // Store the FST file name into preferences - const auto& mapping = _skeletonModel->getNetworkModel()->getMapping(); + const auto& mapping = _skeletonModel->getGeometry()->getMapping(); if (mapping.value("name").isValid()) { _fullAvatarModelName = mapping.value("name").toString(); } @@ -2491,7 +2482,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); - _fstAnimGraphOverrideUrl = _skeletonModel->getNetworkModel()->getAnimGraphOverrideUrl(); + _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); initFlowFromFST(); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2a83ab69c1..98008583a4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -762,7 +762,7 @@ public: *

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards. + * Avatar Standards. * @function MyAvatar.overrideRoleAnimation * @param {string} role - The animation role to override * @param {string} url - The URL to the animation file. Animation files need to be in glTF or FBX format, but only need to @@ -1920,7 +1920,7 @@ public: /**jsdoc * Enables and disables flow simulation of physics on the avatar's hair, clothes, and body parts. See - * {@link https://docs.projectathena.dev/create/avatars/add-flow.html|Add Flow to Your Avatar} for more + * {@link https://docs.vircadia.dev/create/avatars/add-flow.html|Add Flow to Your Avatar} for more * information. * @function MyAvatar.useFlow * @param {boolean} isActive - true if flow simulation is enabled on the joint, false if it isn't. @@ -2285,7 +2285,7 @@ public slots: /**jsdoc * Gets the URL of the override animation graph. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.getAnimGraphOverrideUrl * @returns {string} The URL of the override animation graph JSON file. "" if there is no override animation @@ -2295,7 +2295,7 @@ public slots: /**jsdoc * Sets the animation graph to use in preference to the default animation graph. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.setAnimGraphOverrideUrl * @param {string} url - The URL of the animation graph JSON file to use. Set to "" to clear an override. @@ -2304,7 +2304,7 @@ public slots: /**jsdoc * Gets the URL of animation graph (i.e., the avatar animation JSON) that's currently being used for avatar animations. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.getAnimGraphUrl * @returns {string} The URL of the current animation graph JSON file. @@ -2315,7 +2315,7 @@ public slots: /**jsdoc * Sets the current animation graph (i.e., the avatar animation JSON) to use for avatar animations and makes it the default. - *

See {@link https://docs.projectathena.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for + *

See {@link https://docs.vircadia.dev/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.setAnimGraphUrl * @param {string} url - The URL of the animation graph JSON file to use. @@ -2702,7 +2702,7 @@ private: bool _enableFlying { false }; bool _flyingPrefDesktop { true }; - bool _flyingPrefHMD { false }; + bool _flyingPrefHMD { true }; bool _wasPushing { false }; bool _isPushing { false }; bool _isBeingPushed { false }; diff --git a/interface/src/graphics/WorldBox.h b/interface/src/graphics/WorldBox.h index 114777ba0f..4d53652c0e 100644 --- a/interface/src/graphics/WorldBox.h +++ b/interface/src/graphics/WorldBox.h @@ -19,8 +19,6 @@ #include #include "Menu.h" - - class WorldBoxRenderData { public: typedef render::Payload Payload; @@ -29,8 +27,6 @@ public: int _val = 0; static render::ItemID _item; // unique WorldBoxRenderData - - static void renderWorldBox(RenderArgs* args, gpu::Batch& batch); }; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 81616e5773..3f6d5ed408 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -72,7 +72,7 @@ int main(int argc, const char* argv[]) { } QCommandLineParser parser; - parser.setApplicationDescription("High Fidelity"); + parser.setApplicationDescription("Vircadia"); QCommandLineOption versionOption = parser.addVersionOption(); QCommandLineOption helpOption = parser.addHelpOption(); @@ -218,12 +218,12 @@ int main(int argc, const char* argv[]) { } qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled(); - if (ual.isEnabled()) { + qDebug() << "Crash handler logger is enabled:" << ual.isCrashMonitorEnabled(); + if (ual.isCrashMonitorEnabled()) { auto crashHandlerStarted = startCrashHandler(argv[0]); qDebug() << "Crash handler started:" << crashHandlerStarted; } - const QString& applicationName = getInterfaceSharedMemoryName(); bool instanceMightBeRunning = true; #ifdef Q_OS_WIN diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 756c8fab7f..2602bdb0a0 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -121,9 +121,8 @@ bool CollisionPick::isLoaded() const { bool CollisionPick::getShapeInfoReady(const CollisionRegion& pick) { if (_mathPick.shouldComputeShapeInfo()) { if (_cachedResource && _cachedResource->isLoaded()) { - // TODO: Model CollisionPick support - //computeShapeInfo(pick, *_mathPick.shapeInfo, _cachedResource); - //_mathPick.loaded = true; + computeShapeInfo(pick, *_mathPick.shapeInfo, _cachedResource); + _mathPick.loaded = true; } else { _mathPick.loaded = false; } @@ -135,7 +134,7 @@ bool CollisionPick::getShapeInfoReady(const CollisionRegion& pick) { return _mathPick.loaded; } -void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { +void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { ShapeType type = shapeInfo.getType(); glm::vec3 dimensions = pick.transform.getScale(); QString modelURL = (resource ? resource->getURL().toString() : ""); @@ -148,12 +147,241 @@ void CollisionPick::computeShapeInfoDimensionsOnly(const CollisionRegion& pick, } } +void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { + // This code was copied and modified from RenderableModelEntityItem::computeShapeInfo + // TODO: Move to some shared code area (in entities-renderer? model-networking?) + // after we verify this is working and do a diff comparison with RenderableModelEntityItem::computeShapeInfo + // to consolidate the code. + // We may also want to make computeShapeInfo always abstract away from the gpu model mesh, like it does here. + const uint32_t TRIANGLE_STRIDE = 3; + const uint32_t QUAD_STRIDE = 4; + + ShapeType type = shapeInfo.getType(); + glm::vec3 dimensions = pick.transform.getScale(); + if (type == SHAPE_TYPE_COMPOUND) { + // should never fall in here when collision model not fully loaded + // TODO: assert that all geometries exist and are loaded + //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); + const HFMModel& collisionModel = resource->getHFMModel(); + + ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); + pointCollection.clear(); + uint32_t i = 0; + + // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect + // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. + foreach (const HFMMesh& mesh, collisionModel.meshes) { + // each meshPart is a convex hull + foreach (const HFMMeshPart &meshPart, mesh.parts) { + pointCollection.push_back(QVector()); + ShapeInfo::PointList& pointsInPart = pointCollection[i]; + + // run through all the triangles and (uniquely) add each point to the hull + uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up + //assert(numIndices % TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer + + for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) { + glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]]; + glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]]; + if (!pointsInPart.contains(p0)) { + pointsInPart << p0; + } + if (!pointsInPart.contains(p1)) { + pointsInPart << p1; + } + if (!pointsInPart.contains(p2)) { + pointsInPart << p2; + } + } + + // run through all the quads and (uniquely) add each point to the hull + numIndices = (uint32_t)meshPart.quadIndices.size(); + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up + //assert(numIndices % QUAD_STRIDE == 0); + numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer + + for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) { + glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]]; + glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.quadIndices[j + 2]]; + glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]]; + if (!pointsInPart.contains(p0)) { + pointsInPart << p0; + } + if (!pointsInPart.contains(p1)) { + pointsInPart << p1; + } + if (!pointsInPart.contains(p2)) { + pointsInPart << p2; + } + if (!pointsInPart.contains(p3)) { + pointsInPart << p3; + } + } + + if (pointsInPart.size() == 0) { + qCDebug(scriptengine) << "Warning -- meshPart has no faces"; + pointCollection.pop_back(); + continue; + } + ++i; + } + } + + // We expect that the collision model will have the same units and will be displaced + // from its origin in the same way the visual model is. The visual model has + // been centered and probably scaled. We take the scaling and offset which were applied + // to the visual model and apply them to the collision model (without regard for the + // collision model's extents). + + glm::vec3 scaleToFit = dimensions / resource->getHFMModel().getUnscaledMeshExtents().size(); + // multiply each point by scale + for (int32_t i = 0; i < pointCollection.size(); i++) { + for (int32_t j = 0; j < pointCollection[i].size(); j++) { + // back compensate for registration so we can apply that offset to the shapeInfo later + pointCollection[i][j] = scaleToFit * pointCollection[i][j]; + } + } + shapeInfo.setParams(type, dimensions, resource->getURL().toString()); + } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { + const HFMModel& hfmModel = resource->getHFMModel(); + int numHFMMeshes = hfmModel.meshes.size(); + int totalNumVertices = 0; + for (int i = 0; i < numHFMMeshes; i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); + totalNumVertices += mesh.vertices.size(); + } + const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; + if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) { + qWarning() << "model" << "has too many vertices" << totalNumVertices << "and will collide as a box."; + shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); + return; + } + + auto& meshes = resource->getHFMModel().meshes; + int32_t numMeshes = (int32_t)(meshes.size()); + + const int MAX_ALLOWED_MESH_COUNT = 1000; + if (numMeshes > MAX_ALLOWED_MESH_COUNT) { + // too many will cause the deadlock timer to throw... + shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); + return; + } + + ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); + pointCollection.clear(); + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + pointCollection.resize(numMeshes); + } else { + pointCollection.resize(1); + } + + ShapeInfo::TriangleIndices& triangleIndices = shapeInfo.getTriangleIndices(); + triangleIndices.clear(); + + Extents extents; + int32_t meshCount = 0; + int32_t pointListIndex = 0; + for (auto& mesh : meshes) { + if (!mesh.vertices.size()) { + continue; + } + QVector vertices = mesh.vertices; + + ShapeInfo::PointList& points = pointCollection[pointListIndex]; + + // reserve room + int32_t sizeToReserve = (int32_t)(vertices.count()); + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + // a list of points for each mesh + pointListIndex++; + } else { + // only one list of points + sizeToReserve += (int32_t)points.size(); + } + points.reserve(sizeToReserve); + + // copy points + const glm::vec3* vertexItr = vertices.cbegin(); + while (vertexItr != vertices.cend()) { + glm::vec3 point = *vertexItr; + points.push_back(point); + extents.addPoint(point); + ++vertexItr; + } + + if (type == SHAPE_TYPE_STATIC_MESH) { + // copy into triangleIndices + size_t triangleIndicesCount = 0; + for (const HFMMeshPart& meshPart : mesh.parts) { + triangleIndicesCount += meshPart.triangleIndices.count(); + } + triangleIndices.reserve((int)triangleIndicesCount); + + for (const HFMMeshPart& meshPart : mesh.parts) { + const int* indexItr = meshPart.triangleIndices.cbegin(); + while (indexItr != meshPart.triangleIndices.cend()) { + triangleIndices.push_back(*indexItr); + ++indexItr; + } + } + } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + // for each mesh copy unique part indices, separated by special bogus (flag) index values + for (const HFMMeshPart& meshPart : mesh.parts) { + // collect unique list of indices for this part + std::set uniqueIndices; + auto numIndices = meshPart.triangleIndices.count(); + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up + //assert(numIndices% TRIANGLE_STRIDE == 0); + numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer + + auto indexItr = meshPart.triangleIndices.cbegin(); + while (indexItr != meshPart.triangleIndices.cend()) { + uniqueIndices.insert(*indexItr); + ++indexItr; + } + + // store uniqueIndices in triangleIndices + triangleIndices.reserve(triangleIndices.size() + (int32_t)uniqueIndices.size()); + for (auto index : uniqueIndices) { + triangleIndices.push_back(index); + } + // flag end of part + triangleIndices.push_back(END_OF_MESH_PART); + } + // flag end of mesh + triangleIndices.push_back(END_OF_MESH); + } + ++meshCount; + } + + // scale and shift + glm::vec3 extentsSize = extents.size(); + glm::vec3 scaleToFit = dimensions / extentsSize; + for (int32_t i = 0; i < 3; ++i) { + if (extentsSize[i] < 1.0e-6f) { + scaleToFit[i] = 1.0f; + } + } + for (auto points : pointCollection) { + for (int32_t i = 0; i < points.size(); ++i) { + points[i] = (points[i] * scaleToFit); + } + } + + shapeInfo.setParams(type, 0.5f * dimensions, resource->getURL().toString()); + } +} + CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, bool scaleWithParent, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) : Pick(collisionRegion, filter, maxDistance, enabled), _scaleWithParent(scaleWithParent), _physicsEngine(physicsEngine) { if (collisionRegion.shouldComputeShapeInfo()) { - _cachedResource = DependencyManager::get()->getCollisionModelResource(collisionRegion.modelURL); + _cachedResource = DependencyManager::get()->getCollisionGeometryResource(collisionRegion.modelURL); } _mathPick.loaded = isLoaded(); } diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 617c7b1f00..24317bf19a 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -63,13 +63,14 @@ protected: bool isLoaded() const; // Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use. bool getShapeInfoReady(const CollisionRegion& pick); - void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); + void computeShapeInfo(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); + void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); void filterIntersections(std::vector& intersections) const; bool _scaleWithParent; PhysicsEnginePointer _physicsEngine; - QSharedPointer _cachedResource; + QSharedPointer _cachedResource; // Options for what information to get from collision results bool _includeNormals; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 7e11406808..4ce30063e2 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -254,7 +254,15 @@ void setupPreferences() { auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); }; preferences->addPreference(new CheckPreference("Privacy", "Send data - High Fidelity uses information provided by your " "client to improve the product through the logging of errors, tracking of usage patterns, " - "installation and system details, and crash events. By allowing High Fidelity to collect " + "installation and system details. By allowing High Fidelity to collect this information " + "you are helping to improve the product. ", getter, setter)); + } + + { + auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableCrashLogger); }; + auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableCrashLogger, !value); }; + preferences->addPreference(new CheckPreference("Privacy", "Send crashes - Vircadia uses information provided by your " + "client to improve the product through crash reports. By allowing Vircadia to collect " "this information you are helping to improve the product. ", getter, setter)); } diff --git a/launchers/qt/src/BuildsRequest.h b/launchers/qt/src/BuildsRequest.h index 865d375d2a..cb33182cdb 100644 --- a/launchers/qt/src/BuildsRequest.h +++ b/launchers/qt/src/BuildsRequest.h @@ -4,10 +4,10 @@ #include struct Build { - QString tag{ QString::null }; + QString tag; int latestVersion{ 0 }; int buildNumber{ 0 }; - QString installerZipURL{ QString::null }; + QString installerZipURL; }; struct Builds { diff --git a/launchers/qt/src/LauncherState.cpp b/launchers/qt/src/LauncherState.cpp index 300bd37ef8..03e91ae58f 100644 --- a/launchers/qt/src/LauncherState.cpp +++ b/launchers/qt/src/LauncherState.cpp @@ -244,7 +244,7 @@ void LauncherState::getCurrentClientVersion() { if (match.hasMatch()) { _currentClientVersion = match.captured("version"); } else { - _currentClientVersion = QString::null; + _currentClientVersion.clear(); } qDebug() << "Current client version is: " << _currentClientVersion; diff --git a/launchers/qt/src/LauncherState.h b/launchers/qt/src/LauncherState.h index 39c4141b81..c0eb44cb08 100644 --- a/launchers/qt/src/LauncherState.h +++ b/launchers/qt/src/LauncherState.h @@ -176,7 +176,7 @@ private: QString _displayName; QString _applicationErrorMessage; QString _currentClientVersion; - QString _buildTag { QString::null }; + QString _buildTag; QString _contentCacheURL; QString _loginTokenResponse; QFile _clientZipFile; diff --git a/launchers/qt/src/UserSettingsRequest.h b/launchers/qt/src/UserSettingsRequest.h index 5827364377..a42a70df37 100644 --- a/launchers/qt/src/UserSettingsRequest.h +++ b/launchers/qt/src/UserSettingsRequest.h @@ -6,7 +6,7 @@ #include "LoginRequest.h" struct UserSettings { - QString homeLocation{ QString::null }; + QString homeLocation; }; class UserSettingsRequest : public QObject { diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index e5f05ab45f..b26d00d8d0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -20,17 +20,24 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { _geometryOffset = hfmModel.offset; - buildSkeletonFromJoints(hfmModel.joints, hfmModel.jointRotationOffsets); + // convert to std::vector of joints + std::vector joints; + joints.reserve(hfmModel.joints.size()); + for (auto& joint : hfmModel.joints) { + joints.push_back(joint); + } + buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose // when we are dealing with a joint offset in the model - for (uint32_t i = 0; i < (uint32_t)hfmModel.skinDeformers.size(); i++) { - const auto& deformer = hfmModel.skinDeformers[i]; + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); std::vector dummyClustersList; - for (uint32_t j = 0; j < (uint32_t)deformer.clusters.size(); j++) { + for (int j = 0; j < mesh.clusters.size(); j++) { + std::vector bindMatrices; // cast into a non-const reference, so we can mutate the FBXCluster - HFMCluster& cluster = const_cast(deformer.clusters.at(j)); + HFMCluster& cluster = const_cast(mesh.clusters.at(j)); HFMCluster localCluster; localCluster.jointIndex = cluster.jointIndex; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index a6470ac609..efc1c1599f 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -68,7 +68,7 @@ public: void dump(const AnimPoseVec& poses) const; std::vector lookUpJointIndices(const std::vector& jointNames) const; - const HFMCluster getClusterBindMatricesOriginalValues(int skinDeformerIndex, int clusterIndex) const { return _clusterBindMatrixOriginalValues[skinDeformerIndex][clusterIndex]; } + const HFMCluster getClusterBindMatricesOriginalValues(const int meshIndex, const int clusterIndex) const { return _clusterBindMatrixOriginalValues[meshIndex][clusterIndex]; } protected: void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 3792057052..16dc99ab49 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -943,7 +943,7 @@ void Avatar::simulateAttachments(float deltaTime) { bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i); // Watch for texture loading - if (!texturesLoaded && model->getNetworkModel() && model->getNetworkModel()->areTexturesLoaded()) { + if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { _attachmentModelsTexturesLoaded[i] = true; model->updateRenderItems(); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index cd1fc7c7fc..25dd347484 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -207,7 +207,7 @@ public: /**jsdoc * Gets the default rotation of a joint (in the current avatar) relative to its parent. *

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.getDefaultJointRotation * @param {number} index - The joint index. * @returns {Quat} The default rotation of the joint if the joint index is valid, otherwise {@link Quat(0)|Quat.IDENTITY}. @@ -218,7 +218,7 @@ public: * Gets the default translation of a joint (in the current avatar) relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.getDefaultJointTranslation * @param {number} index - The joint index. * @returns {Vec3} The default translation of the joint (in model coordinates) if the joint index is valid, otherwise diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index e9e26ca716..ccc87c28f3 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -171,7 +171,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { // FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem, // but Avatars don't get updates in the same way - if (!_texturesLoaded && getNetworkModel() && getNetworkModel()->areTexturesLoaded()) { + if (!_texturesLoaded && getGeometry() && getGeometry()->areTexturesLoaded()) { _texturesLoaded = true; updateRenderItems(); } @@ -326,7 +326,7 @@ void SkeletonModel::computeBoundingShape() { } const HFMModel& hfmModel = getHFMModel(); - if (hfmModel.joints.empty() || _rig.indexOfJoint("Hips") == -1) { + if (hfmModel.joints.isEmpty() || _rig.indexOfJoint("Hips") == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d6a4497ee7..2e25c9559c 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -796,7 +796,7 @@ public: * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example
+ * Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -860,7 +860,7 @@ public: /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -871,7 +871,7 @@ public: * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. @@ -904,7 +904,7 @@ public: * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @example + * Avatar in T-pose with arm rotated * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -939,7 +939,7 @@ public: * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example + * Avatar with neck stretched * // Stretch your avatar's neck. * MyAvatar.setJointTranslation("Neck", Vec3.multiply(2, MyAvatar.getJointTranslation("Neck"))); * @@ -981,7 +981,7 @@ public: /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {string} name - The name of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -996,7 +996,7 @@ public: * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} name - The name of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. @@ -1041,7 +1041,7 @@ public: * @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the * array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @example + * Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { @@ -1138,7 +1138,7 @@ public: * set hasScriptedBlendshapes back to false when the animation is complete. * @function Avatar.setBlendshape * @param {string} name - The name of the blendshape, per the - * {@link https://docs.projectathena.dev/create/avatars/avatar-standards.html#blendshapes Avatar Standards}. + * {@link https://docs.vircadia.dev/create/avatars/avatar-standards.html#blendshapes Avatar Standards}. * @param {number} value - A value between 0.0 and 1.0. * @example * MyAvatar.hasScriptedBlendshapes = true; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 5e8d5c457f..392b08d48c 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -34,7 +34,6 @@ HeadData::HeadData(AvatarData* owningAvatar) : { _userProceduralAnimationFlags.assign((size_t)ProceduralAnimaitonTypeCount, true); _suppressProceduralAnimationFlags.assign((size_t)ProceduralAnimaitonTypeCount, false); - computeBlendshapesLookupMap(); } glm::quat HeadData::getRawOrientation() const { @@ -72,12 +71,6 @@ void HeadData::setOrientation(const glm::quat& orientation) { setHeadOrientation(orientation); } -void HeadData::computeBlendshapesLookupMap(){ - for (int i = 0; i < (int)Blendshapes::BlendshapeCount; i++) { - _blendshapeLookupMap[FACESHIFT_BLENDSHAPES[i]] = i; - } -} - int HeadData::getNumSummedBlendshapeCoefficients() const { int maxSize = std::max(_blendshapeCoefficients.size(), _transientBlendshapeCoefficients.size()); return maxSize; @@ -109,8 +102,8 @@ const QVector& HeadData::getSummedBlendshapeCoefficients() { void HeadData::setBlendshape(QString name, float val) { // Check to see if the named blendshape exists, and then set its value if it does - auto it = _blendshapeLookupMap.find(name); - if (it != _blendshapeLookupMap.end()) { + auto it = BLENDSHAPE_LOOKUP_MAP.find(name); + if (it != BLENDSHAPE_LOOKUP_MAP.end()) { if (_blendshapeCoefficients.size() <= it.value()) { _blendshapeCoefficients.resize(it.value() + 1); } @@ -135,8 +128,8 @@ void HeadData::setBlendshape(QString name, float val) { } int HeadData::getBlendshapeIndex(const QString& name) { - auto it = _blendshapeLookupMap.find(name); - int index = it != _blendshapeLookupMap.end() ? it.value() : -1; + auto it = BLENDSHAPE_LOOKUP_MAP.find(name); + int index = it != BLENDSHAPE_LOOKUP_MAP.end() ? it.value() : -1; return index; } @@ -155,8 +148,8 @@ static const QString JSON_AVATAR_HEAD_LOOKAT = QStringLiteral("lookAt"); QJsonObject HeadData::toJson() const { QJsonObject headJson; QJsonObject blendshapesJson; - for (auto name : _blendshapeLookupMap.keys()) { - auto index = _blendshapeLookupMap[name]; + for (auto name : BLENDSHAPE_LOOKUP_MAP.keys()) { + auto index = BLENDSHAPE_LOOKUP_MAP[name]; float value = 0.0f; if (index < _blendshapeCoefficients.size()) { value += _blendshapeCoefficients[index]; diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 2fa91c0bed..af71fd883d 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -125,7 +125,6 @@ protected: QVector _blendshapeCoefficients; QVector _transientBlendshapeCoefficients; QVector _summedBlendshapeCoefficients; - QMap _blendshapeLookupMap; AvatarData* _owningAvatar; private: @@ -134,7 +133,6 @@ private: HeadData& operator= (const HeadData&); void setHeadOrientation(const glm::quat& orientation); - void computeBlendshapesLookupMap(); }; #endif // hifi_HeadData_h diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 7f508dfe15..eb02ac2241 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -90,11 +90,11 @@ void FBXBaker::replaceMeshNodeWithDraco(FBXNode& meshNode, const QByteArray& dra } } -void FBXBaker::rewriteAndBakeSceneModels(const std::vector& meshes, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) { +void FBXBaker::rewriteAndBakeSceneModels(const QVector& meshes, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) { std::vector meshIndexToRuntimeOrder; - auto meshCount = (uint32_t)meshes.size(); + auto meshCount = (int)meshes.size(); meshIndexToRuntimeOrder.resize(meshCount); - for (uint32_t i = 0; i < meshCount; i++) { + for (int i = 0; i < meshCount; i++) { meshIndexToRuntimeOrder[meshes[i].meshIndex] = i; } diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 6ac05e36e9..a528de512d 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -33,7 +33,7 @@ protected: virtual void bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) override; private: - void rewriteAndBakeSceneModels(const std::vector& meshes, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists); + void rewriteAndBakeSceneModels(const QVector& meshes, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists); void replaceMeshNodeWithDraco(FBXNode& meshNode, const QByteArray& dracoMeshBytes, const std::vector& dracoMaterialList); }; diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index fbb17f0d01..9a1b1b2d24 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -258,9 +258,9 @@ void MaterialBaker::addTexture(const QString& materialName, image::TextureUsage: } }; -void MaterialBaker::setMaterials(const std::vector& materials, const QString& baseURL) { +void MaterialBaker::setMaterials(const QHash& materials, const QString& baseURL) { _materialResource = NetworkMaterialResourcePointer(new NetworkMaterialResource(), [](NetworkMaterialResource* ptr) { ptr->deleteLater(); }); - for (const auto& material : materials) { + for (auto& material : materials) { _materialResource->parsedMaterials.names.push_back(material.name.toStdString()); _materialResource->parsedMaterials.networkMaterials[material.name.toStdString()] = std::make_shared(material, baseURL); diff --git a/libraries/baking/src/MaterialBaker.h b/libraries/baking/src/MaterialBaker.h index 68efcfbd7e..33123cfc73 100644 --- a/libraries/baking/src/MaterialBaker.h +++ b/libraries/baking/src/MaterialBaker.h @@ -32,7 +32,7 @@ public: bool isURL() const { return _isURL; } QString getBakedMaterialData() const { return _bakedMaterialData; } - void setMaterials(const std::vector& materials, const QString& baseURL); + void setMaterials(const QHash& materials, const QString& baseURL); void setMaterials(const NetworkMaterialResourcePointer& materialResource); NetworkMaterialResourcePointer getNetworkMaterialResource() const { return _materialResource; } diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 4e76dbb2d5..70290fe283 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -265,7 +265,7 @@ void ModelBaker::bakeSourceCopy() { return; } - if (!_hfmModel->materials.empty()) { + if (!_hfmModel->materials.isEmpty()) { _materialBaker = QSharedPointer( new MaterialBaker(_modelURL.fileName(), true, _bakedOutputDir), &MaterialBaker::deleteLater diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index d726dee897..a2d0ab1094 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -37,10 +37,10 @@ const QByteArray MESH = "Mesh"; void OBJBaker::bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) { // Write OBJ Data as FBX tree nodes - createFBXNodeTree(_rootNode, hfmModel, dracoMeshes[0], dracoMaterialLists[0]); + createFBXNodeTree(_rootNode, hfmModel, dracoMeshes[0]); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh, const std::vector& dracoMaterialList) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh) { // Make all generated nodes children of rootNode rootNode.children = { FBXNode(), FBXNode(), FBXNode() }; FBXNode& globalSettingsNode = rootNode.children[0]; @@ -100,22 +100,19 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& h } // Generating Objects node's child - Material node - - // Each material ID should only appear once thanks to deduplication in BuildDracoMeshTask, but we want to make sure they are created in the right order - std::unordered_map materialIDToIndex; - for (uint32_t materialIndex = 0; materialIndex < hfmModel->materials.size(); ++materialIndex) { - const auto& material = hfmModel->materials[materialIndex]; - materialIDToIndex[material.materialID] = materialIndex; - } - - // Create nodes for each material in the material list - for (const auto& dracoMaterial : dracoMaterialList) { - const QString materialID = QString(dracoMaterial); - const uint32_t materialIndex = materialIDToIndex[materialID]; - const auto& material = hfmModel->materials[materialIndex]; + auto& meshParts = hfmModel->meshes[0].parts; + for (auto& meshPart : meshParts) { FBXNode materialNode; materialNode.name = MATERIAL_NODE_NAME; - setMaterialNodeProperties(materialNode, material.materialID, material, hfmModel); + if (hfmModel->materials.size() == 1) { + // case when no material information is provided, OBJSerializer considers it as a single default material + for (auto& materialID : hfmModel->materials.keys()) { + setMaterialNodeProperties(materialNode, materialID, hfmModel); + } + } else { + setMaterialNodeProperties(materialNode, meshPart.materialID, hfmModel); + } + objectNode.children.append(materialNode); } @@ -156,10 +153,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& h } // Set properties for material nodes -void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, const QString& materialName, const hfm::Material& material, const hfm::Model::Pointer& hfmModel) { +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, const hfm::Model::Pointer& hfmModel) { auto materialID = nextNodeID(); _materialIDs.push_back(materialID); - materialNode.properties = { materialID, materialName, MESH }; + materialNode.properties = { materialID, material, MESH }; + + HFMMaterial currentMaterial = hfmModel->materials[material]; // Setting the hierarchy: Material -> Properties70 -> P -> Properties FBXNode properties70Node; @@ -171,7 +170,7 @@ void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, const QString& m pNodeDiffuseColor.name = P_NODE_NAME; pNodeDiffuseColor.properties.append({ "DiffuseColor", "Color", "", "A", - material.diffuseColor[0], material.diffuseColor[1], material.diffuseColor[2] + currentMaterial.diffuseColor[0], currentMaterial.diffuseColor[1], currentMaterial.diffuseColor[2] }); } properties70Node.children.append(pNodeDiffuseColor); @@ -182,7 +181,7 @@ void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, const QString& m pNodeSpecularColor.name = P_NODE_NAME; pNodeSpecularColor.properties.append({ "SpecularColor", "Color", "", "A", - material.specularColor[0], material.specularColor[1], material.specularColor[2] + currentMaterial.specularColor[0], currentMaterial.specularColor[1], currentMaterial.specularColor[2] }); } properties70Node.children.append(pNodeSpecularColor); @@ -193,7 +192,7 @@ void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, const QString& m pNodeShininess.name = P_NODE_NAME; pNodeShininess.properties.append({ "Shininess", "Number", "", "A", - material.shininess + currentMaterial.shininess }); } properties70Node.children.append(pNodeShininess); @@ -204,7 +203,7 @@ void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, const QString& m pNodeOpacity.name = P_NODE_NAME; pNodeOpacity.properties.append({ "Opacity", "Number", "", "A", - material.opacity + currentMaterial.opacity }); } properties70Node.children.append(pNodeOpacity); diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 778b4da341..55adec5786 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -27,8 +27,8 @@ protected: virtual void bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) override; private: - void createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh, const std::vector& dracoMaterialList); - void setMaterialNodeProperties(FBXNode& materialNode, const QString& materialName, const hfm::Material& material, const hfm::Model::Pointer& hfmModel); + void createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, const hfm::Model::Pointer& hfmModel); NodeID nextNodeID() { return _nodeID++; } NodeID _nodeID { 0 }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3695ed4b0b..51178fea1b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -248,7 +248,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { for (const auto& entry : _entitiesInScene) { const auto& renderer = entry.second; const EntityItemPointer& entityItem = renderer->getEntity(); - if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { + if (entityItem && !(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { fadeOutRenderable(renderer); } else { savedEntities[entry.first] = entry.second; @@ -685,7 +685,7 @@ void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() { QSet currentEntitiesInsideToSave; foreach (const EntityItemID& entityID, _currentEntitiesInside) { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); - if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { + if (entityItem && !(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { emit leaveEntity(entityID); if (_entitiesScriptEngine) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 7e853f617a..807a240763 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -129,7 +129,7 @@ std::shared_ptr make_renderer(const EntityItemPointer& entity) { return std::shared_ptr(new T(entity), [](T* ptr) { ptr->deleteLater(); }); } -EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity) {} +EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity), _entityID(entity->getID()) {} EntityRenderer::~EntityRenderer() {} @@ -197,6 +197,23 @@ uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const { return 0; } +bool EntityRenderer::passesZoneOcclusionTest(const std::unordered_set& containingZones) const { + auto renderWithZones = resultWithReadLock>([&] { + return _renderWithZones; + }); + if (!renderWithZones.isEmpty()) { + if (!containingZones.empty()) { + for (auto renderWithZone : renderWithZones) { + if (containingZones.find(renderWithZone) != containingZones.end()) { + return true; + } + } + } + return false; + } + return true; +} + void EntityRenderer::render(RenderArgs* args) { if (!isValidRenderItem()) { return; @@ -373,6 +390,10 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity return true; } + if (entity->needsZoneOcclusionUpdate()) { + return true; + } + return false; } @@ -414,6 +435,10 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa _canCastShadow = entity->getCanCastShadow(); setCullWithParent(entity->getCullWithParent()); _cauterized = entity->getCauterized(); + if (entity->needsZoneOcclusionUpdate()) { + entity->resetNeedsZoneOcclusionUpdate(); + setRenderWithZones(entity->getRenderWithZones()); + } entity->setNeedsRenderUpdate(false); }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index d52ccf4a99..39c30e5883 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -65,6 +65,7 @@ public: virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; virtual Item::Bound getBound() override; + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override; protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } @@ -109,6 +110,7 @@ protected: 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& renderWithZones) { _renderWithZones = renderWithZones; } template T withReadLockResult(const std::function& f) { @@ -143,6 +145,7 @@ protected: bool _cullWithParent { false }; RenderLayer _renderLayer { RenderLayer::WORLD }; PrimitiveMode _primitiveMode { PrimitiveMode::SOLID }; + QVector _renderWithZones; bool _cauterized { false }; bool _moving { false }; Transform _renderTransform; @@ -151,6 +154,7 @@ protected: std::mutex _materialsLock; quint64 _created; + QUuid _entityID; // The base class relies on comparing the model transform to the entity transform in order // to trigger an update, so the member must not be visible to derived classes as a modifiable diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ad2737143c..fb2cf01580 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -282,7 +282,7 @@ bool RenderableModelEntityItem::findDetailedParabolaIntersection(const glm::vec3 } void RenderableModelEntityItem::fetchCollisionGeometryResource() { - _collisionGeometryResource = DependencyManager::get()->getCollisionModelResource(getCollisionShapeURL()); + _collisionGeometryResource = DependencyManager::get()->getCollisionGeometryResource(getCollisionShapeURL()); } bool RenderableModelEntityItem::unableToLoadCollisionShape() { @@ -357,6 +357,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() const { void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { const uint32_t TRIANGLE_STRIDE = 3; + const uint32_t QUAD_STRIDE = 4; ShapeType type = getShapeType(); @@ -379,35 +380,59 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); - - size_t numParts = 0; - for (const HFMMesh& mesh : collisionGeometry.meshes) { - numParts += mesh.triangleListMesh.parts.size(); - } - pointCollection.reserve(numParts); + uint32_t i = 0; // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - for (const HFMMesh& mesh : collisionGeometry.meshes) { - const hfm::TriangleListMesh& triangleListMesh = mesh.triangleListMesh; + foreach (const HFMMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull - for (const glm::ivec2& part : triangleListMesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { + pointCollection.push_back(QVector()); + ShapeInfo::PointList& pointsInPart = pointCollection[i]; + // run through all the triangles and (uniquely) add each point to the hull - - pointCollection.emplace_back(); - ShapeInfo::PointList& pointsInPart = pointCollection.back(); - - uint32_t numIndices = (uint32_t)part.y; + uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer - uint32_t indexStart = (uint32_t)part.x; - uint32_t indexEnd = indexStart + numIndices; - for (uint32_t j = indexStart; j < indexEnd; ++j) { - // NOTE: It seems odd to skip vertices when initializing a btConvexHullShape, but let's keep the behavior similar to the old behavior for now - glm::vec3 point = triangleListMesh.vertices[triangleListMesh.indices[j]]; - if (std::find(pointsInPart.cbegin(), pointsInPart.cend(), point) == pointsInPart.cend()) { - pointsInPart.push_back(point); + + for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) { + glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]]; + glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]]; + if (!pointsInPart.contains(p0)) { + pointsInPart << p0; + } + if (!pointsInPart.contains(p1)) { + pointsInPart << p1; + } + if (!pointsInPart.contains(p2)) { + pointsInPart << p2; + } + } + + // run through all the quads and (uniquely) add each point to the hull + numIndices = (uint32_t)meshPart.quadIndices.size(); + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up + //assert(numIndices % QUAD_STRIDE == 0); + numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer + + for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) { + glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]]; + glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.quadIndices[j + 2]]; + glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]]; + if (!pointsInPart.contains(p0)) { + pointsInPart << p0; + } + if (!pointsInPart.contains(p1)) { + pointsInPart << p1; + } + if (!pointsInPart.contains(p2)) { + pointsInPart << p2; + } + if (!pointsInPart.contains(p3)) { + pointsInPart << p3; } } @@ -416,6 +441,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { pointCollection.pop_back(); continue; } + ++i; } } @@ -430,8 +456,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // 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()); - for (size_t i = 0; i < pointCollection.size(); i++) { - for (size_t j = 0; j < pointCollection[i].size(); j++) { + for (int32_t i = 0; i < pointCollection.size(); i++) { + for (int32_t j = 0; j < pointCollection[i].size(); j++) { // back compensate for registration so we can apply that offset to the shapeInfo later pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + model->getOffset()) - registrationOffset; } @@ -445,63 +471,46 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { model->updateGeometry(); // compute meshPart local transforms + QVector localTransforms; const HFMModel& hfmModel = model->getHFMModel(); + int numHFMMeshes = hfmModel.meshes.size(); + int totalNumVertices = 0; glm::vec3 dimensions = getScaledDimensions(); glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); - - ShapeInfo::TriangleIndices& triangleIndices = shapeInfo.getTriangleIndices(); - triangleIndices.clear(); - - Extents extents; - int32_t shapeCount = 0; - int32_t instanceIndex = 0; - - // NOTE: Each pointCollection corresponds to a mesh. Therefore, we should have one pointCollection per mesh instance - // A mesh instance is a unique combination of mesh/transform. For every mesh instance, there are as many shapes as there are parts for that mesh. - // We assume the shapes are grouped by mesh instance, and the group contains one of each mesh part. - uint32_t numInstances = 0; - std::vector>> shapesPerInstancePerMesh; - shapesPerInstancePerMesh.resize(hfmModel.meshes.size()); - for (uint32_t shapeIndex = 0; shapeIndex < hfmModel.shapes.size();) { - const auto& shape = hfmModel.shapes[shapeIndex]; - uint32_t meshIndex = shape.mesh; - const auto& mesh = hfmModel.meshes[meshIndex]; - uint32_t numMeshParts = (uint32_t)mesh.parts.size(); - assert(numMeshParts != 0); - - auto& shapesPerInstance = shapesPerInstancePerMesh[meshIndex]; - shapesPerInstance.emplace_back(); - - auto& shapes = shapesPerInstance.back(); - shapes.resize(numMeshParts); - std::iota(shapes.begin(), shapes.end(), shapeIndex); - - shapeIndex += numMeshParts; - ++numInstances; + 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); + } else { + localTransforms.push_back(invRegistraionOffset); + } + totalNumVertices += mesh.vertices.size(); } - - const uint32_t MAX_ALLOWED_MESH_COUNT = 1000; - if (numInstances > MAX_ALLOWED_MESH_COUNT) { - // too many will cause the deadlock timer to throw... - qWarning() << "model" << getModelURL() << "has too many collision meshes" << numInstances << "and will collide as a box."; + const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; + if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) { + qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box."; shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); return; } - size_t totalNumVertices = 0; - for (const auto& shapesPerInstance : shapesPerInstancePerMesh) { - for (const auto& instanceShapes : shapesPerInstance) { - const uint32_t firstShapeIndex = instanceShapes.front(); - const auto& firstShape = hfmModel.shapes[firstShapeIndex]; - const auto& mesh = hfmModel.meshes[firstShape.mesh]; - const auto& triangleListMesh = mesh.triangleListMesh; - // Added once per instance per mesh - totalNumVertices += triangleListMesh.vertices.size(); + std::vector> meshes; + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + auto& hfmMeshes = _collisionGeometryResource->getHFMModel().meshes; + meshes.reserve(hfmMeshes.size()); + for (auto& hfmMesh : hfmMeshes) { + meshes.push_back(hfmMesh._mesh); } + } else { + meshes = model->getGeometry()->getMeshes(); } - const size_t MAX_VERTICES_PER_STATIC_MESH = 1e6; - if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) { - qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box."; + int32_t numMeshes = (int32_t)(meshes.size()); + + const int MAX_ALLOWED_MESH_COUNT = 1000; + if (numMeshes > MAX_ALLOWED_MESH_COUNT) { + // too many will cause the deadlock timer to throw... shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); return; } @@ -509,118 +518,169 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { - pointCollection.resize(numInstances); + pointCollection.resize(numMeshes); } else { pointCollection.resize(1); } - for (uint32_t meshIndex = 0; meshIndex < hfmModel.meshes.size(); ++meshIndex) { - const auto& mesh = hfmModel.meshes[meshIndex]; - const auto& triangleListMesh = mesh.triangleListMesh; - const auto& vertices = triangleListMesh.vertices; - const auto& indices = triangleListMesh.indices; - const std::vector& parts = triangleListMesh.parts; + ShapeInfo::TriangleIndices& triangleIndices = shapeInfo.getTriangleIndices(); + triangleIndices.clear(); - const auto& shapesPerInstance = shapesPerInstancePerMesh[meshIndex]; - for (const std::vector& instanceShapes : shapesPerInstance) { - ShapeInfo::PointList& points = pointCollection[instanceIndex]; + Extents extents; + int32_t meshCount = 0; + int32_t pointListIndex = 0; + for (auto& mesh : meshes) { + if (!mesh) { + continue; + } + const gpu::BufferView& vertices = mesh->getVertexBuffer(); + const gpu::BufferView& indices = mesh->getIndexBuffer(); + const gpu::BufferView& parts = mesh->getPartBuffer(); - // reserve room - int32_t sizeToReserve = (int32_t)(vertices.size()); - if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { - // a list of points for each instance - instanceIndex++; - } else { - // only one list of points - sizeToReserve += (int32_t)((gpu::Size)points.size()); - } - points.reserve(sizeToReserve); - - // get mesh instance transform - const uint32_t meshIndexOffset = (uint32_t)points.size(); - const uint32_t instanceShapeIndexForTransform = instanceShapes.front(); - const auto& instanceShapeForTransform = hfmModel.shapes[instanceShapeIndexForTransform]; - glm::mat4 localTransform; - if (instanceShapeForTransform.joint != hfm::UNDEFINED_KEY) { - auto jointMatrix = model->getRig().getJointTransform(instanceShapeForTransform.joint); - // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later - if (instanceShapeForTransform.skinDeformer != hfm::UNDEFINED_KEY) { - const auto& skinDeformer = hfmModel.skinDeformers[instanceShapeForTransform.skinDeformer]; - glm::mat4 inverseBindMatrix; - if (!skinDeformer.clusters.empty()) { - const auto& cluster = skinDeformer.clusters.back(); - inverseBindMatrix = cluster.inverseBindMatrix; - } - localTransform = invRegistraionOffset * jointMatrix * inverseBindMatrix; - } else { - localTransform = invRegistraionOffset * jointMatrix; - } - } else { - localTransform = invRegistraionOffset; - } + ShapeInfo::PointList& points = pointCollection[pointListIndex]; - // copy points - auto vertexItr = vertices.cbegin(); - while (vertexItr != vertices.cend()) { - glm::vec3 point = extractTranslation(localTransform * glm::translate(*vertexItr)); - points.push_back(point); - ++vertexItr; - } - for (const auto& instanceShapeIndex : instanceShapes) { - const auto& instanceShape = hfmModel.shapes[instanceShapeIndex]; - extents.addExtents(instanceShape.transformedExtents); - } + // reserve room + int32_t sizeToReserve = (int32_t)(vertices.getNumElements()); + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + // a list of points for each mesh + pointListIndex++; + } else { + // only one list of points + sizeToReserve += (int32_t)((gpu::Size)points.size()); + } + points.reserve(sizeToReserve); - if (type == SHAPE_TYPE_STATIC_MESH) { - // copy into triangleIndices - triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.size())); - auto partItr = parts.cbegin(); - while (partItr != parts.cend()) { - auto numIndices = partItr->y; + // copy points + uint32_t meshIndexOffset = (uint32_t)points.size(); + const glm::mat4& localTransform = localTransforms[meshCount]; + gpu::BufferView::Iterator vertexItr = vertices.cbegin(); + while (vertexItr != vertices.cend()) { + glm::vec3 point = extractTranslation(localTransform * glm::translate(*vertexItr)); + points.push_back(point); + extents.addPoint(point); + ++vertexItr; + } + + if (type == SHAPE_TYPE_STATIC_MESH) { + // copy into triangleIndices + triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); + gpu::BufferView::Iterator partItr = parts.cbegin(); + while (partItr != parts.cend()) { + auto numIndices = partItr->_numIndices; + if (partItr->_topology == graphics::Mesh::TRIANGLES) { // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer - auto indexItr = indices.cbegin() + partItr->x; + + auto indexItr = indices.cbegin() + partItr->_startIndex; auto indexEnd = indexItr + numIndices; while (indexItr != indexEnd) { triangleIndices.push_back(*indexItr + meshIndexOffset); ++indexItr; } - ++partItr; + } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { + // TODO: resurrect assert after we start sanitizing HFMMesh higher up + //assert(numIndices > 2); + + uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices; + if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) { + // we underestimated the final size of triangleIndices so we pre-emptively expand it + triangleIndices.reserve(triangleIndices.size() + approxNumIndices); + } + + auto indexItr = indices.cbegin() + partItr->_startIndex; + auto indexEnd = indexItr + (numIndices - 2); + + // first triangle uses the first three indices + triangleIndices.push_back(*(indexItr++) + meshIndexOffset); + triangleIndices.push_back(*(indexItr++) + meshIndexOffset); + triangleIndices.push_back(*(indexItr++) + meshIndexOffset); + + // the rest use previous and next index + uint32_t triangleCount = 1; + while (indexItr != indexEnd) { + if ((*indexItr) != graphics::Mesh::PRIMITIVE_RESTART_INDEX) { + if (triangleCount % 2 == 0) { + // even triangles use first two indices in order + triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); + triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset); + } else { + // odd triangles swap order of first two indices + triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset); + triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); + } + triangleIndices.push_back(*indexItr + meshIndexOffset); + ++triangleCount; + } + ++indexItr; + } } - } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { - // for each mesh copy unique part indices, separated by special bogus (flag) index values - auto partItr = parts.cbegin(); - while (partItr != parts.cend()) { - // collect unique list of indices for this part - std::set uniqueIndices; - auto numIndices = partItr->y; + ++partItr; + } + } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + // for each mesh copy unique part indices, separated by special bogus (flag) index values + gpu::BufferView::Iterator partItr = parts.cbegin(); + while (partItr != parts.cend()) { + // collect unique list of indices for this part + std::set uniqueIndices; + auto numIndices = partItr->_numIndices; + if (partItr->_topology == graphics::Mesh::TRIANGLES) { // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices% TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXSerializer - auto indexItr = indices.cbegin() + partItr->x; + + auto indexItr = indices.cbegin() + partItr->_startIndex; auto indexEnd = indexItr + numIndices; while (indexItr != indexEnd) { uniqueIndices.insert(*indexItr); ++indexItr; } + } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { + // TODO: resurrect assert after we start sanitizing HFMMesh higher up + //assert(numIndices > TRIANGLE_STRIDE - 1); - // store uniqueIndices in triangleIndices - triangleIndices.reserve(triangleIndices.size() + (int32_t)uniqueIndices.size()); - for (auto index : uniqueIndices) { - triangleIndices.push_back(index); + auto indexItr = indices.cbegin() + partItr->_startIndex; + auto indexEnd = indexItr + (numIndices - 2); + + // first triangle uses the first three indices + uniqueIndices.insert(*(indexItr++)); + uniqueIndices.insert(*(indexItr++)); + uniqueIndices.insert(*(indexItr++)); + + // the rest use previous and next index + uint32_t triangleCount = 1; + while (indexItr != indexEnd) { + if ((*indexItr) != graphics::Mesh::PRIMITIVE_RESTART_INDEX) { + if (triangleCount % 2 == 0) { + // EVEN triangles use first two indices in order + uniqueIndices.insert(*(indexItr - 2)); + uniqueIndices.insert(*(indexItr - 1)); + } else { + // ODD triangles swap order of first two indices + uniqueIndices.insert(*(indexItr - 1)); + uniqueIndices.insert(*(indexItr - 2)); + } + uniqueIndices.insert(*indexItr); + ++triangleCount; + } + ++indexItr; } - // flag end of part - triangleIndices.push_back(END_OF_MESH_PART); - - ++partItr; } - // flag end of mesh - triangleIndices.push_back(END_OF_MESH); - } - } - ++shapeCount; + // store uniqueIndices in triangleIndices + triangleIndices.reserve(triangleIndices.size() + (int32_t)uniqueIndices.size()); + for (auto index : uniqueIndices) { + triangleIndices.push_back(index); + } + // flag end of part + triangleIndices.push_back(END_OF_MESH_PART); + + ++partItr; + } + // flag end of mesh + triangleIndices.push_back(END_OF_MESH); + } + ++meshCount; } // scale and shift @@ -632,7 +692,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { } } for (auto points : pointCollection) { - for (size_t i = 0; i < points.size(); ++i) { + for (int32_t i = 0; i < points.size(); ++i) { points[i] = (points[i] * scaleToFit); } } @@ -1023,6 +1083,11 @@ uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const { return 0; } +void ModelEntityRenderer::handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) { + setBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs); +} + void ModelEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& transaction) { if (_model) { _model->removeFromScene(scene, transaction); @@ -1191,7 +1256,11 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin 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()) { @@ -1264,6 +1333,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce setKey(didVisualGeometryRequestSucceed); _model->setTagMask(getTagMask()); _model->setHifiRenderLayer(getHifiRenderLayer()); + _model->setPrimitiveMode(_primitiveMode); + _model->setCullWithParent(_cullWithParent); + _model->setRenderWithZones(_renderWithZones); emit requestRenderUpdate(); if(didVisualGeometryRequestSucceed) { emit DependencyManager::get()-> @@ -1347,6 +1419,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->setTagMask(tagMask, scene); } + if (entity->blendshapesChanged()) { + model->setBlendshapeCoefficients(entity->getBlendshapeCoefficientVector()); + model->updateBlendshapes(); + } + // TODO? early exit here when not visible? if (model->canCastShadow() != _canCastShadow) { @@ -1367,13 +1444,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->removeFromScene(scene, transaction); render::Item::Status::Getters statusGetters; makeStatusGetters(entity, statusGetters); - model->addToScene(scene, transaction, statusGetters); + using namespace std::placeholders; + model->addToScene(scene, transaction, statusGetters, std::bind(&ModelEntityRenderer::metaBlendshapeOperator, _renderItemID, _1, _2, _3, _4)); entity->bumpAncestorChainRenderableVersion(); processMaterials(); } } - if (!_texturesLoaded && model->getNetworkModel() && model->getNetworkModel()->areTexturesLoaded()) { + if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { withWriteLock([&] { _texturesLoaded = true; }); @@ -1447,6 +1525,13 @@ void ModelEntityRenderer::setCullWithParent(bool value) { } } +void ModelEntityRenderer::setRenderWithZones(const QVector& renderWithZones) { + Parent::setRenderWithZones(renderWithZones); + if (_model) { + _model->setRenderWithZones(renderWithZones); + } +} + // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items void ModelEntityRenderer::doRender(RenderArgs* args) { DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); @@ -1519,3 +1604,12 @@ void ModelEntityRenderer::processMaterials() { } } } + +void ModelEntityRenderer::metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) { + render::Transaction transaction; + transaction.updateItem(renderItemID, [blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs](PayloadProxyInterface& self) { + self.handleBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs); + }); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index eb8aeefba9..f42f0aec94 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "RenderableEntityItem.h" @@ -120,7 +121,7 @@ private: bool readyToAnimate() const; void fetchCollisionGeometryResource(); - ModelResource::Pointer _collisionGeometryResource; + GeometryResource::Pointer _collisionGeometryResource; std::vector _jointMap; QVariantMap _originalTextures; bool _jointMapCompleted { false }; @@ -131,7 +132,7 @@ private: namespace render { namespace entities { -class ModelEntityRenderer : public TypedEntityRenderer { +class ModelEntityRenderer : public TypedEntityRenderer, public MetaModelPayload { using Parent = TypedEntityRenderer; friend class EntityRenderer; Q_OBJECT @@ -155,6 +156,8 @@ protected: void setKey(bool didVisualGeometryRequestSucceed); virtual ItemKey getKey() override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override; + virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual bool needsRenderUpdate() const override; @@ -165,6 +168,7 @@ protected: void setRenderLayer(RenderLayer value) override; void setPrimitiveMode(PrimitiveMode value) override; void setCullWithParent(bool value) override; + void setRenderWithZones(const QVector& renderWithZones) override; private: void animate(const TypedEntityPointer& entity); @@ -198,6 +202,10 @@ private: bool _prevModelLoaded { false }; void processMaterials(); + + static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs); + }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index b08ef8111d..5ffb395302 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -220,7 +220,7 @@ float importanceSample3DDimension(float startDim) { } ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, - const ShapeType& shapeType, const ModelResource::Pointer& geometryResource, + const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource, const TriangleInfo& triangleInfo) { CpuParticle particle; @@ -405,7 +405,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { particle::Properties particleProperties; ShapeType shapeType; - ModelResource::Pointer geometryResource; + GeometryResource::Pointer geometryResource; withReadLock([&] { particleProperties = _particleProperties; shapeType = _shapeType; @@ -508,7 +508,7 @@ void ParticleEffectEntityRenderer::fetchGeometryResource() { if (hullURL.isEmpty()) { _geometryResource.reset(); } else { - _geometryResource = DependencyManager::get()->getCollisionModelResource(hullURL); + _geometryResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); } } @@ -516,7 +516,7 @@ void ParticleEffectEntityRenderer::fetchGeometryResource() { void ParticleEffectEntityRenderer::computeTriangles(const hfm::Model& hfmModel) { PROFILE_RANGE(render, __FUNCTION__); - uint32_t numberOfMeshes = (uint32_t)hfmModel.meshes.size(); + int numberOfMeshes = hfmModel.meshes.size(); _hasComputedTriangles = true; _triangleInfo.triangles.clear(); @@ -526,11 +526,11 @@ void ParticleEffectEntityRenderer::computeTriangles(const hfm::Model& hfmModel) float minArea = FLT_MAX; AABox bounds; - for (uint32_t i = 0; i < numberOfMeshes; i++) { + for (int i = 0; i < numberOfMeshes; i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - const uint32_t numberOfParts = (uint32_t)mesh.parts.size(); - for (uint32_t j = 0; j < numberOfParts; j++) { + const int numberOfParts = mesh.parts.size(); + for (int j = 0; j < numberOfParts; j++) { const HFMMeshPart& part = mesh.parts.at(j); const int INDICES_PER_TRIANGLE = 3; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 9e73892486..a89ab804fc 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -90,7 +90,7 @@ private: } _triangleInfo; static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, - const ShapeType& shapeType, const ModelResource::Pointer& geometryResource, + const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource, const TriangleInfo& triangleInfo); void stepSimulation(); @@ -109,7 +109,7 @@ private: QString _compoundShapeURL; void fetchGeometryResource(); - ModelResource::Pointer _geometryResource; + GeometryResource::Pointer _geometryResource; NetworkTexturePointer _networkTexture; bool _textureLoaded { false }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 5370c17275..1555604604 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1429,13 +1429,14 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { QtConcurrent::run([entity, voxelSurfaceStyle, voxelVolumeSize, mesh] { auto polyVoxEntity = std::static_pointer_cast(entity); - ShapeInfo::PointCollection pointCollection; + QVector> pointCollection; AABox box; glm::mat4 vtoM = std::static_pointer_cast(entity)->voxelToLocalMatrix(); if (voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_MARCHING_CUBES || voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { // pull each triangle in the mesh into a polyhedron which can be collided with + unsigned int i = 0; const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); @@ -1464,16 +1465,19 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { box += p2Model; box += p3Model; - ShapeInfo::PointList pointsInPart; - pointsInPart.push_back(p0Model); - pointsInPart.push_back(p1Model); - pointsInPart.push_back(p2Model); - pointsInPart.push_back(p3Model); - - // add points to a new convex hull - pointCollection.push_back(pointsInPart); + QVector pointsInPart; + pointsInPart << p0Model; + pointsInPart << p1Model; + pointsInPart << p2Model; + pointsInPart << p3Model; + // add next convex hull + QVector newMeshPoints; + pointCollection << newMeshPoints; + // add points to the new convex hull + pointCollection[i++] << pointsInPart; } } else { + unsigned int i = 0; polyVoxEntity->forEachVoxelValue(voxelVolumeSize, [&](const ivec3& v, uint8_t value) { if (value > 0) { const auto& x = v.x; @@ -1492,7 +1496,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { return; } - ShapeInfo::PointList pointsInPart; + QVector pointsInPart; float offL = -0.5f; float offH = 0.5f; @@ -1519,17 +1523,20 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { box += p110; box += p111; - pointsInPart.push_back(p000); - pointsInPart.push_back(p001); - pointsInPart.push_back(p010); - pointsInPart.push_back(p011); - pointsInPart.push_back(p100); - pointsInPart.push_back(p101); - pointsInPart.push_back(p110); - pointsInPart.push_back(p111); + pointsInPart << p000; + pointsInPart << p001; + pointsInPart << p010; + pointsInPart << p011; + pointsInPart << p100; + pointsInPart << p101; + pointsInPart << p110; + pointsInPart << p111; - // add points to a new convex hull - pointCollection.push_back(pointsInPart); + // add next convex hull + QVector newMeshPoints; + pointCollection << newMeshPoints; + // add points to the new convex hull + pointCollection[i++] << pointsInPart; } }); } @@ -1539,7 +1546,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { void RenderablePolyVoxEntityItem::setCollisionPoints(ShapeInfo::PointCollection pointCollection, AABox box) { // this catches the payload from computeShapeInfoWorker - if (pointCollection.empty()) { + if (pointCollection.isEmpty()) { EntityItem::computeShapeInfo(_shapeInfo); withWriteLock([&] { _shapeReady = true; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a5baad4841..a744fc9a62 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -288,6 +288,17 @@ ShapeKey entities::TextPayload::getShapeKey() const { return ShapeKey::Builder::invalid(); } +bool entities::TextPayload::passesZoneOcclusionTest(const std::unordered_set& containingZones) const { + auto entityTreeRenderer = DependencyManager::get(); + if (entityTreeRenderer) { + auto renderable = entityTreeRenderer->renderableForEntityId(_entityID); + if (renderable) { + return std::static_pointer_cast(renderable)->passesZoneOcclusionTest(containingZones); + } + } + return false; +} + void entities::TextPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("TextPayload::render"); Q_ASSERT(args->_batch); @@ -387,4 +398,12 @@ template <> const ShapeKey shapeGetShapeKey(const TextPayload::Pointer& payload) template <> void payloadRender(const TextPayload::Pointer& payload, RenderArgs* args) { return payload->render(args); } + +template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones) { + if (payload) { + return payload->passesZoneOcclusionTest(containingZones); + } + return false; +} + } diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 63cf3e6e9e..87102daa32 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -94,6 +94,7 @@ public: Item::Bound getBound() const; ShapeKey getShapeKey() const; void render(RenderArgs* args); + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; protected: QUuid _entityID; @@ -109,6 +110,7 @@ namespace render { template <> const Item::Bound payloadGetBound(const entities::TextPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload); template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args); + template <> bool payloadPassesZoneOcclusionTest(const entities::TextPayload::Pointer& payload, const std::unordered_set& containingZones); } #endif // hifi_RenderableTextEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index d6c675e88c..d3c9225b88 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -71,6 +71,11 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity } void ZoneEntityRenderer::doRender(RenderArgs* args) { + // This is necessary so that zones can themselves be zone culled + if (!passesZoneOcclusionTest(CullTest::_prevContainingZones)) { + return; + } + if (!_stage) { _stage = args->_scene->getStage(); assert(_stage); @@ -180,6 +185,8 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { _bloomStage->_currentFrame.pushBloom(_bloomIndex); } } + + CullTest::_containingZones.insert(_entityID); } void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 3d5cc190d0..eb46be1e20 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -19,6 +19,8 @@ <@include paintStroke.slh@> <$declarePolyLineBuffers()$> +<@include CullFace.slh@> + LAYOUT(binding=0) uniform sampler2D _texture; <@if not HIFI_USE_FORWARD@> @@ -35,9 +37,9 @@ void main(void) { <@if not HIFI_USE_FORWARD@> <@if HIFI_USE_TRANSLUCENT@> - packDeferredFragmentTranslucent((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb, DEFAULT_ROUGHNESS); + packDeferredFragmentTranslucent(evalFrontOrBackFaceNormal(_normalWS), texel.a, texel.rgb, DEFAULT_ROUGHNESS); <@else@> - packDeferredFragmentUnlit((2.0 * float(gl_FrontFacing) - 1.0) * _normalWS, texel.a, texel.rgb); + packDeferredFragmentUnlit(evalFrontOrBackFaceNormal(_normalWS), texel.a, texel.rgb); <@endif@> <@else@> _fragColor0 = texel; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b075239caf..69f1ed2aee 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -106,6 +106,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_RENDER_LAYER; requestedProperties += PROP_PRIMITIVE_MODE; requestedProperties += PROP_IGNORE_PICK_INTERSECTION; + requestedProperties += PROP_RENDER_WITH_ZONES; requestedProperties += _grabProperties.getEntityProperties(params); // Physics @@ -301,6 +302,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)getRenderLayer()); APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)getPrimitiveMode()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection()); + APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, getRenderWithZones()); withReadLock([&] { _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -872,6 +874,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_RENDER_LAYER, RenderLayer, setRenderLayer); READ_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode); READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); + READ_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); withWriteLock([&] { int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, @@ -1351,6 +1354,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderLayer, getRenderLayer); COPY_ENTITY_PROPERTY_TO_PROPERTIES(primitiveMode, getPrimitiveMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderWithZones, getRenderWithZones); withReadLock([&] { _grabProperties.getProperties(properties); }); @@ -1500,6 +1504,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderLayer, setRenderLayer); SET_ENTITY_PROPERTY_FROM_PROPERTIES(primitiveMode, setPrimitiveMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderWithZones, setRenderWithZones); withWriteLock([&] { bool grabPropertiesChanged = _grabProperties.setProperties(properties); somethingChanged |= grabPropertiesChanged; @@ -3570,3 +3575,18 @@ void EntityItem::disableGrab(GrabPointer grab) { } } } + +void EntityItem::setRenderWithZones(const QVector& renderWithZones) { + withWriteLock([&] { + if (_renderWithZones != renderWithZones) { + _needsZoneOcclusionUpdate = true; + _renderWithZones = renderWithZones; + } + }); +} + +QVector EntityItem::getRenderWithZones() const { + return resultWithReadLock>([&] { + return _renderWithZones; + }); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index cb0beb601b..2a6952fc0d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -582,6 +582,11 @@ public: bool needsRenderUpdate() const { return _needsRenderUpdate; } void setNeedsRenderUpdate(bool needsRenderUpdate) { _needsRenderUpdate = needsRenderUpdate; } + void setRenderWithZones(const QVector& renderWithZones); + QVector getRenderWithZones() const; + bool needsZoneOcclusionUpdate() const { return _needsZoneOcclusionUpdate; } + void resetNeedsZoneOcclusionUpdate() { withWriteLock([&] { _needsZoneOcclusionUpdate = false; }); } + signals: void spaceUpdate(std::pair data); @@ -770,6 +775,9 @@ protected: QHash _grabActions; + QVector _renderWithZones; + mutable bool _needsZoneOcclusionUpdate { false }; + bool _cullWithParent { false }; mutable bool _needsRenderUpdate { false }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c81cd2a70a..d671d46c22 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -431,6 +431,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RENDER_LAYER, renderLayer); CHECK_PROPERTY_CHANGE(PROP_PRIMITIVE_MODE, primitiveMode); CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); + CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); changedProperties += _grab.getChangedProperties(); // Physics @@ -535,6 +536,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_JOINT_TRANSLATIONS, jointTranslations); CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled); + CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); changedProperties += _animation.getChangedProperties(); // Light @@ -642,33 +644,33 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { } /**jsdoc - * Different entity types have different properties: some common to all entities (listed in the table) and some specific to + * Different entity types have different properties: some common to all entities (listed in the table) and some specific to * each {@link Entities.EntityType|EntityType} (linked to below). * * @typedef {object} Entities.EntityProperties * @property {Uuid} id - The ID of the entity. Read-only. * @property {string} name="" - A name for the entity. Need not be unique. - * @property {Entities.EntityType} type - The entity's type. You cannot change the type of an entity after it's created. - * However, its value may switch among "Box", "Shape", and "Sphere" depending on + * @property {Entities.EntityType} type - The entity's type. You cannot change the type of an entity after it's created. + * However, its value may switch among "Box", "Shape", and "Sphere" depending on * changes to the shape property set for entities of these types. Read-only. * * @property {Entities.EntityHostType} entityHostType="domain" - How the entity is hosted and sent to others for display. * The value can only be set at entity creation by one of the {@link Entities.addEntity} methods. Read-only. - * @property {boolean} avatarEntity=false - true if the entity is an {@link Entities.EntityHostType|avatar entity}, - * false if it isn't. The value is per the entityHostType property value, set at entity creation + * @property {boolean} avatarEntity=false - true if the entity is an {@link Entities.EntityHostType|avatar entity}, + * false if it isn't. The value is per the entityHostType property value, set at entity creation * by one of the {@link Entities.addEntity} methods. Read-only. * @property {boolean} clientOnly=false - A synonym for avatarEntity. Read-only. - * @property {boolean} localEntity=false - true if the entity is a {@link Entities.EntityHostType|local entity}, - * false if it isn't. The value is per the entityHostType property value, set at entity creation + * @property {boolean} localEntity=false - true if the entity is a {@link Entities.EntityHostType|local entity}, + * false if it isn't. The value is per the entityHostType property value, set at entity creation * by one of the {@link Entities.addEntity} methods. Read-only. * - * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is + * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is * true, otherwise {@link Uuid(0)|Uuid.NULL}. Read-only. * * @property {string} created - The UTC date and time that the entity was created, in ISO 8601 format as * yyyy-MM-ddTHH:mm:ssZ. Read-only. * @property {number} age - The age of the entity in seconds since it was created. Read-only. - * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s + * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s * seconds. * @property {number} lifetime=-1 - How long an entity lives for, in seconds, before being automatically deleted. A value of * -1 means that the entity lives for ever. @@ -677,133 +679,137 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Uuid} lastEditedBy - The session ID of the avatar or agent that most recently created or edited the entity. * Read-only. * - * @property {boolean} locked=false - true if properties other than locked cannot be changed and the + * @property {boolean} locked=false - true if properties other than locked cannot be changed and the * entity cannot be deleted, false if all properties can be changed and the entity can be deleted. * @property {boolean} visible=true - true if the entity is rendered, false if it isn't. - * @property {boolean} canCastShadow=true - true if the entity can cast a shadow, false if it can't. - * Currently applicable only to {@link Entities.EntityProperties-Model|Model} and - * {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a - * {@link Entities.EntityProperties-Zone|Zone} entity with castShadows enabled in its keyLight + * @property {boolean} canCastShadow=true - true if the entity can cast a shadow, false if it can't. + * Currently applicable only to {@link Entities.EntityProperties-Model|Model} and + * {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a + * {@link Entities.EntityProperties-Zone|Zone} entity with castShadows enabled in its keyLight * property. - * @property {boolean} isVisibleInSecondaryCamera=true - true if the entity is rendered in the secondary camera, + * @property {boolean} isVisibleInSecondaryCamera=true - true if the entity is rendered in the secondary camera, * false if it isn't. * @property {Entities.RenderLayer} renderLayer="world" - The layer that the entity renders in. * @property {Entities.PrimitiveMode} primitiveMode="solid" - How the entity's geometry is rendered. - * @property {boolean} ignorePickIntersection=false - true if {@link Picks} and {@link RayPick} ignore the entity, + * @property {boolean} ignorePickIntersection=false - true if {@link Picks} and {@link RayPick} ignore the entity, * false if they don't. * * @property {Vec3} position=0,0,0 - The position of the entity in world coordinates. * @property {Quat} rotation=0,0,0,1 - The orientation of the entity in world coordinates. - * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated - * about, range {@link Vec3(0)|Vec3.ZERO} – {@link Vec3(0)|Vec3.ONE}. A value of {@link Vec3(0)|Vec3.ZERO} is the + * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated + * about, range {@link Vec3(0)|Vec3.ZERO} – {@link Vec3(0)|Vec3.ONE}. A value of {@link Vec3(0)|Vec3.ZERO} is the * entity's minimum x, y, z corner; a value of {@link Vec3(0)|Vec3.ONE} is the entity's maximum x, y, z corner. * * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise * {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise + * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise * {@link Vec3(0)|Vec3.ONE}. Read-only. * * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. - * @property {number} damping=0.39347 - How much the linear velocity of an entity slows down over time, range - * 0.01.0. A higher damping value slows down the entity more quickly. The default value - * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * @property {number} damping=0.39347 - How much the linear velocity of an entity slows down over time, range + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 * of its initial value. * @property {Vec3} angularVelocity=0,0,0 - The angular velocity of the entity in rad/s with respect to its axes, about its * registration point. - * @property {number} angularDamping=0.39347 - How much the angular velocity of an entity slows down over time, range - * 0.01.0. A higher damping value slows down the entity more quickly. The default value - * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * @property {number} angularDamping=0.39347 - How much the angular velocity of an entity slows down over time, range + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 * of its initial value. * - * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in - * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied + * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in + * world coordinates. Use a value of { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied * to an entity's motion only if its dynamic property is true. - *

If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small + *

If changing an entity's gravity from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small * velocity in order to kick off physics simulation.

* @property {Vec3} acceleration - The current, measured acceleration of the entity, in m/s2. *

Deprecated: This property is deprecated and will be removed.

- * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – + * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range 0.0 – * 0.99. The higher the value, the more bouncy. - * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 - * – 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, + * @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range 0.0 + * – 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, * 0.9 for sandpaper. - * @property {number} density=1000 - The density of the entity in kg/m3, range 100 – - * 10000. Examples: 100 for balsa wood, 10000 for silver. The density is used in + * @property {number} density=1000 - The density of the entity in kg/m3, range 100 – + * 10000. Examples: 100 for balsa wood, 10000 for silver. The density is used in * conjunction with the entity's bounding box volume to work out its mass in the application of physics. * - * @property {boolean} collisionless=false - true if the entity shouldn't collide, false if it + * @property {boolean} collisionless=false - true if the entity shouldn't collide, false if it * collides with items per its collisionMask property. * @property {boolean} ignoreForCollisions - Synonym for collisionless. * @property {CollisionMask} collisionMask=31 - What types of items the entity should collide with. * @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for collisionMask, * in text format. - * @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file + * @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file * formats are per {@link SoundObject}. - * @property {boolean} dynamic=false - true if the entity's movement is affected by collisions, false - * if it isn't. + * @property {boolean} dynamic=false - true if the entity's movement is affected by collisions, false + * if it isn't. * @property {boolean} collisionsWillMove - A synonym for dynamic. * * @property {string} href="" - A "hifi://" metaverse address that a user is teleported to when they click on the entity. * @property {string} description="" - A description of the href property value. * - * @property {string} userData="" - Used to store extra data about the entity in JSON format. - *

Warning: Other apps may also use this property, so make sure you handle data stored by other apps: - * edit only your bit and leave the rest of the data intact. You can use JSON.parse() to parse the string into - * a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert the + * @property {string} userData="" - Used to store extra data about the entity in JSON format. + *

Warning: Other apps may also use this property, so make sure you handle data stored by other apps: + * edit only your bit and leave the rest of the data intact. You can use JSON.parse() to parse the string into + * a JavaScript object which you can manipulate the properties of, and use JSON.stringify() to convert the * object into a string to put back in the property.

* - * @property {string} privateUserData="" - Like userData, but only accessible by server entity scripts, assignment + * @property {string} privateUserData="" - Like userData, but only accessible by server entity scripts, assignment * client scripts, and users who have "Can Get and Set Private User Data" permissions in the domain. * * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. - * @property {number} scriptTimestamp=0 - Used to indicate when the client entity script was loaded. Should be - * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). - * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" + * @property {number} scriptTimestamp=0 - Used to indicate when the client entity script was loaded. Should be + * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). + * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" * button beside the "script URL" field in properties tab of the Create app works. * @property {string} serverScripts="" - The URL of the server entity script, if any, that is attached to the entity. * - * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of + * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of * {@link Uuid(0)|Uuid.NULL} is used if the entity is not parented. - * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use + * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a joint. - * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, - * otherwise the same value as position. If the entity is parented to an avatar and is an avatar entity + * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, + * otherwise the same value as position. If the entity is parented to an avatar and is an avatar entity * so that it scales with the avatar, this value remains the original local position value while the avatar scale changes. - * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, + * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, * otherwise the same value as rotation. - * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, + * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, * otherwise the same value as velocity. - * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is + * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is * parented, otherwise the same value as angularVelocity. * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is an - * avatar entity so that it scales with the avatar, this value remains the original dimensions value while the + * avatar entity so that it scales with the avatar, this value remains the original dimensions value while the * avatar scale changes. * - * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. + * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. * Read-only. - * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. - * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: - * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce + * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. + * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: + * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce * unnecessary entity server updates. Scripts should not change this property's value. * * @property {string} actionData="" - Base-64 encoded compressed dump of the actions associated with the entity. This property * is typically not used in scripts directly; rather, functions that manipulate an entity's actions update it, e.g., * {@link Entities.addAction}. The size of this property increases with the number of actions. Because this property value - * has to fit within a High Fidelity datagram packet, there is a limit to the number of actions that an entity can have; + * has to fit within a Vircadia datagram packet, there is a limit to the number of actions that an entity can have; * edits which would result in overflow are rejected. Read-only. - * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only + * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only * provided for Model entities. Read-only. * - * @property {boolean} cloneable=false - true if the domain or avatar entity can be cloned via + * @property {boolean} cloneable=false - true if the domain or avatar entity can be cloned via * {@link Entities.cloneEntity}, false if it can't be. * @property {number} cloneLifetime=300 - The entity lifetime for clones created from this entity. * @property {number} cloneLimit=0 - The total number of clones of this entity that can exist in the domain at any given time. - * @property {boolean} cloneDynamic=false - true if clones created from this entity will have their + * @property {boolean} cloneDynamic=false - true if clones created from this entity will have their * dynamic property set to true, false if they won't. - * @property {boolean} cloneAvatarEntity=false - true if clones created from this entity will be created as + * @property {boolean} cloneAvatarEntity=false - true if clones created from this entity will be created as * avatar entities, false if they won't be. * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. * + * @property {Uuid[]} renderWithZones=[]] - A list of entity IDs representing with which zones this entity should render. + * If it is empty, this entity will render normally. Otherwise, this entity will only render if your avatar is within + * one of the zones in this list. + * * @property {Entities.Grab} grab - The entity's grab-related properties. * * @property {string} itemName="" - Certifiable name of the Marketplace item. @@ -811,12 +817,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {string} itemCategories="" - Certifiable category of the Marketplace item. * @property {string} itemArtist="" - Certifiable artist that created the Marketplace item. * @property {string} itemLicense="" - Certifiable license URL for the Marketplace item. - * @property {number} limitedRun=4294967295 - Certifiable maximum integer number of editions (copies) of the Marketplace item + * @property {number} limitedRun=4294967295 - Certifiable maximum integer number of editions (copies) of the Marketplace item * allowed to be sold. - * @property {number} editionNumber=0 - Certifiable integer edition (copy) number or the Marketplace item. Each copy sold in + * @property {number} editionNumber=0 - Certifiable integer edition (copy) number or the Marketplace item. Each copy sold in * the Marketplace is numbered sequentially, starting at 1. - * @property {number} entityInstanceNumber=0 - Certifiable integer instance number for identical entities in a Marketplace - * item. A Marketplace item may have multiple, identical parts. If so, then each is numbered sequentially with an instance + * @property {number} entityInstanceNumber=0 - Certifiable integer instance number for identical entities in a Marketplace + * item. A Marketplace item may have multiple, identical parts. If so, then each is numbered sequentially with an instance * number. * @property {string} marketplaceID="" - Certifiable UUID for the Marketplace item, as used in the URL of the item's download * and its Marketplace Web page. @@ -845,8 +851,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Box" {@link Entities.EntityType|EntityType} is the same as the "Shape" * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Cube" - * when the entity is created. If its shape property value is subsequently changed then the entity's - * type will be reported as "Sphere" if the shape is set to "Sphere", + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Sphere" if the shape is set to "Sphere", * otherwise it will be reported as "Shape". * * @typedef {object} Entities.EntityProperties-Box @@ -854,11 +860,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. It has properties in addition + * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. It has properties in addition * to the common {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Light - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Surfaces outside these dimensions are not lit + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Surfaces outside these dimensions are not lit * by the light. * @property {Color} color=255,255,255 - The color of the light emitted. * @property {number} intensity=1 - The brightness of the light. @@ -911,46 +917,46 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Material" {@link Entities.EntityType|EntityType} modifies existing materials on entities and avatars. It + * The "Material" {@link Entities.EntityType|EntityType} modifies existing materials on entities and avatars. It * has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. *

To apply a material to an entity, set the material entity's parentID property to the entity ID. * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. - * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity - * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar" and set the - * entity's parentID property to MyAvatar.SELF_ID. + * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity + * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar" and set the + * entity's parentID property to MyAvatar.SELF_ID. * Material entities render as non-scalable spheres if they don't have their parent set.

* * @typedef {object} Entities.EntityProperties-Material * @property {Vec3} dimensions=0.1,0.1,0.1 - Used when materialMappingMode == "projected". - * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. If you append - * "#name" to the URL, the material with that name in the {@link Entities.MaterialResource|MaterialResource} - * will be applied to the entity. Alternatively, set the property value to "materialData" to use the + * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. If you append + * "#name" to the URL, the material with that name in the {@link Entities.MaterialResource|MaterialResource} + * will be applied to the entity. Alternatively, set the property value to "materialData" to use the * materialData property for the {@link Entities.MaterialResource|MaterialResource} values. - * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. - * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the + * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. + * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the * properties of, and use JSON.stringify() to convert the object into a string to put in the property. * @property {number} priority=0 - The priority for applying the material to its parent. Only the highest priority material is - * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of + * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of * 0. * @property {string} parentMaterialName="0" - Selects the mesh part or parts within the parent to which to apply the material. * If in the format "mat::string", all mesh parts with material name "string" are replaced. - * If "all", then all mesh parts are replaced. - * Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify. - *

If the string represents an array (starts with "[" and ends with "]"), the string is split - * at each "," and each element parsed as either a number or a string if it starts with "mat::". - * For example, "[0,1,mat::string,mat::string2]" will replace mesh parts 0 and 1, and any mesh parts with - * material "string" or "string2". Do not put spaces around the commas. Invalid values are parsed + * If "all", then all mesh parts are replaced. + * Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify. + *

If the string represents an array (starts with "[" and ends with "]"), the string is split + * at each "," and each element parsed as either a number or a string if it starts with "mat::". + * For example, "[0,1,mat::string,mat::string2]" will replace mesh parts 0 and 1, and any mesh parts with + * material "string" or "string2". Do not put spaces around the commas. Invalid values are parsed * to 0.

- * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or - * "projected". In "uv" mode, the material is evaluated within the UV space of the mesh it is - * applied to. In "projected" mode, the 3D transform (position, rotation, and dimensions) of the Material + * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or + * "projected". In "uv" mode, the material is evaluated within the UV space of the mesh it is + * applied to. In "projected" mode, the 3D transform (position, rotation, and dimensions) of the Material * entity is used to evaluate the texture coordinates for the material. - * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range + * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range * { x: 0, y: 0 }{ x: 1, y: 1 }. * @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space. * @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees. - * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If - * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and + * @property {boolean} materialRepeat=true - true if the material repeats, false if it doesn't. If + * false, fragments outside of texCoord 0 – 1 will be discarded. Works in both "uv" and * "projected" modes. * @example
* var entityID = Entities.addEntity({ @@ -977,27 +983,30 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { */ /**jsdoc - * The "Model" {@link Entities.EntityType|EntityType} displays a glTF, FBX, or OBJ model. When adding an entity, - * if no dimensions value is specified then the model is automatically sized to its - * {@link Entities.EntityProperties|naturalDimensions}. It has properties in addition to the common + * The "Model" {@link Entities.EntityType|EntityType} displays a glTF, FBX, or OBJ model. When adding an entity, + * if no dimensions value is specified then the model is automatically sized to its + * {@link Entities.EntityProperties|naturalDimensions}. It has properties in addition to the common * {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Model - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions - * value is specified then the model is automatically sized to its + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions + * value is specified then the model is automatically sized to its * {@link Entities.EntityProperties|naturalDimensions}. - * @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format - * (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be + * @property {string} modelURL="" - The URL of the glTF, FBX, or OBJ model. glTF models may be in JSON or binary format + * (".gltf" or ".glb" URLs respectively). Baked models' URLs have ".baked" before the file type. Model files may also be * compressed in GZ format, in which case the URL ends in ".gz". * @property {Vec3} modelScale - The scale factor applied to the model's dimensions. *

Deprecated: This property is deprecated and will be removed.

+ * @property {string} blendshapeCoefficients - A JSON string of a map of blendshape names to values. Only stores set values. + * When editing this property, only coefficients that you are editing will change; it will not explicitly reset other + * coefficients. * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the - * model's original textures. Use a texture name from the originalTextures property to override that texture. - * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no - * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON + * model's original textures. Use a texture name from the originalTextures property to override that texture. + * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no + * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON * string. - * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is - * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to + * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is + * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to * parse the JSON string into a JavaScript object of name, URL pairs. Read-only. * @property {Color} color=255,255,255 - Currently not used. * @@ -1007,28 +1016,28 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Entities.AnimationProperties} animation - An animation to play on the model. * - * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the - * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to + * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the + * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to * each joint's parent. - *

Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or - * by setting the value of this property. If you set a joint rotation using this property, you also need to set the + *

Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or + * by setting the value of this property. If you set a joint rotation using this property, you also need to set the * corresponding jointRotationsSet value to true.

- * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, - * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per * {@link Entities.getJointIndex|getJointIndex}. - * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or - * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Translations are + * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or + * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Translations are * relative to each joint's parent. - *

Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar - * functions, or by setting the value of this property. If you set a joint translation using this property you also need to + *

Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar + * functions, or by setting the value of this property. If you set a joint translation using this property you also need to * set the corresponding jointTranslationsSet value to true.

- * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, - * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per * {@link Entities.getJointIndex|getJointIndex}. - * @property {boolean} relayParentJoints=false - true if when the entity is parented to an avatar, the avatar's - * joint rotations are applied to the entity's joints; false if a parent avatar's joint rotations are not + * @property {boolean} relayParentJoints=false - true if when the entity is parented to an avatar, the avatar's + * joint rotations are applied to the entity's joints; false if a parent avatar's joint rotations are not * applied to the entity's joints. - * @property {boolean} groupCulled=false - true if the mesh parts of the model are LOD culled as a group, + * @property {boolean} groupCulled=false - true if the mesh parts of the model are LOD culled as a group, * false if separate mesh parts are LOD culled individually. * * @example
@@ -1049,15 +1058,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-ParticleEffect * @property {boolean} isEmitting=true - true if particles are being emitted, false if they aren't. - * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if + * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if * necessary when new ones are created. * @property {number} lifespan=3s - How long, in seconds, each particle lives. * @property {number} emitRate=15 - The number of particles per second to emit. * @property {number} emitSpeed=5 - The speed, in m/s, that each particle is emitted at. - * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. For example, if - * emitSpeed == 5 and speedSpread == 1, particles will be emitted with speeds in the range + * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. For example, if + * emitSpeed == 5 and speedSpread == 1, particles will be emitted with speeds in the range * 46m/s. - * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The + * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The * default is Earth's gravity value. * @property {Vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. For example, if * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == @@ -1069,33 +1078,33 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * false if they stay within the entity's dimensions. * * @property {Quat} emitOrientation=-0.707,0,0,0.707 - The orientation of particle emission relative to the entity's axes. By - * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish - * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., + * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish + * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., * the particles emit vertically. * * @property {ShapeType} shapeType="ellipsoid" - The shape from which particles are emitted. - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType == + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType == * "compound". * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. * @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted; * range 0.01.0 for the center to the surface, respectively. - * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the + * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the * shape's surface. - * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted - * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted + * within the shape; range 0Math.PI. Particles are emitted from the portion of the * shape that lies between polarStart and polarFinish. Only used if shapeType is * "ellipsoid" or "sphere". - * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted - * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted + * within the shape; range 0Math.PI. Particles are emitted from the portion of the * shape that lies between polarStart and polarFinish. Only used if shapeType is * "ellipsoid" or "sphere". - * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local - * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local + * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. * Only used if shapeType is "ellipsoid", "sphere", or "circle". * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. * Only used if shapeType is "ellipsoid", "sphere", or "circle". * * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, @@ -1105,40 +1114,40 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * particleRadius value is used. * @property {number} radiusFinish=null - The radius of each particle at the end of its life. If null, the * particleRadius value is used. - * @property {number} radiusSpread=0 - The spread in radius that each particle is given. For example, if - * particleRadius == 0.5 and radiusSpread == 0.25, each particle will have a radius in the range + * @property {number} radiusSpread=0 - The spread in radius that each particle is given. For example, if + * particleRadius == 0.5 and radiusSpread == 0.25, each particle will have a radius in the range * 0.250.75. * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. - * @property {ColorFloat} colorStart=null,null,null - The color of each particle at the start of its life. If any of the + * @property {ColorFloat} colorStart=null,null,null - The color of each particle at the start of its life. If any of the * component values are undefined, the color value is used. - * @property {ColorFloat} colorFinish=null,null,null - The color of each particle at the end of its life. If any of the + * @property {ColorFloat} colorFinish=null,null,null - The color of each particle at the end of its life. If any of the * component values are undefined, the color value is used. * @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. For example, if * color == {red: 100, green: 100, blue: 100} and colorSpread == - * {red: 10, green: 25, blue: 50}, each particle will have a color in the range + * {red: 10, green: 25, blue: 50}, each particle will have a color in the range * {red: 90, green: 75, blue: 50}{red: 110, green: 125, blue: 150}. * @property {number} alpha=1 - The opacity of each particle at the middle of its life. * @property {number} alphaStart=null - The opacity of each particle at the start of its life. If null, the * alpha value is used. * @property {number} alphaFinish=null - The opacity of each particle at the end of its life. If null, the * alpha value is used. - * @property {number} alphaSpread=0 - The spread in alpha that each particle is given. For example, if - * alpha == 0.5 and alphaSpread == 0.25, each particle will have an alpha in the range + * @property {number} alphaSpread=0 - The spread in alpha that each particle is given. For example, if + * alpha == 0.5 and alphaSpread == 0.25, each particle will have an alpha in the range * 0.250.75. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

- * @property {number} particleSpin=0 - The rotation of each particle at the middle of its life, range -2 * Math.PI + * @property {number} particleSpin=0 - The rotation of each particle at the middle of its life, range -2 * Math.PI * – 2 * Math.PI radians. - * @property {number} spinStart=null - The rotation of each particle at the start of its life, range -2 * Math.PI + * @property {number} spinStart=null - The rotation of each particle at the start of its life, range -2 * Math.PI * – 2 * Math.PI radians. If null, the particleSpin value is used. - * @property {number} spinFinish=null - The rotation of each particle at the end of its life, range -2 * Math.PI + * @property {number} spinFinish=null - The rotation of each particle at the end of its life, range -2 * Math.PI * – 2 * Math.PI radians. If null, the particleSpin value is used. - * @property {number} spinSpread=0 - The spread in spin that each particle is given, range 0 – - * 2 * Math.PI radians. For example, if particleSpin == Math.PI and - * spinSpread == Math.PI / 2, each particle will have a rotation in the range Math.PI / 2 – + * @property {number} spinSpread=0 - The spread in spin that each particle is given, range 0 – + * 2 * Math.PI radians. For example, if particleSpin == Math.PI and + * spinSpread == Math.PI / 2, each particle will have a rotation in the range Math.PI / 2 – * 3 * Math.PI / 2. - * @property {boolean} rotateWithEntity=false - true if the particles' rotations are relative to the entity's - * instantaneous rotation, false if they're relative to world coordinates. If true with + * @property {boolean} rotateWithEntity=false - true if the particles' rotations are relative to the entity's + * instantaneous rotation, false if they're relative to world coordinates. If true with * particleSpin == 0, the particles keep oriented per the entity's orientation. * * @example
@@ -1164,27 +1173,27 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-PolyLine - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity, i.e., the size of the bounding box that contains the + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity, i.e., the size of the bounding box that contains the * lines drawn. Read-only. * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's * position. A maximum of 70 points can be specified. - * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are + * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are * relative to the entity's orientation. Must be specified in order for the entity to render. - * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in + * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in * order for the entity to render. - * @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range 0.0,0.0,0.0 - * – 1.0,1.0,1.0. These colors are multiplied with the color of the texture. If there are more line + * @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range 0.0,0.0,0.0 + * – 1.0,1.0,1.0. These colors are multiplied with the color of the texture. If there are more line * points than stroke colors, the color property value is used for the remaining points. *

Warning: The ordinate values are in the range 0.01.0.

- * @property {Color} color=255,255,255 - Used as the color for each point if strokeColors doesn't have a value for + * @property {Color} color=255,255,255 - Used as the color for each point if strokeColors doesn't have a value for * the point. * @property {string} textures="" - The URL of a JPG or PNG texture to use for the lines. If you want transparency, use PNG * format. - * @property {boolean} isUVModeStretch=true - true if the texture is stretched to fill the whole line, + * @property {boolean} isUVModeStretch=true - true if the texture is stretched to fill the whole line, * false if the texture repeats along the line. - * @property {boolean} glow=false - true if the opacity of the strokes drops off away from the line center, + * @property {boolean} glow=false - true if the opacity of the strokes drops off away from the line center, * false if it doesn't. - * @property {boolean} faceCamera=false - true if each line segment rotates to face the camera, false + * @property {boolean} faceCamera=false - true if each line segment rotates to face the camera, false * if they don't. * @example
* var entity = Entities.addEntity({ @@ -1219,34 +1228,34 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-PolyVox * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. - * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range - * 1,1,1 to 128,128,128. The dimensions of each voxel is + * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range + * 1,1,1 to 128,128,128. The dimensions of each voxel is * dimensions / voxelVolumesize. - * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of - * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox + * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of + * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox * entity update it. *

The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how - * the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet, + * the particular entity's voxels compress. Because this property value has to fit within a Vircadia datagram packet, * there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.

- * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how + * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how * neighboring PolyVox entities are joined. - * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. + * @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis. * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {string} yTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local y-axis. + * @property {string} yTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local y-axis. * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {string} zTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. + * @property {string} zTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local z-axis. * JPG or PNG format. If no texture is specified the surfaces display white. - * @property {Uuid} xNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local x-axis + * @property {Uuid} xNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local x-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} yNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local y-axis + * @property {Uuid} yNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local y-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} zNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local z-axis + * @property {Uuid} zNNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's -ve local z-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} xPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local x-axis + * @property {Uuid} xPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local x-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} yPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local y-axis + * @property {Uuid} yPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local y-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. - * @property {Uuid} zPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local z-axis + * @property {Uuid} zPNeighborID=Uuid.NULL - The ID of the neighboring PolyVox entity in the entity's +ve local z-axis * direction, if you want them joined. Set to {@link Uuid(0)|Uuid.NULL} if there is none or you don't want to join them. * @example
* var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); @@ -1287,8 +1296,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" - * when the entity is created. If its shape property value is subsequently changed then the entity's - * type will be reported as "Box" if the shape is set to "Cube", + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Box" if the shape is set to "Cube", * otherwise it will be reported as "Shape". * * @typedef {object} Entities.EntityProperties-Sphere @@ -1314,7 +1323,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} rightMargin=0.0 - The right margin, in meters. * @property {number} topMargin=0.0 - The top margin, in meters. * @property {number} bottomMargin=0.0 - The bottom margin, in meters. - * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit + * @property {boolean} unlit=false - true if the entity is unaffected by lighting, false if it is lit * by the key light and local lights. * @property {string} font="" - The font to render the text with. It can be one of the following: "Courier", * "Inconsolata", "Roboto", "Timeless", or a path to a .sdff file. @@ -1322,10 +1331,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Color} textEffectColor=255,255,255 - The color of the effect. * @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range 0.00.5. * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * @property {boolean} isFacingAvatar - true if billboardMode is "full", * false if it isn't. Setting this property to false sets the billboardMode to * "none". *

Deprecated: This property is deprecated and will be removed.

@@ -1343,41 +1352,41 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Web" {@link Entities.EntityType|EntityType} displays a browsable web page. Each user views their own copy - * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being - * played, users don't see it in sync. It has properties in addition to the common + * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being + * played, users don't see it in sync. It has properties in addition to the common * {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Web * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {string} sourceUrl="" - The URL of the web page to display. This value does not change as you or others navigate * on the Web entity. - * @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel - * colors on the web page are multiplied by the property color. For example, a value of + * @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel + * colors on the web page are multiplied by the property color. For example, a value of * { red: 255, green: 0, blue: 0 } lets only the red channel of pixels' colors through. * @property {number} alpha=1 - The opacity of the web surface. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * @property {boolean} isFacingAvatar - true if billboardMode is "full", * false if it isn't. Setting this property to false sets the billboardMode to * "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter - * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value + * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter + * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value * you get the resolution in pixels. * @property {string} scriptURL="" - The URL of a JavaScript file to inject into the web page. * @property {number} maxFPS=10 - The maximum update rate for the web content, in frames/second. * @property {WebInputMode} inputMode="touch" - The user input mode to use. - * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard + * @property {boolean} showKeyboardFocusHighlight=true - true if the entity is highlighted when it has keyboard * focus, false if it isn't. * @example
* var METERS_TO_INCHES = 39.3701; * var entity = Entities.addEntity({ * type: "Web", - * sourceUrl: "https://projectathena.io/", + * sourceUrl: "https://vircadia.com/", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), * rotation: MyAvatar.orientation, * dimensions: { @@ -1392,17 +1401,17 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /**jsdoc * The "Zone" {@link Entities.EntityType|EntityType} is a volume of lighting effects and avatar permissions. - * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. It has properties in + * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. It has properties in * addition to the common {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Zone - * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the volume in which the zone's lighting effects and avatar + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the volume in which the zone's lighting effects and avatar * permissions have effect. * * @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar - * permissions have effect. Reverts to the default value if set to "none", or set to "compound" + * permissions have effect. Reverts to the default value if set to "none", or set to "compound" * and compoundShapeURL is "". - * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is * "compound". * * @property {Entities.ComponentMode} keyLightMode="inherit" - Configures the key light in the zone. @@ -1420,14 +1429,14 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone. * @property {Entities.Bloom} bloom - The bloom properties of the zone. * - * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they + * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they * cannot. Only works for domain entities. - * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not - * collide with content in the zone; false if visitors will always collide with content in the zone. Only + * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not + * collide with content in the zone; false if visitors will always collide with content in the zone. Only * works for domain entities. * - * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the - * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to + * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the + * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to * certain properties: *
  * function filter(properties) {
@@ -1437,7 +1446,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
  * }
  * 
* - * @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the + * @property {Entities.AvatarPriorityMode} avatarPriority="inherit" - Configures the priority of updates from avatars in the * zone to other clients. * * @property {Entities.ScreenshareMode} screenshare="inherit" - Configures a zone for screen-sharing. @@ -1463,21 +1472,21 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @typedef {object} Entities.EntityProperties-Image * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {string} imageURL="" - The URL of the image to use. - * @property {boolean} emissive=false - true if the image should be emissive (unlit), false if it + * @property {boolean} emissive=false - true if the image should be emissive (unlit), false if it * shouldn't. - * @property {boolean} keepAspectRatio=true - true if the image should maintain its aspect ratio, + * @property {boolean} keepAspectRatio=true - true if the image should maintain its aspect ratio, * false if it shouldn't. - * @property {Rect} subImage=0,0,0,0 - The portion of the image to display. If width or height are 0, it defaults + * @property {Rect} subImage=0,0,0,0 - The portion of the image to display. If width or height are 0, it defaults * to the full image in that dimension. * @property {Color} color=255,255,255 - The color of the image. * @property {number} alpha=1 - The opacity of the image. * @property {Entities.Pulse} pulse - Color and alpha pulse. *

Deprecated: This property is deprecated and will be removed.

* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. - * @property {boolean} faceCamera - true if billboardMode is "yaw", false + * @property {boolean} faceCamera - true if billboardMode is "yaw", false * if it isn't. Setting this property to false sets the billboardMode to "none". *

Deprecated: This property is deprecated and will be removed.

- * @property {boolean} isFacingAvatar - true if billboardMode is "full", + * @property {boolean} isFacingAvatar - true if billboardMode is "full", * false if it isn't. Setting this property to false sets the billboardMode to * "none". *

Deprecated: This property is deprecated and will be removed.

@@ -1596,6 +1605,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_RENDER_LAYER, renderLayer, getRenderLayerAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PRIMITIVE_MODE, primitiveMode, getPrimitiveModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RENDER_WITH_ZONES, renderWithZones); _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); // Physics @@ -1717,6 +1727,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients); if (!psuedoPropertyFlagsButDesiredEmpty) { _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); } @@ -1936,13 +1947,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool QScriptValue renderInfo = engine->newObject(); /**jsdoc - * Information on how an entity is rendered. Properties are only filled in for Model entities; other + * Information on how an entity is rendered. Properties are only filled in for Model entities; other * entity types have an empty object, {}. * @typedef {object} Entities.RenderInfo * @property {number} verticesCount - The number of vertices in the entity. * @property {number} texturesCount - The number of textures in the entity. * @property {number} texturesSize - The total size of the textures in the entity, in bytes. - * @property {boolean} hasTransparent - true if any of the textures has transparency, false + * @property {boolean} hasTransparent - true if any of the textures has transparency, false * if none of them do. * @property {number} drawCalls - The number of draw calls required to render the entity. */ @@ -2014,6 +2025,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(renderLayer, RenderLayer); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(primitiveMode, PrimitiveMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection); + COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones); _grab.copyFromScriptValue(object, _defaultSettings); // Physics @@ -2123,6 +2135,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations); COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints); COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled); + COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients); _animation.copyFromScriptValue(object, _defaultSettings); // Light @@ -2308,6 +2321,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(renderLayer); COPY_PROPERTY_IF_CHANGED(primitiveMode); COPY_PROPERTY_IF_CHANGED(ignorePickIntersection); + COPY_PROPERTY_IF_CHANGED(renderWithZones); _grab.merge(other._grab); // Physics @@ -2412,6 +2426,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(jointTranslations); COPY_PROPERTY_IF_CHANGED(relayParentJoints); COPY_PROPERTY_IF_CHANGED(groupCulled); + COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients); _animation.merge(other._animation); // Light @@ -2589,7 +2604,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DIMENSIONS, Dimensions, dimensions, vec3, ENTITY_ITEM_MIN_DIMENSION, FLT_MAX); ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, quat); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3, ENTITY_ITEM_MIN_REGISTRATION_POINT, ENTITY_ITEM_MAX_REGISTRATION_POINT); ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); ADD_PROPERTY_TO_MAP(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid); @@ -2601,6 +2616,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer); ADD_PROPERTY_TO_MAP(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode); ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool); + ADD_PROPERTY_TO_MAP(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector); { // Grab ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); @@ -2625,19 +2641,19 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr } // Physics - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DENSITY, Density, density, float, ENTITY_ITEM_MIN_DENSITY, ENTITY_ITEM_MAX_DENSITY); ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, vec3); ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, vec3); ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, vec3); ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DAMPING, Damping, damping, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_DAMPING, Damping, damping, float, ENTITY_ITEM_MIN_DAMPING, ENTITY_ITEM_MAX_DAMPING); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_MIN_DAMPING, ENTITY_ITEM_MAX_DAMPING); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RESTITUTION, Restitution, restitution, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RESTITUTION, Restitution, restitution, float, ENTITY_ITEM_MIN_RESTITUTION, ENTITY_ITEM_MAX_RESTITUTION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_FRICTION, Friction, friction, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_FRICTION, Friction, friction, float, ENTITY_ITEM_MIN_FRICTION, ENTITY_ITEM_MAX_FRICTION); ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); @@ -2681,7 +2697,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat); ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, vec3); ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, vec3); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, ENTITY_ITEM_MIN_DIMENSION, FLT_MAX); // Common @@ -2700,59 +2716,59 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); // Particles - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::MINIMUM_MAX_PARTICLES, particle::MAXIMUM_MAX_PARTICLES); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LIFESPAN, Lifespan, lifespan, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_LIFESPAN, Lifespan, lifespan, float, particle::MINIMUM_LIFESPAN, particle::MAXIMUM_LIFESPAN); ADD_PROPERTY_TO_MAP(PROP_EMITTING_PARTICLES, IsEmitting, isEmitting, bool); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RATE, EmitRate, emitRate, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RATE, EmitRate, emitRate, float, particle::MINIMUM_EMIT_RATE, particle::MAXIMUM_EMIT_RATE); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3, particle::MINIMUM_EMIT_SPEED, particle::MAXIMUM_EMIT_SPEED); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3, particle::MINIMUM_EMIT_SPEED, particle::MAXIMUM_EMIT_SPEED); ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, quat); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3, particle::MINIMUM_EMIT_DIMENSION, particle::MAXIMUM_EMIT_DIMENSION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, particle::MINIMUM_EMIT_RADIUS_START, particle::MAXIMUM_EMIT_RADIUS_START); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_START, EmitPolarStart, polarStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_START, EmitPolarStart, polarStart, float, particle::MINIMUM_POLAR, particle::MAXIMUM_POLAR); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float, particle::MINIMUM_POLAR, particle::MAXIMUM_POLAR); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float, particle::MINIMUM_AZIMUTH, particle::MAXIMUM_AZIMUTH); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float, particle::MINIMUM_AZIMUTH, particle::MAXIMUM_AZIMUTH); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3, particle::MINIMUM_EMIT_ACCELERATION, particle::MAXIMUM_EMIT_ACCELERATION); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3, particle::MINIMUM_ACCELERATION_SPREAD, particle::MAXIMUM_ACCELERATION_SPREAD); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_START, RadiusStart, radiusStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_START, RadiusStart, radiusStart, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::MINIMUM_PARTICLE_RADIUS, particle::MAXIMUM_PARTICLE_RADIUS); ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, vec3Color); ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3Color); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_START, AlphaStart, alphaStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA); ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_START, SpinStart, spinStart, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_START, SpinStart, spinStart, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::MINIMUM_PARTICLE_SPIN, particle::MAXIMUM_PARTICLE_SPIN); ADD_PROPERTY_TO_MAP(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, float); @@ -2765,6 +2781,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool); ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool); + ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString); { // Animation ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); @@ -2781,7 +2798,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool); ADD_PROPERTY_TO_MAP(PROP_INTENSITY, Intensity, intensity, float); ADD_PROPERTY_TO_MAP(PROP_EXPONENT, Exponent, exponent, float); - ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_CUTOFF, Cutoff, cutoff, float, + ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::MIN_CUTOFF, LightEntityItem::MAX_CUTOFF); ADD_PROPERTY_TO_MAP(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float); @@ -3090,6 +3107,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)properties.getRenderLayer()); APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)properties.getPrimitiveMode()); APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection()); + APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, properties.getRenderWithZones()); _staticGrab.setProperties(properties); _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3203,6 +3221,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations()); APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints()); APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled()); + APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients()); _staticAnimation.setProperties(properties); _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3580,6 +3599,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_LAYER, RenderLayer, setRenderLayer); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); // Physics @@ -3689,6 +3709,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); } @@ -3998,6 +4019,7 @@ void EntityItemProperties::markAllChanged() { _renderLayerChanged = true; _primitiveModeChanged = true; _ignorePickIntersectionChanged = true; + _renderWithZonesChanged = true; _grab.markAllChanged(); // Physics @@ -4095,6 +4117,7 @@ void EntityItemProperties::markAllChanged() { _jointTranslationsChanged = true; _relayParentJointsChanged = true; _groupCulledChanged = true; + _blendshapeCoefficientsChanged = true; _animation.markAllChanged(); // Light @@ -4401,6 +4424,9 @@ QList EntityItemProperties::listChangedProperties() { if (ignorePickIntersectionChanged()) { out += "ignorePickIntersection"; } + if (renderWithZonesChanged()) { + out += "renderWithZones"; + } getGrab().listChangedProperties(out); // Physics @@ -4660,6 +4686,9 @@ QList EntityItemProperties::listChangedProperties() { if (groupCulledChanged()) { out += "groupCulled"; } + if (blendshapeCoefficientsChanged()) { + out += "blendshapeCoefficients"; + } getAnimation().listChangedProperties(out); // Light @@ -5076,7 +5105,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte bool EntityItemProperties::verifyStaticCertificateProperties() { // True IFF a non-empty certificateID matches the static certificate json. - // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. + // I.e., if we can verify that the certificateID was produced by Vircadia signing the static certificate hash. return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8())); } @@ -5120,7 +5149,7 @@ bool EntityItemProperties::blobToProperties(QScriptEngine& scriptEngine, const Q return true; } -void EntityItemProperties::propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, +void EntityItemProperties::propertiesToBlob(QScriptEngine& scriptEngine, const QUuid& myAvatarID, const EntityItemProperties& properties, QByteArray& blob, bool allProperties) { // DANGER: this method is NOT efficient. // begin recipe for extracting unfortunately-formatted-binary-blob from EntityItem diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 264fc3881b..efc8b5dc33 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -195,6 +195,7 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer, RenderLayer::WORLD); DEFINE_PROPERTY_REF_ENUM(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode, PrimitiveMode::SOLID); DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false); + DEFINE_PROPERTY_REF(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector, QVector()); DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); // Physics @@ -299,6 +300,7 @@ public: DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false); + DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, ""); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); // Light diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 4c989ef74e..c25eb21e6c 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -119,6 +119,7 @@ inline QScriptValue qVectorVec3Color_convertScriptValue(QScriptEngine* e, const inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorQuatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorQUuidToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } @@ -216,6 +217,7 @@ typedef QVector qVectorVec3; typedef QVector qVectorQuat; typedef QVector qVectorBool; typedef QVector qVectorFloat; +typedef QVector qVectorQUuid; inline float float_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline quint64 quint64_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline quint32 quint32_convertFromScriptValue(const QScriptValue& v, bool& isValid) { @@ -293,6 +295,11 @@ inline qVectorBool qVectorBool_convertFromScriptValue(const QScriptValue& v, boo return qVectorBoolFromScriptValue(v); } +inline qVectorQUuid qVectorQUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + return qVectorQUuidFromScriptValue(v); +} + inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted QScriptValue x = v.property("x"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 1ffd8fdc3c..a7359c0bca 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -43,6 +43,7 @@ enum EntityPropertyList { PROP_RENDER_LAYER, PROP_PRIMITIVE_MODE, PROP_IGNORE_PICK_INTERSECTION, + PROP_RENDER_WITH_ZONES, // Grab PROP_GRAB_GRABBABLE, PROP_GRAB_KINEMATIC, @@ -215,16 +216,17 @@ enum EntityPropertyList { PROP_JOINT_TRANSLATIONS = PROP_DERIVED_5, PROP_RELAY_PARENT_JOINTS = PROP_DERIVED_6, PROP_GROUP_CULLED = PROP_DERIVED_7, + PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8, // Animation - PROP_ANIMATION_URL = PROP_DERIVED_8, - PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_9, - PROP_ANIMATION_FPS = PROP_DERIVED_10, - PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_11, - PROP_ANIMATION_PLAYING = PROP_DERIVED_12, - PROP_ANIMATION_LOOP = PROP_DERIVED_13, - PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_14, - PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_15, - PROP_ANIMATION_HOLD = PROP_DERIVED_16, + PROP_ANIMATION_URL = PROP_DERIVED_9, + PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_10, + PROP_ANIMATION_FPS = PROP_DERIVED_11, + PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_12, + PROP_ANIMATION_PLAYING = PROP_DERIVED_13, + PROP_ANIMATION_LOOP = PROP_DERIVED_14, + PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_15, + PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_16, + PROP_ANIMATION_HOLD = PROP_DERIVED_17, // Light PROP_IS_SPOTLIGHT = PROP_DERIVED_0, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d68747b96c..fd83c99ca5 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -987,8 +987,8 @@ void EntityScriptingInterface::deleteEntity(const QUuid& id) { // Deleting an entity has consequences for linked children: some can be deleted but others can't. // Local- and my-avatar-entities can be deleted immediately, but other-avatar-entities can't be deleted - // by this context, and a domain-entity must rountrip through the entity-server for authorization. - if (entity->isDomainEntity()) { + // by this context, and a domain-entity must round trip through the entity-server for authorization. + if (entity->isDomainEntity() && !_entityTree->isServerlessMode()) { getEntityPacketSender()->queueEraseEntityMessage(id); } else { entitiesToDeleteImmediately.push_back(entity); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 11cd6309da..fedda7a42e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -784,6 +784,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) } } if (theEntity->isSimulated()) { + theEntity->die(); _simulation->prepareEntityForDelete(theEntity); } } @@ -3193,21 +3194,30 @@ glm::vec3 EntityTree::getUnscaledDimensionsForID(const QUuid& id) { return glm::vec3(1.0f); } -void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, +AACube EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { + glm::vec3 min(FLT_MAX); + glm::vec3 max(-FLT_MAX); + // if the queryBox has changed, tell the entity-server EntityItemPointer entity = std::dynamic_pointer_cast(object); if (entity) { bool queryAACubeChanged = false; if (!entity->hasChildren()) { - // updateQueryAACube will also update all ancestors' AACubes, so we only need to call this for leaf nodes - queryAACubeChanged = entity->updateQueryAACube(); + queryAACubeChanged = entity->updateQueryAACube(false); + AACube entityAACube = entity->getQueryAACube(); + min = glm::min(min, entityAACube.getMinimumPoint()); + max = glm::max(max, entityAACube.getMaximumPoint()); } else { - AACube oldCube = entity->getQueryAACube(); object->forEachChild([&](SpatiallyNestablePointer descendant) { - updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + AACube entityAACube = updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + min = glm::min(min, entityAACube.getMinimumPoint()); + max = glm::max(max, entityAACube.getMaximumPoint()); }); - queryAACubeChanged = oldCube != entity->getQueryAACube(); + queryAACubeChanged = entity->updateQueryAACubeWithDescendantAACube(AACube(Extents(min, max)), false); + AACube newCube = entity->getQueryAACube(); + min = glm::min(min, newCube.getMinimumPoint()); + max = glm::max(max, newCube.getMaximumPoint()); } if (queryAACubeChanged || force) { @@ -3216,9 +3226,10 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, if (success) { moveOperator.addEntityToMoveList(entity, newCube); } - // send an edit packet to update the entity-server about the queryAABox. We do this for domain-hosted - // entities as well as for avatar-entities; the packet-sender will route the update accordingly - if (tellServer && packetSender && (entity->isDomainEntity() || entity->isAvatarEntity())) { + // send an edit packet to update the entity-server about the queryAABox. We only do this for domain-hosted + // entities, as we don't want to flood the update pipeline with AvatarEntity updates, so we assume + // others have all info required to properly update queryAACube of AvatarEntities on their end + if (tellServer && packetSender && entity->isDomainEntity()) { quint64 now = usecTimestampNow(); EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); @@ -3233,7 +3244,16 @@ void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, entity->markDirtyFlags(Simulation::DIRTY_POSITION); entityChanged(entity); } + } else { + // if we're called on a non-entity, we might still have entity descendants + object->forEachChild([&](SpatiallyNestablePointer descendant) { + AACube entityAACube = updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer); + min = glm::min(min, entityAACube.getMinimumPoint()); + max = glm::max(max, entityAACube.getMaximumPoint()); + }); } + + return AACube(Extents(min, max)); } void EntityTree::updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3662f6acf0..2d5119d626 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -400,8 +400,9 @@ private: std::map _namedPaths; - void updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, - MovingEntitiesOperator& moveOperator, bool force, bool tellServer); + // Return an AACube containing object and all its entity descendants + AACube updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, + MovingEntitiesOperator& moveOperator, bool force, bool tellServer); }; void convertGrabUserDataToProperties(EntityItemProperties& properties); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index aa0c7304a7..6818c5cb6a 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -33,7 +33,8 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E return entity; } -ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) +ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID), + _blendshapeCoefficientsVector((int)Blendshapes::BlendshapeCount, 0.0f) { _lastAnimated = usecTimestampNow(); // set the last animated when interface (re)starts @@ -71,6 +72,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations); COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints); COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients); withReadLock([&] { _animationProperties.getProperties(properties); }); @@ -94,6 +96,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations); SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints); SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients); withWriteLock([&] { AnimationPropertyGroup animationProperties = _animationProperties; @@ -139,6 +142,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled); + READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients); // grab a local copy of _animationProperties to avoid multiple locks int bytesFromAnimation; @@ -177,6 +181,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_JOINT_TRANSLATIONS; requestedProperties += PROP_RELAY_PARENT_JOINTS; requestedProperties += PROP_GROUP_CULLED; + requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS; requestedProperties += _animationProperties.getEntityProperties(params); return requestedProperties; @@ -205,6 +210,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations()); APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints()); APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled()); + APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients()); withReadLock([&] { _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -257,6 +263,7 @@ void ModelEntityItem::debugDump() const { qCDebug(entities) << " dimensions:" << getScaledDimensions(); qCDebug(entities) << " model URL:" << getModelURL(); qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); + qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients(); } void ModelEntityItem::setShapeType(ShapeType type) { @@ -744,3 +751,39 @@ void ModelEntityItem::setModelScale(const glm::vec3& modelScale) { _modelScale = modelScale; }); } + +QString ModelEntityItem::getBlendshapeCoefficients() const { + return resultWithReadLock([&] { + return QJsonDocument::fromVariant(_blendshapeCoefficientsMap).toJson(); + }); +} + +void ModelEntityItem::setBlendshapeCoefficients(const QString& blendshapeCoefficients) { + QJsonParseError error; + QJsonDocument newCoefficientsJSON = QJsonDocument::fromJson(blendshapeCoefficients.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "Could not evaluate blendshapeCoefficients property value:" << newCoefficientsJSON; + return; + } + + QVariantMap newCoefficientsMap = newCoefficientsJSON.toVariant().toMap(); + withWriteLock([&] { + for (auto& blendshape : newCoefficientsMap.keys()) { + auto newCoefficient = newCoefficientsMap[blendshape]; + auto blendshapeIter = BLENDSHAPE_LOOKUP_MAP.find(blendshape); + if (newCoefficient.canConvert() && blendshapeIter != BLENDSHAPE_LOOKUP_MAP.end()) { + float newCoefficientValue = newCoefficient.toFloat(); + _blendshapeCoefficientsVector[blendshapeIter.value()] = newCoefficientValue; + _blendshapeCoefficientsMap[blendshape] = newCoefficientValue; + _blendshapesChanged = true; + } + } + }); +} + +QVector ModelEntityItem::getBlendshapeCoefficientVector() { + return resultWithReadLock>([&] { + _blendshapesChanged = false; // ok to change this within read lock here + return _blendshapeCoefficientsVector; + }); +} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 75a695f1c0..c331a94adf 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -17,6 +17,7 @@ #include #include "AnimationPropertyGroup.h" +#include "BlendshapeConstants.h" class ModelEntityItem : public EntityItem { public: @@ -133,6 +134,11 @@ public: glm::vec3 getModelScale() const; void setModelScale(const glm::vec3& modelScale); + QString getBlendshapeCoefficients() const; + void setBlendshapeCoefficients(const QString& blendshapeCoefficients); + bool blendshapesChanged() const { return _blendshapesChanged; } + QVector getBlendshapeCoefficientVector(); + private: void setAnimationSettings(const QString& value); // only called for old bitstream format bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); @@ -166,6 +172,7 @@ protected: QString _modelURL; bool _relayParentJoints; bool _groupCulled { false }; + QVariantMap _blendshapeCoefficientsMap; ThreadSafeValueCache _compoundShapeURL; @@ -178,6 +185,9 @@ protected: private: uint64_t _lastAnimated{ 0 }; float _currentFrame{ -1.0f }; + + QVector _blendshapeCoefficientsVector; + bool _blendshapesChanged { false }; }; #endif // hifi_ModelEntityItem_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 6fbf764459..842651fcac 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -351,7 +351,7 @@ bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, c } bool ZoneEntityItem::contains(const glm::vec3& point) const { - ModelResource::Pointer resource = _shapeResource; + GeometryResource::Pointer resource = _shapeResource; if (_shapeType == SHAPE_TYPE_COMPOUND && resource) { if (resource->isLoaded()) { const HFMModel& hfmModel = resource->getHFMModel(); @@ -468,7 +468,7 @@ void ZoneEntityItem::fetchCollisionGeometryResource() { if (hullURL.isEmpty()) { _shapeResource.reset(); } else { - _shapeResource = DependencyManager::get()->getCollisionModelResource(hullURL); + _shapeResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); } } diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 295727d657..8ec3ea12b4 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -173,7 +173,7 @@ protected: static bool _zonesArePickable; void fetchCollisionGeometryResource(); - ModelResource::Pointer _shapeResource; + GeometryResource::Pointer _shapeResource; }; diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index d7c05e29de..100f6ee98e 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -20,7 +20,6 @@ #include #include -#include // TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems... //#define DEBUG_FBXSERIALIZER @@ -146,9 +145,8 @@ public: bool isLimbNode; // is this FBXModel transform is a "LimbNode" i.e. a joint }; - glm::mat4 getGlobalTransform(const QMultiMap& _connectionParentMap, - const QHash& fbxModels, QString nodeID, bool mixamoHack, const QString& url) { + const QHash& fbxModels, QString nodeID, bool mixamoHack, const QString& url) { glm::mat4 globalTransform; QVector visitedNodes; // Used to prevent following a cycle while (!nodeID.isNull()) { @@ -168,11 +166,12 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen } QList parentIDs = _connectionParentMap.values(nodeID); nodeID = QString(); - foreach(const QString& parentID, parentIDs) { + foreach (const QString& parentID, parentIDs) { if (visitedNodes.contains(parentID)) { qCWarning(modelformat) << "Ignoring loop detected in FBX connection map for" << url; continue; } + if (fbxModels.contains(parentID)) { nodeID = parentID; break; @@ -182,21 +181,6 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen return globalTransform; } -std::vector getModelIDsForMeshID(const QString& meshID, const QHash& fbxModels, const QMultiMap& _connectionParentMap) { - std::vector modelsForMesh; - if (fbxModels.contains(meshID)) { - modelsForMesh.push_back(meshID); - } else { - // This mesh may have more than one parent model, with different material and transform, representing a different instance of the mesh - for (const auto& parentID : _connectionParentMap.values(meshID)) { - if (fbxModels.contains(parentID)) { - modelsForMesh.push_back(parentID); - } - } - } - return modelsForMesh; -} - class ExtractedBlendshape { public: QString id; @@ -420,7 +404,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const QVector blendshapes; QHash fbxModels; - QHash fbxClusters; + QHash clusters; QHash animationCurves; QHash typeFlags; @@ -531,8 +515,8 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const if (object.properties.at(2) == "Mesh") { meshes.insert(getID(object.properties), extractMesh(object, meshIndex, deduplicateIndices)); } else { // object.properties.at(2) == "Shape" - ExtractedBlendshape blendshape = { getID(object.properties), extractBlendshape(object) }; - blendshapes.append(blendshape); + ExtractedBlendshape extracted = { getID(object.properties), extractBlendshape(object) }; + blendshapes.append(extracted); } } else if (object.name == "Model") { QString name = getModelName(object.properties); @@ -706,8 +690,8 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const // add the blendshapes included in the model, if any if (mesh) { - foreach (const ExtractedBlendshape& blendshape, blendshapes) { - addBlendshapes(blendshape, blendshapeIndices.values(blendshape.id.toLatin1()), *mesh); + foreach (const ExtractedBlendshape& extracted, blendshapes) { + addBlendshapes(extracted, blendshapeIndices.values(extracted.id.toLatin1()), *mesh); } } @@ -1074,9 +1058,9 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } } - // skip empty fbxClusters + // skip empty clusters if (cluster.indices.size() > 0 && cluster.weights.size() > 0) { - fbxClusters.insert(getID(object.properties), cluster); + clusters.insert(getID(object.properties), cluster); } } else if (object.properties.last() == "BlendShapeChannel") { @@ -1230,11 +1214,11 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } // assign the blendshapes to their corresponding meshes - foreach (const ExtractedBlendshape& blendshape, blendshapes) { - QString blendshapeChannelID = _connectionParentMap.value(blendshape.id); + foreach (const ExtractedBlendshape& extracted, blendshapes) { + QString blendshapeChannelID = _connectionParentMap.value(extracted.id); QString blendshapeID = _connectionParentMap.value(blendshapeChannelID); QString meshID = _connectionParentMap.value(blendshapeID); - addBlendshapes(blendshape, blendshapeChannelIndices.values(blendshapeChannelID), meshes[meshID]); + addBlendshapes(extracted, blendshapeChannelIndices.values(blendshapeChannelID), meshes[meshID]); } // get offset transform from mapping @@ -1249,13 +1233,13 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const QVector modelIDs; QSet remainingFBXModels; for (QHash::const_iterator fbxModel = fbxModels.constBegin(); fbxModel != fbxModels.constEnd(); fbxModel++) { - // models with fbxClusters must be parented to the cluster top + // models with clusters must be parented to the cluster top // Unless the model is a root node. bool isARootNode = !modelIDs.contains(_connectionParentMap.value(fbxModel.key())); if (!isARootNode) { foreach(const QString& deformerID, _connectionChildMap.values(fbxModel.key())) { foreach(const QString& clusterID, _connectionChildMap.values(deformerID)) { - if (!fbxClusters.contains(clusterID)) { + if (!clusters.contains(clusterID)) { continue; } QString topID = getTopModelID(_connectionParentMap, fbxModels, _connectionChildMap.value(clusterID), url); @@ -1299,18 +1283,12 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const // convert the models to joints hfmModel.hasSkeletonJoints = false; - - bool needMixamoHack = hfmModel.applicationName == "mixamo.com"; - std::vector transformForClusters; - transformForClusters.reserve((size_t)modelIDs.size()); - for (const QString& modelID : modelIDs) { + foreach (const QString& modelID, modelIDs) { const FBXModel& fbxModel = fbxModels[modelID]; HFMJoint joint; joint.parentIndex = fbxModel.parentIndex; - uint32_t jointIndex = (uint32_t)hfmModel.joints.size(); - - // Copy default joint parameters from model + int jointIndex = hfmModel.joints.size(); joint.translation = fbxModel.translation; // these are usually in centimeters joint.preTransform = fbxModel.preTransform; @@ -1321,62 +1299,35 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const joint.rotationMin = fbxModel.rotationMin; joint.rotationMax = fbxModel.rotationMax; - if (fbxModel.hasGeometricOffset) { - joint.geometricOffset = createMatFromScaleQuatAndPos(fbxModel.geometricScaling, fbxModel.geometricRotation, fbxModel.geometricTranslation); - } + joint.hasGeometricOffset = fbxModel.hasGeometricOffset; + joint.geometricTranslation = fbxModel.geometricTranslation; + joint.geometricRotation = fbxModel.geometricRotation; + joint.geometricScaling = fbxModel.geometricScaling; joint.isSkeletonJoint = fbxModel.isLimbNode; hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint); - - joint.name = fbxModel.name; - - joint.bindTransformFoundInCluster = false; - - // With the basic joint information, we can start to calculate compound transform information - // modelIDs is ordered from parent to children, so we can safely get parent transforms from earlier joints as we iterate - - // Make adjustments to the static joint properties, and pre-calculate static transforms - if (applyUpAxisZRotation && joint.parentIndex == -1) { joint.rotation *= upAxisZRotation; joint.translation = upAxisZRotation * joint.translation; } - glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - joint.localTransform = glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; - if (joint.parentIndex == -1) { - joint.transform = joint.localTransform; - joint.globalTransform = hfmModel.offset * joint.localTransform; + joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform * + glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation); joint.distanceToParent = 0.0f; + } else { const HFMJoint& parentJoint = hfmModel.joints.at(joint.parentIndex); - joint.transform = parentJoint.transform * joint.localTransform; - joint.globalTransform = parentJoint.globalTransform * joint.localTransform; + joint.transform = parentJoint.transform * glm::translate(joint.translation) * + joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; - joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform), extractTranslation(joint.transform)); + joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform), + extractTranslation(joint.transform)); } joint.inverseBindRotation = joint.inverseDefaultRotation; + joint.name = fbxModel.name; - // If needed, separately calculate the FBX-specific transform used for inverse bind transform calculations - - glm::mat4 transformForCluster; - if (applyUpAxisZRotation) { - const glm::quat jointBindCombinedRotation = fbxModel.preRotation * fbxModel.rotation * fbxModel.postRotation; - const glm::mat4 localTransformForCluster = glm::translate(fbxModel.translation) * fbxModel.preTransform * glm::mat4_cast(jointBindCombinedRotation) * fbxModel.postTransform; - if (fbxModel.parentIndex != -1 && fbxModel.parentIndex < (int)jointIndex && !needMixamoHack) { - const glm::mat4& parenttransformForCluster = transformForClusters[fbxModel.parentIndex]; - transformForCluster = parenttransformForCluster * localTransformForCluster; - } else { - transformForCluster = localTransformForCluster; - } - } else { - transformForCluster = joint.transform; - } - transformForClusters.push_back(transformForCluster); - - // Initialize animation information next - // And also get the joint poses from the first frame of the animation, if present + joint.bindTransformFoundInCluster = false; QString rotationID = localRotations.value(modelID); AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID)); @@ -1404,11 +1355,14 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const joint.translation = hfmModel.animationFrames[i].translations[jointIndex]; joint.rotation = hfmModel.animationFrames[i].rotations[jointIndex]; } - } - hfmModel.joints.push_back(joint); + } + hfmModel.joints.append(joint); } + // NOTE: shapeVertices are in joint-frame + hfmModel.shapeVertices.resize(std::max(1, hfmModel.joints.size()) ); + hfmModel.bindExtents.reset(); hfmModel.meshExtents.reset(); @@ -1446,202 +1400,233 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } } #endif - - std::unordered_map materialNameToID; - for (auto materialIt = _hfmMaterials.cbegin(); materialIt != _hfmMaterials.cend(); ++materialIt) { - materialNameToID[materialIt.key().toStdString()] = (uint32_t)hfmModel.materials.size(); - hfmModel.materials.push_back(materialIt.value()); - } + hfmModel.materials = _hfmMaterials; // see if any materials have texture children bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap); for (QMap::iterator it = meshes.begin(); it != meshes.end(); it++) { - const QString& meshID = it.key(); - const ExtractedMesh& extracted = it.value(); - const auto& partMaterialTextures = extracted.partMaterialTextures; + ExtractedMesh& extracted = it.value(); - uint32_t meshIndex = (uint32_t)hfmModel.meshes.size(); - meshIDsToMeshIndices.insert(meshID, meshIndex); - hfmModel.meshes.push_back(extracted.mesh); - hfm::Mesh& mesh = hfmModel.meshes.back(); + extracted.mesh.meshExtents.reset(); - std::vector instanceModelIDs = getModelIDsForMeshID(meshID, fbxModels, _connectionParentMap); - // meshShapes will be added to hfmModel at the very end - std::vector meshShapes; - meshShapes.reserve(instanceModelIDs.size() * mesh.parts.size()); - for (const QString& modelID : instanceModelIDs) { - // The transform node has the same indexing order as the joints - int indexOfModelID = modelIDs.indexOf(modelID); - if (indexOfModelID == -1) { - qCDebug(modelformat) << "Model not in model list: " << modelID; - } - const uint32_t transformIndex = (indexOfModelID == -1) ? 0 : (uint32_t)indexOfModelID; + // accumulate local transforms + QString modelID = fbxModels.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); + glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, hfmModel.applicationName == "mixamo.com", url); - // partShapes will be added to meshShapes at the very end - std::vector partShapes { mesh.parts.size() }; - for (uint32_t i = 0; i < (uint32_t)partShapes.size(); ++i) { - hfm::Shape& shape = partShapes[i]; - shape.mesh = meshIndex; - shape.meshPart = i; - shape.joint = transformIndex; - } + // compute the mesh extents from the transformed vertices + foreach (const glm::vec3& vertex, extracted.mesh.vertices) { + glm::vec3 transformedVertex = glm::vec3(modelTransform * glm::vec4(vertex, 1.0f)); + hfmModel.meshExtents.minimum = glm::min(hfmModel.meshExtents.minimum, transformedVertex); + hfmModel.meshExtents.maximum = glm::max(hfmModel.meshExtents.maximum, transformedVertex); - // For FBX_DRACO_MESH_VERSION < 2, or unbaked models, get materials from the partMaterialTextures - if (!partMaterialTextures.empty()) { - int materialIndex = 0; - int textureIndex = 0; - QList children = _connectionChildMap.values(modelID); - for (int i = children.size() - 1; i >= 0; i--) { - const QString& childID = children.at(i); - if (_hfmMaterials.contains(childID)) { - // the pure material associated with this part - const HFMMaterial& material = _hfmMaterials.value(childID); - for (int j = 0; j < partMaterialTextures.size(); j++) { - if (partMaterialTextures.at(j).first == materialIndex) { - hfm::Shape& shape = partShapes[j]; - shape.material = materialNameToID[material.materialID.toStdString()]; - } - } - materialIndex++; - } else if (_textureFilenames.contains(childID)) { - // NOTE (Sabrina 2019/01/11): getTextures now takes in the materialID as a second parameter, because FBX material nodes can sometimes have uv transform information (ex: "Maya|uv_scale") - // I'm leaving the second parameter blank right now as this code may never be used. - HFMTexture texture = getTexture(childID, ""); - for (int j = 0; j < partMaterialTextures.size(); j++) { - int partTexture = partMaterialTextures.at(j).second; - if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) { - // TODO: DO something here that replaces this legacy code - // Maybe create a material just for this part with the correct textures? - // material.albedoTexture = texture; - // partShapes[j].material = materialIndex; - } - } - textureIndex++; - } - } - } - // For baked models with FBX_DRACO_MESH_VERSION >= 2, get materials from extracted.materialIDPerMeshPart - if (!extracted.materialIDPerMeshPart.empty()) { - assert(partShapes.size() == extracted.materialIDPerMeshPart.size()); - for (uint32_t i = 0; i < (uint32_t)extracted.materialIDPerMeshPart.size(); ++i) { - hfm::Shape& shape = partShapes[i]; - const std::string& materialID = extracted.materialIDPerMeshPart[i]; - auto materialIt = materialNameToID.find(materialID); - if (materialIt != materialNameToID.end()) { - shape.material = materialIt->second; - } - } - } - - // find the clusters with which the mesh is associated - QVector clusterIDs; - for (const QString& childID : _connectionChildMap.values(meshID)) { - for (const QString& clusterID : _connectionChildMap.values(childID)) { - if (!fbxClusters.contains(clusterID)) { - continue; - } - clusterIDs.append(clusterID); - } - } - - // whether we're skinned depends on how many clusters are attached - if (clusterIDs.size() > 0) { - hfm::SkinDeformer skinDeformer; - auto& clusters = skinDeformer.clusters; - for (const auto& clusterID : clusterIDs) { - HFMCluster hfmCluster; - const Cluster& fbxCluster = fbxClusters[clusterID]; - - // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion - // of skinning information in FBX - QString jointID = _connectionChildMap.value(clusterID); - int indexOfJointID = modelIDs.indexOf(jointID); - if (indexOfJointID == -1) { - qCDebug(modelformat) << "Joint not in model list: " << jointID; - hfmCluster.jointIndex = 0; - } else { - hfmCluster.jointIndex = (uint32_t)indexOfJointID; - } - - const glm::mat4& transformForCluster = transformForClusters[transformIndex]; - hfmCluster.inverseBindMatrix = glm::inverse(fbxCluster.transformLink) * transformForCluster; - - // slam bottom row to (0, 0, 0, 1), we KNOW this is not a perspective matrix and - // sometimes floating point fuzz can be introduced after the inverse. - hfmCluster.inverseBindMatrix[0][3] = 0.0f; - hfmCluster.inverseBindMatrix[1][3] = 0.0f; - hfmCluster.inverseBindMatrix[2][3] = 0.0f; - hfmCluster.inverseBindMatrix[3][3] = 1.0f; - - hfmCluster.inverseBindTransform = Transform(hfmCluster.inverseBindMatrix); - - clusters.push_back(hfmCluster); - - // override the bind rotation with the transform link - HFMJoint& joint = hfmModel.joints[hfmCluster.jointIndex]; - joint.inverseBindRotation = glm::inverse(extractRotation(fbxCluster.transformLink)); - joint.bindTransform = fbxCluster.transformLink; - joint.bindTransformFoundInCluster = true; - - // update the bind pose extents - glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * joint.bindTransform); - hfmModel.bindExtents.addPoint(bindTranslation); - } - - // the last cluster is the root cluster - HFMCluster cluster; - cluster.jointIndex = transformIndex; - clusters.push_back(cluster); - - // Skinned mesh instances have an hfm::SkinDeformer - std::vector skinClusters; - for (const auto& clusterID : clusterIDs) { - const Cluster& fbxCluster = fbxClusters[clusterID]; - skinClusters.emplace_back(); - hfm::SkinCluster& skinCluster = skinClusters.back(); - size_t indexWeightPairs = (size_t)std::min(fbxCluster.indices.size(), fbxCluster.weights.size()); - skinCluster.indices.reserve(indexWeightPairs); - skinCluster.weights.reserve(indexWeightPairs); - - for (int j = 0; j < fbxCluster.indices.size(); j++) { - int oldIndex = fbxCluster.indices.at(j); - float weight = fbxCluster.weights.at(j); - for (QMultiHash::const_iterator it = extracted.newIndices.constFind(oldIndex); - it != extracted.newIndices.end() && it.key() == oldIndex; it++) { - int newIndex = it.value(); - - skinCluster.indices.push_back(newIndex); - skinCluster.weights.push_back(weight); - } - } - } - // It seems odd that this mesh-related code should be inside of the for loop for instanced model IDs. - // However, in practice, skinned FBX models appear to not be instanced, as the skinning includes both the weights and joints. - { - hfm::ReweightedDeformers reweightedDeformers = hfm::getReweightedDeformers(mesh.vertices.size(), skinClusters); - if (reweightedDeformers.trimmedToMatch) { - qDebug(modelformat) << "FBXSerializer -- The number of indices and weights for a skinning deformer had different sizes and have been trimmed to match"; - } - mesh.clusterIndices = std::move(reweightedDeformers.indices); - mesh.clusterWeights = std::move(reweightedDeformers.weights); - mesh.clusterWeightsPerVertex = reweightedDeformers.weightsPerVertex; - } - - // Store the model's dynamic transform, and put its ID in the shapes - uint32_t skinDeformerID = (uint32_t)hfmModel.skinDeformers.size(); - hfmModel.skinDeformers.push_back(skinDeformer); - for (hfm::Shape& shape : partShapes) { - shape.skinDeformer = skinDeformerID; - } - } - - // Store the parts for this mesh (or instance of this mesh, as the case may be) - meshShapes.insert(meshShapes.cend(), partShapes.cbegin(), partShapes.cend()); + extracted.mesh.meshExtents.minimum = glm::min(extracted.mesh.meshExtents.minimum, transformedVertex); + extracted.mesh.meshExtents.maximum = glm::max(extracted.mesh.meshExtents.maximum, transformedVertex); + extracted.mesh.modelTransform = modelTransform; } - // Store the shapes for the mesh (or multiple instances of the mesh, as the case may be) - hfmModel.shapes.insert(hfmModel.shapes.cend(), meshShapes.cbegin(), meshShapes.cend()); + // look for textures, material properties + // allocate the Part material library + // NOTE: extracted.partMaterialTextures is empty for FBX_DRACO_MESH_VERSION >= 2. In that case, the mesh part's materialID string is already defined. + int materialIndex = 0; + int textureIndex = 0; + QList children = _connectionChildMap.values(modelID); + for (int i = children.size() - 1; i >= 0; i--) { + + const QString& childID = children.at(i); + if (_hfmMaterials.contains(childID)) { + // the pure material associated with this part + HFMMaterial material = _hfmMaterials.value(childID); + + for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { + if (extracted.partMaterialTextures.at(j).first == materialIndex) { + HFMMeshPart& part = extracted.mesh.parts[j]; + part.materialID = material.materialID; + } + } + + materialIndex++; + } else if (_textureFilenames.contains(childID)) { + // NOTE (Sabrina 2019/01/11): getTextures now takes in the materialID as a second parameter, because FBX material nodes can sometimes have uv transform information (ex: "Maya|uv_scale") + // I'm leaving the second parameter blank right now as this code may never be used. + HFMTexture texture = getTexture(childID, ""); + for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { + int partTexture = extracted.partMaterialTextures.at(j).second; + if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) { + // TODO: DO something here that replaces this legacy code + // Maybe create a material just for this part with the correct textures? + // extracted.mesh.parts[j].diffuseTexture = texture; + } + } + textureIndex++; + } + } + + // find the clusters with which the mesh is associated + QVector clusterIDs; + foreach (const QString& childID, _connectionChildMap.values(it.key())) { + foreach (const QString& clusterID, _connectionChildMap.values(childID)) { + if (!clusters.contains(clusterID)) { + continue; + } + HFMCluster hfmCluster; + const Cluster& cluster = clusters[clusterID]; + clusterIDs.append(clusterID); + + // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion + // of skinning information in FBX + QString jointID = _connectionChildMap.value(clusterID); + hfmCluster.jointIndex = modelIDs.indexOf(jointID); + if (hfmCluster.jointIndex == -1) { + qCDebug(modelformat) << "Joint not in model list: " << jointID; + hfmCluster.jointIndex = 0; + } + + hfmCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + + // slam bottom row to (0, 0, 0, 1), we KNOW this is not a perspective matrix and + // sometimes floating point fuzz can be introduced after the inverse. + hfmCluster.inverseBindMatrix[0][3] = 0.0f; + hfmCluster.inverseBindMatrix[1][3] = 0.0f; + hfmCluster.inverseBindMatrix[2][3] = 0.0f; + hfmCluster.inverseBindMatrix[3][3] = 1.0f; + + hfmCluster.inverseBindTransform = Transform(hfmCluster.inverseBindMatrix); + + extracted.mesh.clusters.append(hfmCluster); + + // override the bind rotation with the transform link + HFMJoint& joint = hfmModel.joints[hfmCluster.jointIndex]; + joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); + joint.bindTransform = cluster.transformLink; + joint.bindTransformFoundInCluster = true; + + // update the bind pose extents + glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * joint.bindTransform); + hfmModel.bindExtents.addPoint(bindTranslation); + } + } + + // the last cluster is the root cluster + { + HFMCluster cluster; + cluster.jointIndex = modelIDs.indexOf(modelID); + if (cluster.jointIndex == -1) { + qCDebug(modelformat) << "Model not in model list: " << modelID; + cluster.jointIndex = 0; + } + extracted.mesh.clusters.append(cluster); + } + + // whether we're skinned depends on how many clusters are attached + if (clusterIDs.size() > 1) { + // this is a multi-mesh joint + const int WEIGHTS_PER_VERTEX = 4; + int numClusterIndices = extracted.mesh.vertices.size() * WEIGHTS_PER_VERTEX; + extracted.mesh.clusterIndices.fill(extracted.mesh.clusters.size() - 1, numClusterIndices); + QVector weightAccumulators; + weightAccumulators.fill(0.0f, numClusterIndices); + + for (int i = 0; i < clusterIDs.size(); i++) { + QString clusterID = clusterIDs.at(i); + const Cluster& cluster = clusters[clusterID]; + const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); + int jointIndex = hfmCluster.jointIndex; + HFMJoint& joint = hfmModel.joints[jointIndex]; + + glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; + ShapeVertices& points = hfmModel.shapeVertices.at(jointIndex); + + for (int j = 0; j < cluster.indices.size(); j++) { + int oldIndex = cluster.indices.at(j); + float weight = cluster.weights.at(j); + for (QMultiHash::const_iterator it = extracted.newIndices.constFind(oldIndex); + it != extracted.newIndices.end() && it.key() == oldIndex; it++) { + int newIndex = it.value(); + + // remember vertices with at least 1/4 weight + // FIXME: vertices with no weightpainting won't get recorded here + const float EXPANSION_WEIGHT_THRESHOLD = 0.25f; + if (weight >= EXPANSION_WEIGHT_THRESHOLD) { + // transform to joint-frame and save for later + const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(newIndex)); + points.push_back(extractTranslation(vertexTransform)); + } + + // look for an unused slot in the weights vector + int weightIndex = newIndex * WEIGHTS_PER_VERTEX; + int lowestIndex = -1; + float lowestWeight = FLT_MAX; + int k = 0; + for (; k < WEIGHTS_PER_VERTEX; k++) { + if (weightAccumulators[weightIndex + k] == 0.0f) { + extracted.mesh.clusterIndices[weightIndex + k] = i; + weightAccumulators[weightIndex + k] = weight; + break; + } + if (weightAccumulators[weightIndex + k] < lowestWeight) { + lowestIndex = k; + lowestWeight = weightAccumulators[weightIndex + k]; + } + } + if (k == WEIGHTS_PER_VERTEX && weight > lowestWeight) { + // no space for an additional weight; we must replace the lowest + weightAccumulators[weightIndex + lowestIndex] = weight; + extracted.mesh.clusterIndices[weightIndex + lowestIndex] = i; + } + } + } + } + + // now that we've accumulated the most relevant weights for each vertex + // normalize and compress to 16-bits + extracted.mesh.clusterWeights.fill(0, numClusterIndices); + int numVertices = extracted.mesh.vertices.size(); + for (int i = 0; i < numVertices; ++i) { + int j = i * WEIGHTS_PER_VERTEX; + + // normalize weights into uint16_t + float totalWeight = 0.0f; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + totalWeight += weightAccumulators[k]; + } + + const float ALMOST_HALF = 0.499f; + if (totalWeight > 0.0f) { + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); + } + } else { + extracted.mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); + } + } + } else { + // this is a single-joint mesh + const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0); + int jointIndex = firstHFMCluster.jointIndex; + HFMJoint& joint = hfmModel.joints[jointIndex]; + + // transform cluster vertices to joint-frame and save for later + glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; + ShapeVertices& points = hfmModel.shapeVertices.at(jointIndex); + foreach (const glm::vec3& vertex, extracted.mesh.vertices) { + const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex); + points.push_back(extractTranslation(vertexTransform)); + } + + // Apply geometric offset, if present, by transforming the vertices directly + if (joint.hasGeometricOffset) { + glm::mat4 geometricOffset = createMatFromScaleQuatAndPos(joint.geometricScaling, joint.geometricRotation, joint.geometricTranslation); + for (int i = 0; i < extracted.mesh.vertices.size(); i++) { + extracted.mesh.vertices[i] = transformPoint(geometricOffset, extracted.mesh.vertices[i]); + } + } + } + + hfmModel.meshes.append(extracted.mesh); + int meshIndex = hfmModel.meshes.size() - 1; + meshIDsToMeshIndices.insert(it.key(), meshIndex); } // attempt to map any meshes to a named model @@ -1660,6 +1645,14 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } } + if (applyUpAxisZRotation) { + hfmModelPtr->meshExtents.transform(glm::mat4_cast(upAxisZRotation)); + hfmModelPtr->bindExtents.transform(glm::mat4_cast(upAxisZRotation)); + for (auto &mesh : hfmModelPtr->meshes) { + mesh.modelTransform *= glm::mat4_cast(upAxisZRotation); + mesh.meshExtents.transform(glm::mat4_cast(upAxisZRotation)); + } + } return hfmModelPtr; } diff --git a/libraries/fbx/src/FBXSerializer.h b/libraries/fbx/src/FBXSerializer.h index 2044d82710..7d41f98444 100644 --- a/libraries/fbx/src/FBXSerializer.h +++ b/libraries/fbx/src/FBXSerializer.h @@ -100,15 +100,7 @@ public: {} }; -class ExtractedMesh { -public: - hfm::Mesh mesh; - std::vector materialIDPerMeshPart; - QMultiHash newIndices; - QVector > blendshapeIndexMaps; - QVector > partMaterialTextures; - QHash texcoordSetMap; -}; +class ExtractedMesh; class FBXSerializer : public HFMSerializer { public: diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp index e687f5e9f2..802db4b428 100644 --- a/libraries/fbx/src/FBXSerializer_Mesh.cpp +++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp @@ -355,7 +355,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me // Check for additional metadata unsigned int dracoMeshNodeVersion = 1; - std::vector dracoMaterialList; + std::vector dracoMaterialList; for (const auto& dracoChild : child.children) { if (dracoChild.name == "FBXDracoMeshVersion") { if (!dracoChild.properties.isEmpty()) { @@ -364,7 +364,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me } else if (dracoChild.name == "MaterialList") { dracoMaterialList.reserve(dracoChild.properties.size()); for (const auto& materialID : dracoChild.properties) { - dracoMaterialList.push_back(materialID.toString().toStdString()); + dracoMaterialList.push_back(materialID.toString()); } } } @@ -486,20 +486,21 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me // grab or setup the HFMMeshPart for the part this face belongs to int& partIndexPlusOne = materialTextureParts[materialTexture]; if (partIndexPlusOne == 0) { - data.extracted.mesh.parts.emplace_back(); + data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); + HFMMeshPart& part = data.extracted.mesh.parts.back(); - // Figure out if this is the older way of defining the per-part material for baked FBX + // Figure out what material this part is if (dracoMeshNodeVersion >= 2) { - // Define the materialID for this mesh part index - uint16_t safeMaterialID = materialID < dracoMaterialList.size() ? materialID : 0; - data.extracted.materialIDPerMeshPart.push_back(dracoMaterialList[safeMaterialID].c_str()); + // Define the materialID now + if (materialID < dracoMaterialList.size()) { + part.materialID = dracoMaterialList[materialID]; + } } else { // Define the materialID later, based on the order of first appearance of the materials in the _connectionChildMap data.extracted.partMaterialTextures.append(materialTexture); } - // in dracoMeshNodeVersion >= 2, fbx meshes have their per-part materials already defined in data.extracted.materialIDPerMeshPart - partIndexPlusOne = (int)data.extracted.mesh.parts.size(); + partIndexPlusOne = data.extracted.mesh.parts.size(); } // give the mesh part this index @@ -534,7 +535,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me if (partIndex == 0) { data.extracted.partMaterialTextures.append(materialTexture); data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); - partIndex = (int)data.extracted.mesh.parts.size(); + partIndex = data.extracted.mesh.parts.size(); } HFMMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; diff --git a/libraries/fbx/src/FST.cpp b/libraries/fbx/src/FST.cpp index 5f5b7cf637..b6f109c217 100644 --- a/libraries/fbx/src/FST.cpp +++ b/libraries/fbx/src/FST.cpp @@ -77,7 +77,7 @@ FST* FST::createFSTFromModel(const QString& fstPath, const QString& modelFilePat mapping.insert(JOINT_FIELD, joints); QVariantHash jointIndices; - for (size_t i = 0; i < (size_t)hfmModel.joints.size(); i++) { + for (int i = 0; i < hfmModel.joints.size(); i++) { jointIndices.insert(hfmModel.joints.at(i).name, QString::number(i)); } mapping.insert(JOINT_INDEX_FIELD, jointIndices); diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 9d9d16d733..a7af5518a9 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -25,13 +25,9 @@ #include #include -#include -#include #include #include -#include - #include #include #include @@ -41,57 +37,30 @@ #include "FBXSerializer.h" -#define GLTF_GET_INDICIES(accCount) \ - int index1 = (indices[n + 0] * accCount); \ - int index2 = (indices[n + 1] * accCount); \ - int index3 = (indices[n + 2] * accCount); +#define GLTF_GET_INDICIES(accCount) int index1 = (indices[n + 0] * accCount); int index2 = (indices[n + 1] * accCount); int index3 = (indices[n + 2] * accCount); -#define GLTF_APPEND_ARRAY_1(newArray, oldArray) \ - GLTF_GET_INDICIES(1) \ - newArray.append(oldArray[index1]); \ - newArray.append(oldArray[index2]); \ - newArray.append(oldArray[index3]); +#define GLTF_APPEND_ARRAY_1(newArray, oldArray) GLTF_GET_INDICIES(1) \ +newArray.append(oldArray[index1]); \ +newArray.append(oldArray[index2]); \ +newArray.append(oldArray[index3]); -#define GLTF_APPEND_ARRAY_2(newArray, oldArray) \ - GLTF_GET_INDICIES(2) \ - newArray.append(oldArray[index1]); \ - newArray.append(oldArray[index1 + 1]); \ - newArray.append(oldArray[index2]); \ - newArray.append(oldArray[index2 + 1]); \ - newArray.append(oldArray[index3]); \ - newArray.append(oldArray[index3 + 1]); +#define GLTF_APPEND_ARRAY_2(newArray, oldArray) GLTF_GET_INDICIES(2) \ +newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); \ +newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); \ +newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); -#define GLTF_APPEND_ARRAY_3(newArray, oldArray) \ - GLTF_GET_INDICIES(3) \ - newArray.append(oldArray[index1]); \ - newArray.append(oldArray[index1 + 1]); \ - newArray.append(oldArray[index1 + 2]); \ - newArray.append(oldArray[index2]); \ - newArray.append(oldArray[index2 + 1]); \ - newArray.append(oldArray[index2 + 2]); \ - newArray.append(oldArray[index3]); \ - newArray.append(oldArray[index3 + 1]); \ - newArray.append(oldArray[index3 + 2]); +#define GLTF_APPEND_ARRAY_3(newArray, oldArray) GLTF_GET_INDICIES(3) \ +newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArray.append(oldArray[index1 + 2]); \ +newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); \ +newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); -#define GLTF_APPEND_ARRAY_4(newArray, oldArray) \ - GLTF_GET_INDICIES(4) \ - newArray.append(oldArray[index1]); \ - newArray.append(oldArray[index1 + 1]); \ - newArray.append(oldArray[index1 + 2]); \ - newArray.append(oldArray[index1 + 3]); \ - newArray.append(oldArray[index2]); \ - newArray.append(oldArray[index2 + 1]); \ - newArray.append(oldArray[index2 + 2]); \ - newArray.append(oldArray[index2 + 3]); \ - newArray.append(oldArray[index3]); \ - newArray.append(oldArray[index3 + 1]); \ - newArray.append(oldArray[index3 + 2]); \ - newArray.append(oldArray[index3 + 3]); +#define GLTF_APPEND_ARRAY_4(newArray, oldArray) GLTF_GET_INDICIES(4) \ +newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArray.append(oldArray[index1 + 2]); newArray.append(oldArray[index1 + 3]); \ +newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); newArray.append(oldArray[index2 + 3]); \ +newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); newArray.append(oldArray[index3 + 3]); -bool GLTFSerializer::getStringVal(const QJsonObject& object, - const QString& fieldname, - QString& value, - QMap& defined) { +bool GLTFSerializer::getStringVal(const QJsonObject& object, const QString& fieldname, + QString& value, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isString()); if (_defined) { value = object[fieldname].toString(); @@ -100,10 +69,8 @@ bool GLTFSerializer::getStringVal(const QJsonObject& object, return _defined; } -bool GLTFSerializer::getBoolVal(const QJsonObject& object, - const QString& fieldname, - bool& value, - QMap& defined) { +bool GLTFSerializer::getBoolVal(const QJsonObject& object, const QString& fieldname, + bool& value, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isBool()); if (_defined) { value = object[fieldname].toBool(); @@ -112,7 +79,8 @@ bool GLTFSerializer::getBoolVal(const QJsonObject& object, return _defined; } -bool GLTFSerializer::getIntVal(const QJsonObject& object, const QString& fieldname, int& value, QMap& defined) { +bool GLTFSerializer::getIntVal(const QJsonObject& object, const QString& fieldname, + int& value, QMap& defined) { bool _defined = (object.contains(fieldname) && !object[fieldname].isNull()); if (_defined) { value = object[fieldname].toInt(); @@ -121,10 +89,8 @@ bool GLTFSerializer::getIntVal(const QJsonObject& object, const QString& fieldna return _defined; } -bool GLTFSerializer::getDoubleVal(const QJsonObject& object, - const QString& fieldname, - double& value, - QMap& defined) { +bool GLTFSerializer::getDoubleVal(const QJsonObject& object, const QString& fieldname, + double& value, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isDouble()); if (_defined) { value = object[fieldname].toDouble(); @@ -132,10 +98,8 @@ bool GLTFSerializer::getDoubleVal(const QJsonObject& object, defined.insert(fieldname, _defined); return _defined; } -bool GLTFSerializer::getObjectVal(const QJsonObject& object, - const QString& fieldname, - QJsonObject& value, - QMap& defined) { +bool GLTFSerializer::getObjectVal(const QJsonObject& object, const QString& fieldname, + QJsonObject& value, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isObject()); if (_defined) { value = object[fieldname].toObject(); @@ -144,14 +108,12 @@ bool GLTFSerializer::getObjectVal(const QJsonObject& object, return _defined; } -bool GLTFSerializer::getIntArrayVal(const QJsonObject& object, - const QString& fieldname, - QVector& values, - QMap& defined) { +bool GLTFSerializer::getIntArrayVal(const QJsonObject& object, const QString& fieldname, + QVector& values, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); if (_defined) { QJsonArray arr = object[fieldname].toArray(); - for (const QJsonValue& v : arr) { + foreach(const QJsonValue & v, arr) { if (!v.isNull()) { values.push_back(v.toInt()); } @@ -161,14 +123,12 @@ bool GLTFSerializer::getIntArrayVal(const QJsonObject& object, return _defined; } -bool GLTFSerializer::getDoubleArrayVal(const QJsonObject& object, - const QString& fieldname, - QVector& values, - QMap& defined) { +bool GLTFSerializer::getDoubleArrayVal(const QJsonObject& object, const QString& fieldname, + QVector& values, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); if (_defined) { QJsonArray arr = object[fieldname].toArray(); - for (const QJsonValue& v : arr) { + foreach(const QJsonValue & v, arr) { if (v.isDouble()) { values.push_back(v.toDouble()); } @@ -178,10 +138,8 @@ bool GLTFSerializer::getDoubleArrayVal(const QJsonObject& object, return _defined; } -bool GLTFSerializer::getObjectArrayVal(const QJsonObject& object, - const QString& fieldname, - QJsonArray& objects, - QMap& defined) { +bool GLTFSerializer::getObjectArrayVal(const QJsonObject& object, const QString& fieldname, + QJsonArray& objects, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isArray()); if (_defined) { objects = object[fieldname].toArray(); @@ -191,7 +149,7 @@ bool GLTFSerializer::getObjectArrayVal(const QJsonObject& object, } hifi::ByteArray GLTFSerializer::setGLBChunks(const hifi::ByteArray& data) { - int byte = 4; + int byte = 4; int jsonStart = data.indexOf("JSON", Qt::CaseSensitive); int binStart = data.indexOf("BIN", Qt::CaseSensitive); int jsonLength, binLength; @@ -215,7 +173,8 @@ hifi::ByteArray GLTFSerializer::setGLBChunks(const hifi::ByteArray& data) { return jsonChunk; } -int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type) { +int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type) +{ if (type == "POINTS") { return GLTFMeshPrimitivesRenderingMode::POINTS; } @@ -240,7 +199,8 @@ int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type) { return GLTFMeshPrimitivesRenderingMode::TRIANGLES; } -int GLTFSerializer::getAccessorType(const QString& type) { +int GLTFSerializer::getAccessorType(const QString& type) +{ if (type == "SCALAR") { return GLTFAccessorType::SCALAR; } @@ -278,7 +238,8 @@ graphics::MaterialKey::OpacityMapMode GLTFSerializer::getMaterialAlphaMode(const return graphics::MaterialKey::OPACITY_MAP_BLEND; } -int GLTFSerializer::getCameraType(const QString& type) { +int GLTFSerializer::getCameraType(const QString& type) +{ if (type == "orthographic") { return GLTFCameraTypes::ORTHOGRAPHIC; } @@ -288,7 +249,8 @@ int GLTFSerializer::getCameraType(const QString& type) { return GLTFCameraTypes::PERSPECTIVE; } -int GLTFSerializer::getImageMimeType(const QString& mime) { +int GLTFSerializer::getImageMimeType(const QString& mime) +{ if (mime == "image/jpeg") { return GLTFImageMimetype::JPEG; } @@ -298,7 +260,8 @@ int GLTFSerializer::getImageMimeType(const QString& mime) { return GLTFImageMimetype::JPEG; } -int GLTFSerializer::getAnimationSamplerInterpolation(const QString& interpolation) { +int GLTFSerializer::getAnimationSamplerInterpolation(const QString& interpolation) +{ if (interpolation == "LINEAR") { return GLTFAnimationSamplerInterpolation::LINEAR; } @@ -309,7 +272,8 @@ bool GLTFSerializer::setAsset(const QJsonObject& object) { QJsonObject jsAsset; bool isAssetDefined = getObjectVal(object, "asset", jsAsset, _file.defined); if (isAssetDefined) { - if (!getStringVal(jsAsset, "version", _file.asset.version, _file.asset.defined) || _file.asset.version != "2.0") { + if (!getStringVal(jsAsset, "version", _file.asset.version, + _file.asset.defined) || _file.asset.version != "2.0") { return false; } getStringVal(jsAsset, "generator", _file.asset.generator, _file.asset.defined); @@ -318,8 +282,7 @@ bool GLTFSerializer::setAsset(const QJsonObject& object) { return isAssetDefined; } -GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::createAccessorSparseIndices( - const QJsonObject& object) { +GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::createAccessorSparseIndices(const QJsonObject& object) { GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices accessorSparseIndices; getIntVal(object, "bufferView", accessorSparseIndices.bufferView, accessorSparseIndices.defined); @@ -329,8 +292,7 @@ GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::crea return accessorSparseIndices; } -GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues GLTFSerializer::createAccessorSparseValues( - const QJsonObject& object) { +GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues GLTFSerializer::createAccessorSparseValues(const QJsonObject& object) { GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues accessorSparseValues; getIntVal(object, "bufferView", accessorSparseValues.bufferView, accessorSparseValues.defined); @@ -357,7 +319,7 @@ GLTFAccessor::GLTFAccessorSparse GLTFSerializer::createAccessorSparse(const QJso bool GLTFSerializer::addAccessor(const QJsonObject& object) { GLTFAccessor accessor; - + getIntVal(object, "bufferView", accessor.bufferView, accessor.defined); getIntVal(object, "byteOffset", accessor.byteOffset, accessor.defined); getIntVal(object, "componentType", accessor.componentType, accessor.defined); @@ -383,10 +345,10 @@ bool GLTFSerializer::addAccessor(const QJsonObject& object) { bool GLTFSerializer::addAnimation(const QJsonObject& object) { GLTFAnimation animation; - + QJsonArray channels; if (getObjectArrayVal(object, "channels", channels, animation.defined)) { - for (const QJsonValue& v : channels) { + foreach(const QJsonValue & v, channels) { if (v.isObject()) { GLTFChannel channel; getIntVal(v.toObject(), "sampler", channel.sampler, channel.defined); @@ -394,14 +356,14 @@ bool GLTFSerializer::addAnimation(const QJsonObject& object) { if (getObjectVal(v.toObject(), "target", jsChannel, channel.defined)) { getIntVal(jsChannel, "node", channel.target.node, channel.target.defined); getIntVal(jsChannel, "path", channel.target.path, channel.target.defined); - } + } } } } QJsonArray samplers; if (getObjectArrayVal(object, "samplers", samplers, animation.defined)) { - for (const QJsonValue& v : samplers) { + foreach(const QJsonValue & v, samplers) { if (v.isObject()) { GLTFAnimationSampler sampler; getIntVal(v.toObject(), "input", sampler.input, sampler.defined); @@ -413,7 +375,7 @@ bool GLTFSerializer::addAnimation(const QJsonObject& object) { } } } - + _file.animations.push_back(animation); return true; @@ -421,23 +383,23 @@ bool GLTFSerializer::addAnimation(const QJsonObject& object) { bool GLTFSerializer::addBufferView(const QJsonObject& object) { GLTFBufferView bufferview; - + getIntVal(object, "buffer", bufferview.buffer, bufferview.defined); getIntVal(object, "byteLength", bufferview.byteLength, bufferview.defined); getIntVal(object, "byteOffset", bufferview.byteOffset, bufferview.defined); getIntVal(object, "target", bufferview.target, bufferview.defined); - + _file.bufferviews.push_back(bufferview); - + return true; } bool GLTFSerializer::addBuffer(const QJsonObject& object) { GLTFBuffer buffer; - + getIntVal(object, "byteLength", buffer.byteLength, buffer.defined); - if (_url.toString().endsWith("glb")) { + if (_url.path().endsWith("glb")) { if (!_glbBinary.isEmpty()) { buffer.blob = _glbBinary; } else { @@ -450,13 +412,13 @@ bool GLTFSerializer::addBuffer(const QJsonObject& object) { } } _file.buffers.push_back(buffer); - + return true; } bool GLTFSerializer::addCamera(const QJsonObject& object) { GLTFCamera camera; - + QJsonObject jsPerspective; QJsonObject jsOrthographic; QString type; @@ -476,15 +438,15 @@ bool GLTFSerializer::addCamera(const QJsonObject& object) { } else if (getStringVal(object, "type", type, camera.defined)) { camera.type = getCameraType(type); } - + _file.cameras.push_back(camera); - + return true; } bool GLTFSerializer::addImage(const QJsonObject& object) { GLTFImage image; - + QString mime; getStringVal(object, "uri", image.uri, image.defined); if (image.uri.contains("data:image/png;base64,")) { @@ -494,18 +456,16 @@ bool GLTFSerializer::addImage(const QJsonObject& object) { } if (getStringVal(object, "mimeType", mime, image.defined)) { image.mimeType = getImageMimeType(mime); - } + } getIntVal(object, "bufferView", image.bufferView, image.defined); - + _file.images.push_back(image); return true; } -bool GLTFSerializer::getIndexFromObject(const QJsonObject& object, - const QString& field, - int& outidx, - QMap& defined) { +bool GLTFSerializer::getIndexFromObject(const QJsonObject& object, const QString& field, + int& outidx, QMap& defined) { QJsonObject subobject; if (getObjectVal(object, field, subobject, defined)) { QMap tmpdefined = QMap(); @@ -530,18 +490,23 @@ bool GLTFSerializer::addMaterial(const QJsonObject& object) { getDoubleVal(object, "alphaCutoff", material.alphaCutoff, material.defined); QJsonObject jsMetallicRoughness; if (getObjectVal(object, "pbrMetallicRoughness", jsMetallicRoughness, material.defined)) { - getDoubleArrayVal(jsMetallicRoughness, "baseColorFactor", material.pbrMetallicRoughness.baseColorFactor, + getDoubleArrayVal(jsMetallicRoughness, "baseColorFactor", + material.pbrMetallicRoughness.baseColorFactor, material.pbrMetallicRoughness.defined); - getIndexFromObject(jsMetallicRoughness, "baseColorTexture", material.pbrMetallicRoughness.baseColorTexture, + getIndexFromObject(jsMetallicRoughness, "baseColorTexture", + material.pbrMetallicRoughness.baseColorTexture, material.pbrMetallicRoughness.defined); - getDoubleVal(jsMetallicRoughness, "metallicFactor", material.pbrMetallicRoughness.metallicFactor, + getDoubleVal(jsMetallicRoughness, "metallicFactor", + material.pbrMetallicRoughness.metallicFactor, material.pbrMetallicRoughness.defined); - getDoubleVal(jsMetallicRoughness, "roughnessFactor", material.pbrMetallicRoughness.roughnessFactor, + getDoubleVal(jsMetallicRoughness, "roughnessFactor", + material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.defined); - getIndexFromObject(jsMetallicRoughness, "metallicRoughnessTexture", - material.pbrMetallicRoughness.metallicRoughnessTexture, material.pbrMetallicRoughness.defined); + getIndexFromObject(jsMetallicRoughness, "metallicRoughnessTexture", + material.pbrMetallicRoughness.metallicRoughnessTexture, + material.pbrMetallicRoughness.defined); } - _file.materials.push_back(material); + _file.materials.push_back(material); return true; } @@ -553,18 +518,18 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) { QJsonArray jsPrimitives; object.keys(); if (getObjectArrayVal(object, "primitives", jsPrimitives, mesh.defined)) { - for (const QJsonValue& prim : jsPrimitives) { + foreach(const QJsonValue & prim, jsPrimitives) { if (prim.isObject()) { GLTFMeshPrimitive primitive; QJsonObject jsPrimitive = prim.toObject(); getIntVal(jsPrimitive, "mode", primitive.mode, primitive.defined); getIntVal(jsPrimitive, "indices", primitive.indices, primitive.defined); getIntVal(jsPrimitive, "material", primitive.material, primitive.defined); - + QJsonObject jsAttributes; if (getObjectVal(jsPrimitive, "attributes", jsAttributes, primitive.defined)) { QStringList attrKeys = jsAttributes.keys(); - for (const QString& attrKey : attrKeys) { + foreach(const QString & attrKey, attrKeys) { int attrVal; getIntVal(jsAttributes, attrKey, attrVal, primitive.attributes.defined); primitive.attributes.values.insert(attrKey, attrVal); @@ -572,13 +537,14 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) { } QJsonArray jsTargets; - if (getObjectArrayVal(jsPrimitive, "targets", jsTargets, primitive.defined)) { - for (const QJsonValue& tar : jsTargets) { + if (getObjectArrayVal(jsPrimitive, "targets", jsTargets, primitive.defined)) + { + foreach(const QJsonValue & tar, jsTargets) { if (tar.isObject()) { QJsonObject jsTarget = tar.toObject(); QStringList tarKeys = jsTarget.keys(); GLTFMeshPrimitiveAttr target; - for (const QString& tarKey : tarKeys) { + foreach(const QString & tarKey, tarKeys) { int tarVal; getIntVal(jsTarget, tarKey, tarVal, target.defined); target.values.insert(tarKey, tarVal); @@ -586,7 +552,7 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) { primitive.targets.push_back(target); } } - } + } mesh.primitives.push_back(primitive); } } @@ -597,7 +563,9 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) { if (getObjectVal(object, "extras", jsExtras, mesh.defined)) { QJsonArray jsTargetNames; if (getObjectArrayVal(jsExtras, "targetNames", jsTargetNames, extras.defined)) { - foreach (const QJsonValue& tarName, jsTargetNames) { extras.targetNames.push_back(tarName.toString()); } + foreach (const QJsonValue& tarName, jsTargetNames) { + extras.targetNames.push_back(tarName.toString()); + } } mesh.extras = extras; } @@ -609,7 +577,7 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) { bool GLTFSerializer::addNode(const QJsonObject& object) { GLTFNode node; - + getStringVal(object, "name", node.name, node.defined); getIntVal(object, "camera", node.camera, node.defined); getIntVal(object, "mesh", node.mesh, node.defined); @@ -638,6 +606,7 @@ bool GLTFSerializer::addSampler(const QJsonObject& object) { _file.samplers.push_back(sampler); return true; + } bool GLTFSerializer::addScene(const QJsonObject& object) { @@ -663,10 +632,10 @@ bool GLTFSerializer::addSkin(const QJsonObject& object) { } bool GLTFSerializer::addTexture(const QJsonObject& object) { - GLTFTexture texture; + GLTFTexture texture; getIntVal(object, "sampler", texture.sampler, texture.defined); getIntVal(object, "source", texture.source, texture.defined); - + _file.textures.push_back(texture); return true; @@ -677,10 +646,10 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { hifi::ByteArray jsonChunk = data; - if (_url.toString().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) { + if (_url.path().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) { jsonChunk = setGLBChunks(data); - } - + } + QJsonDocument d = QJsonDocument::fromJson(jsonChunk); QJsonObject jsFile = d.object(); @@ -688,7 +657,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { if (success) { QJsonArray accessors; if (getObjectArrayVal(jsFile, "accessors", accessors, _file.defined)) { - for (const QJsonValue& accVal : accessors) { + foreach(const QJsonValue & accVal, accessors) { if (accVal.isObject()) { success = success && addAccessor(accVal.toObject()); } @@ -697,7 +666,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray animations; if (getObjectArrayVal(jsFile, "animations", animations, _file.defined)) { - for (const QJsonValue& animVal : accessors) { + foreach(const QJsonValue & animVal, accessors) { if (animVal.isObject()) { success = success && addAnimation(animVal.toObject()); } @@ -706,7 +675,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray bufferViews; if (getObjectArrayVal(jsFile, "bufferViews", bufferViews, _file.defined)) { - for (const QJsonValue& bufviewVal : bufferViews) { + foreach(const QJsonValue & bufviewVal, bufferViews) { if (bufviewVal.isObject()) { success = success && addBufferView(bufviewVal.toObject()); } @@ -715,7 +684,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray buffers; if (getObjectArrayVal(jsFile, "buffers", buffers, _file.defined)) { - for (const QJsonValue& bufVal : buffers) { + foreach(const QJsonValue & bufVal, buffers) { if (bufVal.isObject()) { success = success && addBuffer(bufVal.toObject()); } @@ -724,7 +693,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray cameras; if (getObjectArrayVal(jsFile, "cameras", cameras, _file.defined)) { - for (const QJsonValue& camVal : cameras) { + foreach(const QJsonValue & camVal, cameras) { if (camVal.isObject()) { success = success && addCamera(camVal.toObject()); } @@ -733,7 +702,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray images; if (getObjectArrayVal(jsFile, "images", images, _file.defined)) { - for (const QJsonValue& imgVal : images) { + foreach(const QJsonValue & imgVal, images) { if (imgVal.isObject()) { success = success && addImage(imgVal.toObject()); } @@ -742,7 +711,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray materials; if (getObjectArrayVal(jsFile, "materials", materials, _file.defined)) { - for (const QJsonValue& matVal : materials) { + foreach(const QJsonValue & matVal, materials) { if (matVal.isObject()) { success = success && addMaterial(matVal.toObject()); } @@ -751,7 +720,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray meshes; if (getObjectArrayVal(jsFile, "meshes", meshes, _file.defined)) { - for (const QJsonValue& meshVal : meshes) { + foreach(const QJsonValue & meshVal, meshes) { if (meshVal.isObject()) { success = success && addMesh(meshVal.toObject()); } @@ -760,7 +729,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray nodes; if (getObjectArrayVal(jsFile, "nodes", nodes, _file.defined)) { - for (const QJsonValue& nodeVal : nodes) { + foreach(const QJsonValue & nodeVal, nodes) { if (nodeVal.isObject()) { success = success && addNode(nodeVal.toObject()); } @@ -769,7 +738,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray samplers; if (getObjectArrayVal(jsFile, "samplers", samplers, _file.defined)) { - for (const QJsonValue& samVal : samplers) { + foreach(const QJsonValue & samVal, samplers) { if (samVal.isObject()) { success = success && addSampler(samVal.toObject()); } @@ -778,7 +747,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray scenes; if (getObjectArrayVal(jsFile, "scenes", scenes, _file.defined)) { - for (const QJsonValue& sceneVal : scenes) { + foreach(const QJsonValue & sceneVal, scenes) { if (sceneVal.isObject()) { success = success && addScene(sceneVal.toObject()); } @@ -787,7 +756,7 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray skins; if (getObjectArrayVal(jsFile, "skins", skins, _file.defined)) { - for (const QJsonValue& skinVal : skins) { + foreach(const QJsonValue & skinVal, skins) { if (skinVal.isObject()) { success = success && addSkin(skinVal.toObject()); } @@ -796,22 +765,51 @@ bool GLTFSerializer::parseGLTF(const hifi::ByteArray& data) { QJsonArray textures; if (getObjectArrayVal(jsFile, "textures", textures, _file.defined)) { - for (const QJsonValue& texVal : textures) { + foreach(const QJsonValue & texVal, textures) { if (texVal.isObject()) { success = success && addTexture(texVal.toObject()); } } } - } + } return success; } -const glm::mat4& GLTFSerializer::getModelTransform(const GLTFNode& node) { - return node.transform; +glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { + glm::mat4 tmat = glm::mat4(1.0); + + if (node.defined["matrix"] && node.matrix.size() == 16) { + tmat = glm::mat4(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3], + node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7], + node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11], + node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]); + } else { + + if (node.defined["scale"] && node.scale.size() == 3) { + glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]); + glm::mat4 s = glm::mat4(1.0); + s = glm::scale(s, scale); + tmat = s * tmat; + } + + if (node.defined["rotation"] && node.rotation.size() == 4) { + //quat(x,y,z,w) to quat(w,x,y,z) + glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); + tmat = glm::mat4_cast(rotquat) * tmat; + } + + if (node.defined["translation"] && node.translation.size() == 3) { + glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]); + glm::mat4 t = glm::mat4(1.0); + t = glm::translate(t, trans); + tmat = t * tmat; + } + } + return tmat; } void GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { - for (auto& skin : _file.skins) { + for (auto &skin : _file.skins) { GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; QVector matrices; addArrayFromAccessor(indicesAccessor, matrices); @@ -828,200 +826,111 @@ void GLTFSerializer::generateTargetData(int index, float weight, QVector; -ParentIndexMap findParentIndices(const QVector& nodes) { - ParentIndexMap parentIndices; - int numNodes = nodes.size(); - for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { - auto& gltfNode = nodes[nodeIndex]; - for (const auto& childIndex : gltfNode.children) { - parentIndices[childIndex] = nodeIndex; - } - } - return parentIndices; -} - -bool requiresNodeReordering(const ParentIndexMap& map) { - for (const auto& entry : map) { - if (entry.first < entry.second) { - return true; - } - } - return false; -} - -int findEdgeCount(const ParentIndexMap& parentIndices, int nodeIndex) { - auto parentsEnd = parentIndices.end(); - ParentIndexMap::const_iterator itr; - int result = 0; - while (parentsEnd != (itr = parentIndices.find(nodeIndex))) { - nodeIndex = itr->second; - ++result; - } - return result; -} - -using IndexBag = std::unordered_set; -using EdgeCountMap = std::map; -EdgeCountMap findEdgeCounts(int numNodes, const ParentIndexMap& map) { - EdgeCountMap edgeCounts; - // For each item, determine how many tranversals to a root node - for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { - // How many steps between this node and a root node? - int edgeCount = findEdgeCount(map, nodeIndex); - // Populate the result map - edgeCounts[edgeCount].insert(nodeIndex); - } - return edgeCounts; -} - -using ReorderMap = std::unordered_map; -ReorderMap buildReorderMap(const EdgeCountMap& map) { - ReorderMap result; - int newIndex = 0; - for (const auto& entry : map) { - const IndexBag& oldIndices = entry.second; - for (const auto& oldIndex : oldIndices) { - result.insert({ oldIndex, newIndex }); - ++newIndex; - } - } - return result; -} - -void reorderNodeIndices(QVector& indices, const ReorderMap& oldToNewIndexMap) { - for (auto& index : indices) { - index = oldToNewIndexMap.at(index); - } -} - -} // namespace gltf - -void GLTFFile::populateMaterialNames() { - // Build material names - QSet usedNames; - for (const auto& material : materials) { - if (!material.name.isEmpty()) { - usedNames.insert(material.name); - } - } - - int ukcount = 0; - const QString unknown{ "Default_%1" }; - for (auto& material : materials) { - QString generatedName = unknown.arg(ukcount++); - while (usedNames.contains(generatedName)) { - generatedName = unknown.arg(ukcount++); - } - material.name = generatedName; - material.defined.insert("name", true); - usedNames.insert(generatedName); - } -} - -void GLTFFile::reorderNodes(const std::unordered_map& oldToNewIndexMap) { - int numNodes = nodes.size(); - assert(numNodes == oldToNewIndexMap.size()); - QVector newNodes; - newNodes.resize(numNodes); - for (int oldIndex = 0; oldIndex < numNodes; ++oldIndex) { - const auto& oldNode = nodes[oldIndex]; - int newIndex = oldToNewIndexMap.at(oldIndex); - auto& newNode = newNodes[newIndex]; - // Write the new node - newNode = oldNode; - // Fixup the child indices - gltf::reorderNodeIndices(newNode.children, oldToNewIndexMap); - } - newNodes.swap(nodes); - - for (auto& subScene : scenes) { - gltf::reorderNodeIndices(subScene.nodes, oldToNewIndexMap); - } -} - -// Ensure that the GLTF nodes are ordered so -void GLTFFile::sortNodes() { - // Find all the parents - auto parentIndices = gltf::findParentIndices(nodes); - // If the nodes are already in a good order, we're done - if (!gltf::requiresNodeReordering(parentIndices)) { - return; - } - - auto edgeCounts = gltf::findEdgeCounts(nodes.size(), parentIndices); - auto oldToNewIndexMap = gltf::buildReorderMap(edgeCounts); - reorderNodes(oldToNewIndexMap); - assert(!gltf::requiresNodeReordering(gltf::findParentIndices(nodes))); -} - -void GLTFNode::normalizeTransform() { - if (defined["matrix"] && matrix.size() == 16) { - transform = glm::make_mat4(matrix.constData()); - } else { - transform = glm::mat4(1.0); - if (defined["scale"] && scale.size() == 3) { - glm::vec3 scaleVec = glm::make_vec3(scale.data()); - transform = glm::scale(transform, scaleVec); - } - - if (defined["rotation"] && rotation.size() == 4) { - glm::quat rotQ = glm::make_quat(rotation.data()); - transform = glm::mat4_cast(rotQ) * transform; - } - - if (defined["translation"] && translation.size() == 3) { - glm::vec3 transV = glm::make_vec3(translation.data()); - transform = glm::translate(glm::mat4(1.0), transV) * transform; - } - } -} - -void GLTFFile::normalizeNodeTransforms() { - for (auto& node : nodes) { - node.normalizeTransform(); - } -} - bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) { int numNodes = _file.nodes.size(); - - // Build joints - hfmModel.jointIndices["x"] = numNodes; - auto parentIndices = gltf::findParentIndices(_file.nodes); - const auto parentsEnd = parentIndices.end(); - for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { - HFMJoint joint; - auto& node = _file.nodes[nodeIndex]; - auto parentItr = parentIndices.find(nodeIndex); - if (parentsEnd == parentItr) { - joint.parentIndex = -1; - } else { - joint.parentIndex = parentItr->second; - } - joint.transform = getModelTransform(node); + //Build dependencies + QVector parents; + QVector sortedNodes; + parents.fill(-1, numNodes); + sortedNodes.reserve(numNodes); + int nodecount = 0; + foreach(auto &node, _file.nodes) { + foreach(int child, node.children) { + parents[child] = nodecount; + } + sortedNodes.push_back(nodecount); + ++nodecount; + } + + + // Build transforms + nodecount = 0; + foreach(auto &node, _file.nodes) { + // collect node transform + _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); + int parentIndex = parents[nodecount]; + while (parentIndex != -1) { + const auto& parentNode = _file.nodes[parentIndex]; + // collect transforms for a node's parents, grandparents, etc. + _file.nodes[nodecount].transforms.push_back(getModelTransform(parentNode)); + parentIndex = parents[parentIndex]; + } + ++nodecount; + } + + + // since parent indices must exist in the sorted list before any of their children, sortedNodes might not be initialized in the correct order + // therefore we need to re-initialize the order in which nodes will be parsed + QVector hasBeenSorted; + hasBeenSorted.fill(false, numNodes); + int i = 0; // initial index + while (i < numNodes) { + int currentNode = sortedNodes[i]; + int parentIndex = parents[currentNode]; + if (parentIndex == -1 || hasBeenSorted[parentIndex]) { + hasBeenSorted[currentNode] = true; + ++i; + } else { + int j = i + 1; // index of node to be sorted + while (j < numNodes) { + int nextNode = sortedNodes[j]; + parentIndex = parents[nextNode]; + if (parentIndex == -1 || hasBeenSorted[parentIndex]) { + // swap with currentNode + hasBeenSorted[nextNode] = true; + sortedNodes[i] = nextNode; + sortedNodes[j] = currentNode; + ++i; + currentNode = sortedNodes[i]; + } + ++j; + } + } + } + + + // Build map from original to new indices + QVector originalToNewNodeIndexMap; + originalToNewNodeIndexMap.fill(-1, numNodes); + for (int i = 0; i < numNodes; ++i) { + originalToNewNodeIndexMap[sortedNodes[i]] = i; + } + + + // Build joints + HFMJoint joint; + joint.distanceToParent = 0; + hfmModel.jointIndices["x"] = numNodes; + QVector globalTransforms; + globalTransforms.resize(numNodes); + + for (int nodeIndex : sortedNodes) { + auto& node = _file.nodes[nodeIndex]; + + joint.parentIndex = parents[nodeIndex]; + if (joint.parentIndex != -1) { + joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; + } + joint.transform = node.transforms.first(); joint.translation = extractTranslation(joint.transform); joint.rotation = glmExtractRotation(joint.transform); glm::vec3 scale = extractScale(joint.transform); joint.postTransform = glm::scale(glm::mat4(), scale); - joint.globalTransform = joint.transform; - // Nodes are sorted, so we can apply the full transform just by getting the global transform of the already defined parent - if (joint.parentIndex != -1 && joint.parentIndex != nodeIndex) { - const auto& parentJoint = hfmModel.joints[(size_t)joint.parentIndex]; - joint.transform = parentJoint.transform * joint.transform; - joint.globalTransform = joint.globalTransform * parentJoint.globalTransform; - } else { - joint.globalTransform = hfmModel.offset * joint.globalTransform; + joint.parentIndex = parents[nodeIndex]; + globalTransforms[nodeIndex] = joint.transform; + if (joint.parentIndex != -1) { + globalTransforms[nodeIndex] = globalTransforms[joint.parentIndex] * globalTransforms[nodeIndex]; + joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.name = node.name; joint.isSkeletonJoint = false; hfmModel.joints.push_back(joint); } + hfmModel.shapeVertices.resize(hfmModel.joints.size()); + // get offset transform from mapping float unitScaleFactor = 1.0f; @@ -1036,9 +945,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& std::vector globalBindTransforms; jointInverseBindTransforms.resize(numNodes); globalBindTransforms.resize(numNodes); - // Lookup between the GLTF mesh and the skin - std::vector gltfMeshToSkin; - gltfMeshToSkin.resize(_file.meshes.size(), -1); hfmModel.hasSkeletonJoints = !_file.skins.isEmpty(); if (hfmModel.hasSkeletonJoints) { @@ -1046,8 +952,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& getSkinInverseBindMatrices(inverseBindValues); for (int jointIndex = 0; jointIndex < numNodes; ++jointIndex) { - int nodeIndex = jointIndex; - auto& joint = hfmModel.joints[jointIndex]; + int nodeIndex = sortedNodes[jointIndex]; + auto joint = hfmModel.joints[jointIndex]; for (int s = 0; s < _file.skins.size(); ++s) { const auto& skin = _file.skins[s]; @@ -1073,490 +979,642 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex])); hfmModel.bindExtents.addPoint(bindTranslation); } + hfmModel.joints[jointIndex] = joint; + } + } + + + // Build materials + QVector materialIDs; + QString unknown = "Default"; + int ukcount = 0; + foreach(auto material, _file.materials) { + if (!material.defined["name"]) { + QString name = unknown + QString::number(++ukcount); + material.name = name; + material.defined.insert("name", true); } - std::vector skinToRootJoint; - skinToRootJoint.resize(_file.skins.size(), 0); - for (int jointIndex = 0; jointIndex < numNodes; ++jointIndex) { - const auto& node = _file.nodes[jointIndex]; - if (node.skin != -1) { - skinToRootJoint[node.skin] = jointIndex; - if (node.mesh != -1) { - gltfMeshToSkin[node.mesh] = node.skin; + QString mid = material.name; + materialIDs.push_back(mid); + } + + for (int i = 0; i < materialIDs.size(); ++i) { + QString& matid = materialIDs[i]; + hfmModel.materials[matid] = HFMMaterial(); + HFMMaterial& hfmMaterial = hfmModel.materials[matid]; + hfmMaterial._material = std::make_shared(); + hfmMaterial.name = hfmMaterial.materialID = matid; + setHFMMaterial(hfmMaterial, _file.materials[i]); + } + + + // Build meshes + nodecount = 0; + hfmModel.meshExtents.reset(); + for (int nodeIndex : sortedNodes) { + auto& node = _file.nodes[nodeIndex]; + + if (node.defined["mesh"]) { + + hfmModel.meshes.append(HFMMesh()); + HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; + if (!hfmModel.hasSkeletonJoints) { + HFMCluster cluster; + cluster.jointIndex = nodecount; + cluster.inverseBindMatrix = glm::mat4(); + cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); + mesh.clusters.append(cluster); + } else { // skinned model + for (int j = 0; j < numNodes; ++j) { + HFMCluster cluster; + cluster.jointIndex = j; + cluster.inverseBindMatrix = jointInverseBindTransforms[j]; + cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); + mesh.clusters.append(cluster); } } - } - - for (int skinIndex = 0; skinIndex < _file.skins.size(); ++skinIndex) { - const auto& skin = _file.skins[skinIndex]; - hfmModel.skinDeformers.emplace_back(); - auto& skinDeformer = hfmModel.skinDeformers.back(); - - // Add the nodes being referred to for skinning - for (int skinJointIndex : skin.joints) { - hfm::Cluster cluster; - cluster.jointIndex = skinJointIndex; - cluster.inverseBindMatrix = jointInverseBindTransforms[skinJointIndex]; - cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); - skinDeformer.clusters.push_back(cluster); - } - - // Always append a cluster referring to the root joint at the end - int rootJointIndex = skinToRootJoint[skinIndex]; - hfm::Cluster root; - root.jointIndex = rootJointIndex; + HFMCluster root; + root.jointIndex = 0; root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); - skinDeformer.clusters.push_back(root); - } - } + mesh.clusters.append(root); - for (const auto& material : _file.materials) { - const QString& matid = material.name; - hfmModel.materials.emplace_back(); - HFMMaterial& hfmMaterial = hfmModel.materials.back(); - hfmMaterial._material = std::make_shared(); - hfmMaterial.materialID = matid; - setHFMMaterial(hfmMaterial, material); - } - - - int gltfMeshCount = _file.meshes.size(); - hfmModel.meshExtents.reset(); - std::vector> templateShapePerPrimPerGLTFMesh; - for (int gltfMeshIndex = 0; gltfMeshIndex < gltfMeshCount; ++gltfMeshIndex) { - const auto& gltfMesh = _file.meshes[gltfMeshIndex]; - hfmModel.meshes.emplace_back(); - // NOTE: The number of hfm meshes may be greater than the number of gltf meshes, if a gltf mesh has primitives with different vertex attributes. In that case, this mesh reference may be reassigned. - hfm::Mesh* meshPtr = &hfmModel.meshes.back(); - const size_t firstMeshIndexForGLTFMesh = hfmModel.meshes.size() - 1; - meshPtr->meshIndex = gltfMeshIndex; - templateShapePerPrimPerGLTFMesh.emplace_back(); - std::vector& templateShapePerPrim = templateShapePerPrimPerGLTFMesh.back(); - - QSet primitiveAttributes; - if (!gltfMesh.primitives.empty()) { - for (const auto& attribute : gltfMesh.primitives[0].attributes.values.keys()) { - primitiveAttributes.insert(attribute); - } - } - std::vector> primitiveAttributeVariants; - - int primCount = (int)gltfMesh.primitives.size(); - size_t hfmMeshIndex = firstMeshIndexForGLTFMesh; - for(int primIndex = 0; primIndex < primCount; ++primIndex) { - auto& primitive = gltfMesh.primitives[primIndex]; - - QList keys = primitive.attributes.values.keys(); - QSet newPrimitiveAttributes; - for (const auto& key : keys) { - newPrimitiveAttributes.insert(key); - } - if (newPrimitiveAttributes != primitiveAttributes) { - assert(primIndex != 0); - - // We need to use a different mesh because the vertex attributes are different - auto attributeVariantIt = std::find(primitiveAttributeVariants.cbegin(), primitiveAttributeVariants.cend(), newPrimitiveAttributes); - if (attributeVariantIt == primitiveAttributeVariants.cend()) { - // Need to allocate a new mesh - hfmModel.meshes.emplace_back(); - meshPtr = &hfmModel.meshes.back(); - hfmMeshIndex = hfmModel.meshes.size() - 1; - meshPtr->meshIndex = gltfMeshIndex; - primitiveAttributeVariants.push_back(newPrimitiveAttributes); - } else { - // An hfm mesh already exists for this gltf mesh with the same vertex attributes. Use it again. - auto variantIndex = (size_t)(attributeVariantIt - primitiveAttributeVariants.cbegin()); - hfmMeshIndex = firstMeshIndexForGLTFMesh + variantIndex; - meshPtr = &hfmModel.meshes[hfmMeshIndex]; + QList meshAttributes; + foreach(auto &primitive, _file.meshes[node.mesh].primitives) { + QList keys = primitive.attributes.values.keys(); + foreach (auto &key, keys) { + if (!meshAttributes.contains(key)) { + meshAttributes.push_back(key); + } } - primitiveAttributes = newPrimitiveAttributes; - } - // Now, allocate the part for the correct mesh... - hfm::Mesh& mesh = *meshPtr; - mesh.parts.emplace_back(); - hfm::MeshPart& part = mesh.parts.back(); - // ...and keep track of the relationship between the gltf mesh/primitive and the hfm mesh/part - templateShapePerPrim.emplace_back(); - hfm::Shape& templateShape = templateShapePerPrim.back(); - templateShape.mesh = (uint32_t)hfmMeshIndex; - templateShape.meshPart = (uint32_t)(mesh.parts.size() - 1); - templateShape.material = primitive.material; - - int indicesAccessorIdx = primitive.indices; - - GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx]; - - // Buffers - constexpr int VERTEX_STRIDE = 3; - constexpr int NORMAL_STRIDE = 3; - constexpr int TEX_COORD_STRIDE = 2; - - QVector indices; - QVector vertices; - QVector normals; - QVector tangents; - QVector texcoords; - QVector texcoords2; - QVector colors; - QVector joints; - QVector weights; - - static int tangentStride = 4; - static int colorStride = 3; - static int jointStride = 4; - static int weightStride = 4; - - bool success = addArrayFromAccessor(indicesAccessor, indices); - - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; - continue; } - // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh - int prevMeshVerticesCount = mesh.vertices.count(); - QVector clusterJoints; - QVector clusterWeights; + foreach(auto &primitive, _file.meshes[node.mesh].primitives) { + HFMMeshPart part = HFMMeshPart(); - for(auto &key : keys) { - int accessorIdx = primitive.attributes.values[key]; - GLTFAccessor& accessor = _file.accessors[accessorIdx]; - const auto vertexAttribute = GLTFVertexAttribute::fromString(key); - switch (vertexAttribute) { - case GLTFVertexAttribute::POSITION: - success = addArrayFromAttribute(vertexAttribute, accessor, vertices); - break; + int indicesAccessorIdx = primitive.indices; - case GLTFVertexAttribute::NORMAL: - success = addArrayFromAttribute(vertexAttribute, accessor, normals); - break; + GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx]; - case GLTFVertexAttribute::TANGENT: - success = addArrayFromAttribute(vertexAttribute, accessor, tangents); - tangentStride = GLTFAccessorType::count((GLTFAccessorType::Value)accessor.type); - break; + // Buffers + QVector indices; + QVector vertices; + int verticesStride = 3; + QVector normals; + int normalStride = 3; + QVector tangents; + int tangentStride = 4; + QVector texcoords; + int texCoordStride = 2; + QVector texcoords2; + int texCoord2Stride = 2; + QVector colors; + int colorStride = 3; + QVector joints; + int jointStride = 4; + QVector weights; + int weightStride = 4; - case GLTFVertexAttribute::TEXCOORD_0: - success = addArrayFromAttribute(vertexAttribute, accessor, texcoords); - break; + bool success = addArrayFromAccessor(indicesAccessor, indices); - case GLTFVertexAttribute::TEXCOORD_1: - success = addArrayFromAttribute(vertexAttribute, accessor, texcoords2); - break; - - case GLTFVertexAttribute::COLOR_0: - success = addArrayFromAttribute(vertexAttribute, accessor, colors); - colorStride = GLTFAccessorType::count((GLTFAccessorType::Value)accessor.type); - break; - - case GLTFVertexAttribute::JOINTS_0: - success = addArrayFromAttribute(vertexAttribute, accessor, joints); - jointStride = GLTFAccessorType::count((GLTFAccessorType::Value)accessor.type); - break; - - case GLTFVertexAttribute::WEIGHTS_0: - success = addArrayFromAttribute(vertexAttribute, accessor, weights); - weightStride = GLTFAccessorType::count((GLTFAccessorType::Value)accessor.type); - break; - - default: - success = false; - break; - } if (!success) { + qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; continue; } - } - // Validation stage - if (indices.count() == 0) { - qWarning(modelformat) << "Missing indices for model " << _url; - continue; - } - if (vertices.count() == 0) { - qWarning(modelformat) << "Missing vertices for model " << _url; - continue; - } + // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh + int prevMeshVerticesCount = mesh.vertices.count(); - int partVerticesCount = vertices.size() / 3; + QList keys = primitive.attributes.values.keys(); + QVector clusterJoints; + QVector clusterWeights; - QVector validatedIndices; - for (int n = 0; n < indices.count(); ++n) { - if (indices[n] < partVerticesCount) { - validatedIndices.push_back(indices[n] + prevMeshVerticesCount); - } else { - validatedIndices = QVector(); - break; + foreach(auto &key, keys) { + int accessorIdx = primitive.attributes.values[key]; + + GLTFAccessor& accessor = _file.accessors[accessorIdx]; + + if (key == "POSITION") { + if (accessor.type != GLTFAccessorType::VEC3) { + qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, vertices); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; + continue; + } + } else if (key == "NORMAL") { + if (accessor.type != GLTFAccessorType::VEC3) { + qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, normals); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; + continue; + } + } else if (key == "TANGENT") { + if (accessor.type == GLTFAccessorType::VEC4) { + tangentStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + tangentStride = 3; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, tangents); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url; + tangentStride = 0; + continue; + } + } else if (key == "TEXCOORD_0") { + success = addArrayFromAccessor(accessor, texcoords); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; + continue; + } + + if (accessor.type != GLTFAccessorType::VEC2) { + qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url; + continue; + } + } else if (key == "TEXCOORD_1") { + success = addArrayFromAccessor(accessor, texcoords2); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; + continue; + } + + if (accessor.type != GLTFAccessorType::VEC2) { + qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url; + continue; + } + } else if (key == "COLOR_0") { + if (accessor.type == GLTFAccessorType::VEC4) { + colorStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + colorStride = 3; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, colors); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url; + continue; + } + } else if (key == "JOINTS_0") { + if (accessor.type == GLTFAccessorType::VEC4) { + jointStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + jointStride = 3; + } else if (accessor.type == GLTFAccessorType::VEC2) { + jointStride = 2; + } else if (accessor.type == GLTFAccessorType::SCALAR) { + jointStride = 1; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, joints); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url; + continue; + } + } else if (key == "WEIGHTS_0") { + if (accessor.type == GLTFAccessorType::VEC4) { + weightStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + weightStride = 3; + } else if (accessor.type == GLTFAccessorType::VEC2) { + weightStride = 2; + } else if (accessor.type == GLTFAccessorType::SCALAR) { + weightStride = 1; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, weights); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url; + continue; + } + } } - } - if (validatedIndices.size() == 0) { - qWarning(modelformat) << "Indices out of range for model " << _url; - continue; - } - - part.triangleIndices.append(validatedIndices); - - mesh.vertices.reserve(mesh.vertices.size() + partVerticesCount); - for (int n = 0; n < vertices.size(); n = n + VERTEX_STRIDE) { - mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2])); - } - - mesh.normals.reserve(mesh.normals.size() + partVerticesCount); - for (int n = 0; n < normals.size(); n = n + NORMAL_STRIDE) { - mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2])); - } - - if (tangents.size() == partVerticesCount * tangentStride) { - mesh.tangents.reserve(mesh.tangents.size() + partVerticesCount); - for (int n = 0; n < tangents.size(); n += tangentStride) { - float tanW = tangentStride == 4 ? tangents[n + 3] : 1; - mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2])); + // Validation stage + if (indices.count() == 0) { + qWarning(modelformat) << "Missing indices for model " << _url; + continue; } - } - - if (texcoords.size() == partVerticesCount * TEX_COORD_STRIDE) { - mesh.texCoords.reserve(mesh.texCoords.size() + partVerticesCount); - for (int n = 0; n < texcoords.size(); n = n + 2) { - mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); + if (vertices.count() == 0) { + qWarning(modelformat) << "Missing vertices for model " << _url; + continue; } - } else if (primitiveAttributes.contains("TEXCOORD_0")) { - mesh.texCoords.resize(mesh.texCoords.size() + partVerticesCount); - } - if (texcoords2.size() == partVerticesCount * TEX_COORD_STRIDE) { - mesh.texCoords1.reserve(mesh.texCoords1.size() + partVerticesCount); - for (int n = 0; n < texcoords2.size(); n = n + 2) { - mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1])); - } - } else if (primitiveAttributes.contains("TEXCOORD_1")) { - mesh.texCoords1.resize(mesh.texCoords1.size() + partVerticesCount); - } + int partVerticesCount = vertices.size() / 3; - if (colors.size() == partVerticesCount * colorStride) { - mesh.colors.reserve(mesh.colors.size() + partVerticesCount); - for (int n = 0; n < colors.size(); n += colorStride) { - mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2])); - } - } else if (primitiveAttributes.contains("COLOR_0")) { - mesh.colors.reserve(mesh.colors.size() + partVerticesCount); - for (int i = 0; i < partVerticesCount; ++i) { - mesh.colors.push_back(glm::vec3(1.0f)); - } - } + // generate the normals if they don't exist + if (normals.size() == 0) { + QVector newIndices; + QVector newVertices; + QVector newNormals; + QVector newTexcoords; + QVector newTexcoords2; + QVector newColors; + QVector newJoints; + QVector newWeights; - const int WEIGHTS_PER_VERTEX = 4; + for (int n = 0; n < indices.size(); n = n + 3) { + int v1_index = (indices[n + 0] * 3); + int v2_index = (indices[n + 1] * 3); + int v3_index = (indices[n + 2] * 3); - if (weights.size() == partVerticesCount * weightStride) { - for (int n = 0; n < weights.size(); n += weightStride) { - clusterWeights.push_back(weights[n]); - if (weightStride > 1) { - clusterWeights.push_back(weights[n + 1]); - if (weightStride > 2) { - clusterWeights.push_back(weights[n + 2]); - if (weightStride > 3) { - clusterWeights.push_back(weights[n + 3]); + glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]); + glm::vec3 v2 = glm::vec3(vertices[v2_index], vertices[v2_index + 1], vertices[v2_index + 2]); + glm::vec3 v3 = glm::vec3(vertices[v3_index], vertices[v3_index + 1], vertices[v3_index + 2]); + + newVertices.append(v1.x); + newVertices.append(v1.y); + newVertices.append(v1.z); + newVertices.append(v2.x); + newVertices.append(v2.y); + newVertices.append(v2.z); + newVertices.append(v3.x); + newVertices.append(v3.y); + newVertices.append(v3.z); + + glm::vec3 norm = glm::normalize(glm::cross(v2 - v1, v3 - v1)); + + newNormals.append(norm.x); + newNormals.append(norm.y); + newNormals.append(norm.z); + newNormals.append(norm.x); + newNormals.append(norm.y); + newNormals.append(norm.z); + newNormals.append(norm.x); + newNormals.append(norm.y); + newNormals.append(norm.z); + + if (texcoords.size() == partVerticesCount * texCoordStride) { + GLTF_APPEND_ARRAY_2(newTexcoords, texcoords) + } + + if (texcoords2.size() == partVerticesCount * texCoord2Stride) { + GLTF_APPEND_ARRAY_2(newTexcoords2, texcoords2) + } + + if (colors.size() == partVerticesCount * colorStride) { + if (colorStride == 4) { + GLTF_APPEND_ARRAY_4(newColors, colors) } else { + GLTF_APPEND_ARRAY_3(newColors, colors) + } + } + + if (joints.size() == partVerticesCount * jointStride) { + if (jointStride == 4) { + GLTF_APPEND_ARRAY_4(newJoints, joints) + } else if (jointStride == 3) { + GLTF_APPEND_ARRAY_3(newJoints, joints) + } else if (jointStride == 2) { + GLTF_APPEND_ARRAY_2(newJoints, joints) + } else { + GLTF_APPEND_ARRAY_1(newJoints, joints) + } + } + + if (weights.size() == partVerticesCount * weightStride) { + if (weightStride == 4) { + GLTF_APPEND_ARRAY_4(newWeights, weights) + } else if (weightStride == 3) { + GLTF_APPEND_ARRAY_3(newWeights, weights) + } else if (weightStride == 2) { + GLTF_APPEND_ARRAY_2(newWeights, weights) + } else { + GLTF_APPEND_ARRAY_1(newWeights, weights) + } + } + newIndices.append(n); + newIndices.append(n + 1); + newIndices.append(n + 2); + } + + vertices = newVertices; + normals = newNormals; + tangents = QVector(); + texcoords = newTexcoords; + texcoords2 = newTexcoords2; + colors = newColors; + joints = newJoints; + weights = newWeights; + indices = newIndices; + + partVerticesCount = vertices.size() / 3; + } + + QVector validatedIndices; + for (int n = 0; n < indices.count(); ++n) { + if (indices[n] < partVerticesCount) { + validatedIndices.push_back(indices[n] + prevMeshVerticesCount); + } else { + validatedIndices = QVector(); + break; + } + } + + if (validatedIndices.size() == 0) { + qWarning(modelformat) << "Indices out of range for model " << _url; + continue; + } + + part.triangleIndices.append(validatedIndices); + + for (int n = 0; n < vertices.size(); n = n + verticesStride) { + mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2])); + } + + for (int n = 0; n < normals.size(); n = n + normalStride) { + mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2])); + } + + // TODO: add correct tangent generation + if (tangents.size() == partVerticesCount * tangentStride) { + for (int n = 0; n < tangents.size(); n += tangentStride) { + float tanW = tangentStride == 4 ? tangents[n + 3] : 1; + mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2])); + } + } else { + if (meshAttributes.contains("TANGENT")) { + for (int i = 0; i < partVerticesCount; ++i) { + mesh.tangents.push_back(glm::vec3(0.0f, 0.0f, 0.0f)); + } + } + } + + if (texcoords.size() == partVerticesCount * texCoordStride) { + for (int n = 0; n < texcoords.size(); n = n + 2) { + mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); + } + } else { + if (meshAttributes.contains("TEXCOORD_0")) { + for (int i = 0; i < partVerticesCount; ++i) { + mesh.texCoords.push_back(glm::vec2(0.0f, 0.0f)); + } + } + } + + if (texcoords2.size() == partVerticesCount * texCoord2Stride) { + for (int n = 0; n < texcoords2.size(); n = n + 2) { + mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1])); + } + } else { + if (meshAttributes.contains("TEXCOORD_1")) { + for (int i = 0; i < partVerticesCount; ++i) { + mesh.texCoords1.push_back(glm::vec2(0.0f, 0.0f)); + } + } + } + + if (colors.size() == partVerticesCount * colorStride) { + for (int n = 0; n < colors.size(); n += colorStride) { + mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2])); + } + } else { + if (meshAttributes.contains("COLOR_0")) { + for (int i = 0; i < partVerticesCount; ++i) { + mesh.colors.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); + } + } + } + + if (joints.size() == partVerticesCount * jointStride) { + for (int n = 0; n < joints.size(); n += jointStride) { + clusterJoints.push_back(joints[n]); + if (jointStride > 1) { + clusterJoints.push_back(joints[n + 1]); + if (jointStride > 2) { + clusterJoints.push_back(joints[n + 2]); + if (jointStride > 3) { + clusterJoints.push_back(joints[n + 3]); + } else { + clusterJoints.push_back(0); + } + } else { + clusterJoints.push_back(0); + clusterJoints.push_back(0); + } + } else { + clusterJoints.push_back(0); + clusterJoints.push_back(0); + clusterJoints.push_back(0); + } + } + } else { + if (meshAttributes.contains("JOINTS_0")) { + for (int i = 0; i < partVerticesCount; ++i) { + for (int j = 0; j < 4; ++j) { + clusterJoints.push_back(0); + } + } + } + } + + if (weights.size() == partVerticesCount * weightStride) { + for (int n = 0; n < weights.size(); n += weightStride) { + clusterWeights.push_back(weights[n]); + if (weightStride > 1) { + clusterWeights.push_back(weights[n + 1]); + if (weightStride > 2) { + clusterWeights.push_back(weights[n + 2]); + if (weightStride > 3) { + clusterWeights.push_back(weights[n + 3]); + } else { + clusterWeights.push_back(0.0f); + } + } else { + clusterWeights.push_back(0.0f); clusterWeights.push_back(0.0f); } } else { clusterWeights.push_back(0.0f); clusterWeights.push_back(0.0f); + clusterWeights.push_back(0.0f); + } + } + } else { + if (meshAttributes.contains("WEIGHTS_0")) { + for (int i = 0; i < partVerticesCount; ++i) { + clusterWeights.push_back(1.0f); + for (int j = 1; j < 4; ++j) { + clusterWeights.push_back(0.0f); + } } - } else { - clusterWeights.push_back(0.0f); - clusterWeights.push_back(0.0f); - clusterWeights.push_back(0.0f); } } - } else if (primitiveAttributes.contains("WEIGHTS_0")) { - for (int i = 0; i < partVerticesCount; ++i) { - clusterWeights.push_back(1.0f); - for (int j = 0; j < WEIGHTS_PER_VERTEX; ++j) { - clusterWeights.push_back(0.0f); + + // Build weights (adapted from FBXSerializer.cpp) + if (hfmModel.hasSkeletonJoints) { + int prevMeshClusterIndexCount = mesh.clusterIndices.count(); + int prevMeshClusterWeightCount = mesh.clusterWeights.count(); + const int WEIGHTS_PER_VERTEX = 4; + const float ALMOST_HALF = 0.499f; + int numVertices = mesh.vertices.size() - prevMeshVerticesCount; + + // Append new cluster indices and weights for this mesh part + for (int i = 0; i < numVertices * WEIGHTS_PER_VERTEX; ++i) { + mesh.clusterIndices.push_back(mesh.clusters.size() - 1); + mesh.clusterWeights.push_back(0); } - } - } - // Compress floating point weights to uint16_t for graphics runtime - // TODO: If the GLTF skinning weights are already in integer format, we should just copy the data - if (!clusterWeights.empty()) { - size_t numWeights = 4 * (mesh.vertices.size() - (uint32_t)prevMeshVerticesCount); - size_t newWeightsStart = mesh.clusterWeights.size(); - size_t newWeightsEnd = newWeightsStart + numWeights; - mesh.clusterWeights.reserve(newWeightsEnd); - for (int weightIndex = 0; weightIndex < clusterWeights.size(); ++weightIndex) { - // Per the GLTF specification - uint16_t weight = std::round(clusterWeights[weightIndex] * 65535.0f); - mesh.clusterWeights.push_back(weight); - } - mesh.clusterWeightsPerVertex = WEIGHTS_PER_VERTEX; - } + for (int c = 0; c < clusterJoints.size(); ++c) { + mesh.clusterIndices[prevMeshClusterIndexCount + c] = + originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]]; + } - if (joints.size() == partVerticesCount * jointStride) { - for (int n = 0; n < joints.size(); n += jointStride) { - mesh.clusterIndices.push_back(joints[n]); - if (jointStride > 1) { - mesh.clusterIndices.push_back(joints[n + 1]); - if (jointStride > 2) { - mesh.clusterIndices.push_back(joints[n + 2]); - if (jointStride > 3) { - mesh.clusterIndices.push_back(joints[n + 3]); - } else { - mesh.clusterIndices.push_back(0); + // normalize and compress to 16-bits + for (int i = 0; i < numVertices; ++i) { + int j = i * WEIGHTS_PER_VERTEX; + + float totalWeight = 0.0f; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + totalWeight += clusterWeights[k]; + } + if (totalWeight > 0.0f) { + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + mesh.clusterWeights[prevMeshClusterWeightCount + k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF); } } else { - mesh.clusterIndices.push_back(0); - mesh.clusterIndices.push_back(0); + mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); } - } else { - mesh.clusterIndices.push_back(0); - mesh.clusterIndices.push_back(0); - mesh.clusterIndices.push_back(0); - } - } - } else if (primitiveAttributes.contains("JOINTS_0")) { - for (int i = 0; i < partVerticesCount; ++i) { - for (int j = 0; j < 4; ++j) { - mesh.clusterIndices.push_back(0); - } - } - } + for (int clusterIndex = 0; clusterIndex < mesh.clusters.size() - 1; ++clusterIndex) { + ShapeVertices& points = hfmModel.shapeVertices.at(clusterIndex); + glm::vec3 globalMeshScale = extractScale(globalTransforms[nodeIndex]); + const glm::mat4 meshToJoint = glm::scale(glm::mat4(), globalMeshScale) * jointInverseBindTransforms[clusterIndex]; - if (!mesh.clusterIndices.empty()) { - int skinIndex = gltfMeshToSkin[gltfMeshIndex]; - if (skinIndex != -1) { - const auto& deformer = hfmModel.skinDeformers[(size_t)skinIndex]; - std::vector oldToNew; - oldToNew.resize(_file.nodes.size()); - for (uint16_t clusterIndex = 0; clusterIndex < deformer.clusters.size() - 1; ++clusterIndex) { - const auto& cluster = deformer.clusters[clusterIndex]; - oldToNew[(size_t)cluster.jointIndex] = clusterIndex; - } - } - } - - // populate the texture coordinates if they don't exist - if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) { - for (int i = 0; i < part.triangleIndices.size(); ++i) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); } - } - - // Build morph targets (blend shapes) - if (!primitive.targets.isEmpty()) { - - // Build list of blendshapes from FST - typedef QPair WeightedIndex; - hifi::VariantHash blendshapeMappings = mapping.value("bs").toHash(); - QMultiHash blendshapeIndices; - - for (int i = 0;; ++i) { - hifi::ByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i]; - if (blendshapeName.isEmpty()) { - break; - } - QList mappings = blendshapeMappings.values(blendshapeName); - foreach (const QVariant& mapping, mappings) { - QVariantList blendshapeMapping = mapping.toList(); - blendshapeIndices.insert(blendshapeMapping.at(0).toByteArray(), WeightedIndex(i, blendshapeMapping.at(1).toFloat())); - } - } - - // glTF morph targets may or may not have names. if they are labeled, add them based on - // the corresponding names from the FST. otherwise, just add them in the order they are given - mesh.blendshapes.resize(blendshapeMappings.size()); - auto values = blendshapeIndices.values(); - auto keys = blendshapeIndices.keys(); - auto names = gltfMesh.extras.targetNames; - QVector weights = gltfMesh.weights; - - for (int weightedIndex = 0; weightedIndex < values.size(); ++weightedIndex) { - float weight = 0.1f; - int indexFromMapping = weightedIndex; - int targetIndex = weightedIndex; - hfmModel.blendshapeChannelNames.push_back("target_" + QString::number(weightedIndex)); - - if (!names.isEmpty()) { - targetIndex = names.indexOf(keys[weightedIndex]); - indexFromMapping = values[weightedIndex].first; - weight = weight * values[weightedIndex].second; - hfmModel.blendshapeChannelNames[weightedIndex] = keys[weightedIndex]; - } - HFMBlendshape& blendshape = mesh.blendshapes[indexFromMapping]; - blendshape.indices = part.triangleIndices; - auto target = primitive.targets[targetIndex]; - - QVector normals; - QVector vertices; - - if (weights.size() == primitive.targets.size()) { - int targetWeight = weights[targetIndex]; - if (targetWeight != 0) { - weight = weight * targetWeight; - } - } - - if (target.values.contains((QString) "NORMAL")) { - generateTargetData(target.values.value((QString) "NORMAL"), weight, normals); - } - if (target.values.contains((QString) "POSITION")) { - generateTargetData(target.values.value((QString) "POSITION"), weight, vertices); - } - bool isNewBlendshape = blendshape.vertices.size() < vertices.size(); - int count = 0; - for (int i : blendshape.indices) { - if (isNewBlendshape) { - blendshape.vertices.push_back(vertices[i]); - blendshape.normals.push_back(normals[i]); - } else { - blendshape.vertices[count] = blendshape.vertices[count] + vertices[i]; - blendshape.normals[count] = blendshape.normals[count] + normals[i]; - ++count; + const float EXPANSION_WEIGHT_THRESHOLD = 0.25f; + if (mesh.clusterWeights[j] >= EXPANSION_WEIGHT_THRESHOLD) { + // TODO: fix transformed vertices being pushed back + auto& vertex = mesh.vertices[i]; + const glm::mat4 vertexTransform = meshToJoint * (glm::translate(glm::mat4(), vertex)); + glm::vec3 transformedVertex = hfmModel.joints[clusterIndex].translation * (extractTranslation(vertexTransform)); + points.push_back(transformedVertex); + } } } } - } + + if (primitive.defined["material"]) { + part.materialID = materialIDs[primitive.material]; + } + mesh.parts.push_back(part); + + // populate the texture coordinates if they don't exist + if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) { + for (int i = 0; i < part.triangleIndices.size(); ++i) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); } + } + + // Build morph targets (blend shapes) + if (!primitive.targets.isEmpty()) { + + // Build list of blendshapes from FST + typedef QPair WeightedIndex; + hifi::VariantHash blendshapeMappings = mapping.value("bs").toHash(); + QMultiHash blendshapeIndices; + + for (int i = 0;; ++i) { + hifi::ByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i]; + if (blendshapeName.isEmpty()) { + break; + } + QList mappings = blendshapeMappings.values(blendshapeName); + foreach (const QVariant& mapping, mappings) { + QVariantList blendshapeMapping = mapping.toList(); + blendshapeIndices.insert(blendshapeMapping.at(0).toByteArray(), WeightedIndex(i, blendshapeMapping.at(1).toFloat())); + } + } + + // glTF morph targets may or may not have names. if they are labeled, add them based on + // the corresponding names from the FST. otherwise, just add them in the order they are given + mesh.blendshapes.resize(blendshapeMappings.size()); + auto values = blendshapeIndices.values(); + auto keys = blendshapeIndices.keys(); + auto names = _file.meshes[node.mesh].extras.targetNames; + QVector weights = _file.meshes[node.mesh].weights; + + for (int weightedIndex = 0; weightedIndex < values.size(); ++weightedIndex) { + float weight = 0.1f; + int indexFromMapping = weightedIndex; + int targetIndex = weightedIndex; + hfmModel.blendshapeChannelNames.push_back("target_" + QString::number(weightedIndex)); + + if (!names.isEmpty()) { + targetIndex = names.indexOf(keys[weightedIndex]); + indexFromMapping = values[weightedIndex].first; + weight = weight * values[weightedIndex].second; + hfmModel.blendshapeChannelNames[weightedIndex] = keys[weightedIndex]; + } + HFMBlendshape& blendshape = mesh.blendshapes[indexFromMapping]; + blendshape.indices = part.triangleIndices; + auto target = primitive.targets[targetIndex]; + + QVector normals; + QVector vertices; + + if (weights.size() == primitive.targets.size()) { + int targetWeight = weights[targetIndex]; + if (targetWeight != 0) { + weight = weight * targetWeight; + } + } + + if (target.values.contains((QString) "NORMAL")) { + generateTargetData(target.values.value((QString) "NORMAL"), weight, normals); + } + if (target.values.contains((QString) "POSITION")) { + generateTargetData(target.values.value((QString) "POSITION"), weight, vertices); + } + bool isNewBlendshape = blendshape.vertices.size() < vertices.size(); + int count = 0; + for (int i : blendshape.indices) { + if (isNewBlendshape) { + blendshape.vertices.push_back(vertices[i]); + blendshape.normals.push_back(normals[i]); + } else { + blendshape.vertices[count] = blendshape.vertices[count] + vertices[i]; + blendshape.normals[count] = blendshape.normals[count] + normals[i]; + ++count; + } + } + } + } + + foreach(const glm::vec3& vertex, mesh.vertices) { + glm::vec3 transformedVertex = glm::vec3(globalTransforms[nodeIndex] * glm::vec4(vertex, 1.0f)); + mesh.meshExtents.addPoint(transformedVertex); + hfmModel.meshExtents.addPoint(transformedVertex); + } + } + + // Add epsilon to mesh extents to compensate for planar meshes + mesh.meshExtents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON); + mesh.meshExtents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON); + hfmModel.meshExtents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON); + hfmModel.meshExtents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON); + + mesh.meshIndex = hfmModel.meshes.size(); } - } - - - // Create the instance shapes for each transform node - for (int nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) { - const auto& node = _file.nodes[nodeIndex]; - if (-1 == node.mesh) { - continue; - } - - const auto& gltfMesh = _file.meshes[node.mesh]; - const auto& templateShapePerPrim = templateShapePerPrimPerGLTFMesh[node.mesh]; - int primCount = (int)gltfMesh.primitives.size(); - for (int primIndex = 0; primIndex < primCount; ++primIndex) { - const auto& templateShape = templateShapePerPrim[primIndex]; - hfmModel.shapes.push_back(templateShape); - auto& hfmShape = hfmModel.shapes.back(); - // Everything else is already defined (mesh, meshPart, material), so just define the new transform and deformer if present - hfmShape.joint = nodeIndex; - hfmShape.skinDeformer = node.skin != -1 ? node.skin : hfm::UNDEFINED_KEY; - } - } - - // TODO: Fix skinning and remove this workaround which disables skinning - // TODO: Restore after testing - { - std::vector meshToRootJoint; - meshToRootJoint.resize(hfmModel.meshes.size(), -1); - std::vector meshToClusterSize; - meshToClusterSize.resize(hfmModel.meshes.size()); - for (auto& shape : hfmModel.shapes) { - shape.skinDeformer = hfm::UNDEFINED_KEY; - } - - for (auto& mesh : hfmModel.meshes) { - mesh.clusterWeights.clear(); - mesh.clusterIndices.clear(); - mesh.clusterWeightsPerVertex = 0; - } - + ++nodecount; } return true; @@ -1578,8 +1636,9 @@ std::unique_ptr GLTFSerializer::getFactory() const { } HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi::VariantHash& mapping, const hifi::URL& url) { - _url = url; + _url = url; + // Normalize url for local files hifi::URL normalizeUrl = DependencyManager::get()->normalizeURL(_url); if (normalizeUrl.scheme().isEmpty() || (normalizeUrl.scheme() == "file")) { @@ -1589,9 +1648,6 @@ HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi:: if (parseGLTF(data)) { //_file.dump(); - _file.sortNodes(); - _file.populateMaterialNames(); - _file.normalizeNodeTransforms(); auto hfmModelPtr = std::make_shared(); HFMModel& hfmModel = *hfmModelPtr; buildGeometry(hfmModel, mapping, _url); @@ -1615,7 +1671,7 @@ bool GLTFSerializer::readBinary(const QString& url, hifi::ByteArray& outdata) { hifi::URL binaryUrl = _url.resolved(url); std::tie(success, outdata) = requestData(binaryUrl); } - + return success; } @@ -1628,8 +1684,8 @@ bool GLTFSerializer::doesResourceExist(const QString& url) { } std::tuple GLTFSerializer::requestData(hifi::URL& url) { - auto request = - DependencyManager::get()->createResourceRequest(nullptr, url, true, -1, "GLTFSerializer::requestData"); + auto request = DependencyManager::get()->createResourceRequest( + nullptr, url, true, -1, "GLTFSerializer::requestData"); if (!request) { return std::make_tuple(false, hifi::ByteArray()); @@ -1648,16 +1704,19 @@ std::tuple GLTFSerializer::requestData(hifi::URL& url) { } hifi::ByteArray GLTFSerializer::requestEmbeddedData(const QString& url) { - QString binaryUrl = url.split(",")[1]; + QString binaryUrl = url.split(",")[1]; return binaryUrl.isEmpty() ? hifi::ByteArray() : QByteArray::fromBase64(binaryUrl.toUtf8()); } + QNetworkReply* GLTFSerializer::request(hifi::URL& url, bool isTest) { if (!qApp) { return nullptr; } bool aboutToQuit{ false }; - auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] { aboutToQuit = true; }); + auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] { + aboutToQuit = true; + }); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest netRequest(url); netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); @@ -1666,18 +1725,18 @@ QNetworkReply* GLTFSerializer::request(hifi::URL& url, bool isTest) { netReply->deleteLater(); return nullptr; } - QEventLoop loop; // Create an event loop that will quit when we get the finished signal + QEventLoop loop; // Create an event loop that will quit when we get the finished signal QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); // Nothing is going to happen on this whole run thread until we get this + loop.exec(); // Nothing is going to happen on this whole run thread until we get this QObject::disconnect(connection); - return netReply; // trying to sync later on. + return netReply; // trying to sync later on. } HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { HFMTexture fbxtex = HFMTexture(); fbxtex.texcoordSet = 0; - + if (texture.defined["source"]) { QString url = _file.images[texture.source].uri; @@ -1686,9 +1745,9 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { fbxtex.name = fname; fbxtex.filename = textureUrl.toEncoded(); - if (_url.toString().endsWith("glb") && !_glbBinary.isEmpty()) { + if (_url.path().endsWith("glb") && !_glbBinary.isEmpty()) { int bufferView = _file.images[texture.source].bufferView; - + GLTFBufferView& imagesBufferview = _file.bufferviews[bufferView]; int offset = imagesBufferview.byteOffset; int length = imagesBufferview.byteLength; @@ -1698,7 +1757,7 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { } if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,")) { - fbxtex.content = requestEmbeddedData(url); + fbxtex.content = requestEmbeddedData(url); } } return fbxtex; @@ -1728,12 +1787,12 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& mat hfmMat.emissiveTexture = getHFMTexture(_file.textures[material.emissiveTexture]); hfmMat.useEmissiveMap = true; } - + if (material.defined["normalTexture"]) { hfmMat.normalTexture = getHFMTexture(_file.textures[material.normalTexture]); hfmMat.useNormalMap = true; } - + if (material.defined["occlusionTexture"]) { hfmMat.occlusionTexture = getHFMTexture(_file.textures[material.occlusionTexture]); hfmMat.useOcclusionMap = true; @@ -1761,7 +1820,7 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& mat if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { hfmMat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor); } - if (material.pbrMetallicRoughness.defined["baseColorFactor"] && + if (material.pbrMetallicRoughness.defined["baseColorFactor"] && material.pbrMetallicRoughness.baseColorFactor.size() == 4) { glm::vec3 dcolor = glm::vec3(material.pbrMetallicRoughness.baseColorFactor[0], material.pbrMetallicRoughness.baseColorFactor[1], @@ -1771,10 +1830,13 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& mat hfmMat._material->setOpacity(material.pbrMetallicRoughness.baseColorFactor[3]); } } + } -template -bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType) { +template +bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int count, + QVector& outarray, int accessorType) { + QDataStream blobstream(bin); blobstream.setByteOrder(QDataStream::LittleEndian); blobstream.setVersion(QDataStream::Qt_5_9); @@ -1783,31 +1845,31 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c int bufferCount = 0; switch (accessorType) { - case GLTFAccessorType::SCALAR: - bufferCount = 1; - break; - case GLTFAccessorType::VEC2: - bufferCount = 2; - break; - case GLTFAccessorType::VEC3: - bufferCount = 3; - break; - case GLTFAccessorType::VEC4: - bufferCount = 4; - break; - case GLTFAccessorType::MAT2: - bufferCount = 4; - break; - case GLTFAccessorType::MAT3: - bufferCount = 9; - break; - case GLTFAccessorType::MAT4: - bufferCount = 16; - break; - default: - qWarning(modelformat) << "Unknown accessorType: " << accessorType; - blobstream.setDevice(nullptr); - return false; + case GLTFAccessorType::SCALAR: + bufferCount = 1; + break; + case GLTFAccessorType::VEC2: + bufferCount = 2; + break; + case GLTFAccessorType::VEC3: + bufferCount = 3; + break; + case GLTFAccessorType::VEC4: + bufferCount = 4; + break; + case GLTFAccessorType::MAT2: + bufferCount = 4; + break; + case GLTFAccessorType::MAT3: + bufferCount = 9; + break; + case GLTFAccessorType::MAT4: + bufferCount = 16; + break; + default: + qWarning(modelformat) << "Unknown accessorType: " << accessorType; + blobstream.setDevice(nullptr); + return false; } for (int i = 0; i < count; ++i) { for (int j = 0; j < bufferCount; ++j) { @@ -1825,142 +1887,31 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c blobstream.setDevice(nullptr); return true; } -template -bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, - int byteOffset, - int count, - QVector& outarray, - int accessorType, - int componentType) { +template +bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, + QVector& outarray, int accessorType, int componentType) { + switch (componentType) { - case GLTFAccessorComponentType::BYTE: {} - case GLTFAccessorComponentType::UNSIGNED_BYTE: { - return readArray(bin, byteOffset, count, outarray, accessorType); - } - case GLTFAccessorComponentType::SHORT: { - return readArray(bin, byteOffset, count, outarray, accessorType); - } - case GLTFAccessorComponentType::UNSIGNED_INT: { - return readArray(bin, byteOffset, count, outarray, accessorType); - } - case GLTFAccessorComponentType::UNSIGNED_SHORT: { - return readArray(bin, byteOffset, count, outarray, accessorType); - } - case GLTFAccessorComponentType::FLOAT: { - return readArray(bin, byteOffset, count, outarray, accessorType); - } + case GLTFAccessorComponentType::BYTE: {} + case GLTFAccessorComponentType::UNSIGNED_BYTE: { + return readArray(bin, byteOffset, count, outarray, accessorType); + } + case GLTFAccessorComponentType::SHORT: { + return readArray(bin, byteOffset, count, outarray, accessorType); + } + case GLTFAccessorComponentType::UNSIGNED_INT: { + return readArray(bin, byteOffset, count, outarray, accessorType); + } + case GLTFAccessorComponentType::UNSIGNED_SHORT: { + return readArray(bin, byteOffset, count, outarray, accessorType); + } + case GLTFAccessorComponentType::FLOAT: { + return readArray(bin, byteOffset, count, outarray, accessorType); + } } return false; } - -template -bool GLTFSerializer::addArrayFromAttribute(GLTFVertexAttribute::Value vertexAttribute, GLTFAccessor& accessor, QVector& outarray) { - switch (vertexAttribute) { - case GLTFVertexAttribute::POSITION: - if (accessor.type != GLTFAccessorType::VEC3) { - qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::NORMAL: - if (accessor.type != GLTFAccessorType::VEC3) { - qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::TANGENT: - if (accessor.type != GLTFAccessorType::VEC4 && accessor.type != GLTFAccessorType::VEC3) { - qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::TEXCOORD_0: - if (accessor.type != GLTFAccessorType::VEC2) { - qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::TEXCOORD_1: - if (accessor.type != GLTFAccessorType::VEC2) { - qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url; - return false; - } - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::COLOR_0: - if (accessor.type != GLTFAccessorType::VEC4 && accessor.type != GLTFAccessorType::VEC3) { - qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::JOINTS_0: - if (accessor.type < GLTFAccessorType::SCALAR || accessor.type > GLTFAccessorType::VEC4) { - qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url; - return false; - } - break; - - case GLTFVertexAttribute::WEIGHTS_0: - if (accessor.type < GLTFAccessorType::SCALAR || accessor.type > GLTFAccessorType::VEC4) { - qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url; - return false; - } - - if (!addArrayFromAccessor(accessor, outarray)) { - qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url; - return false; - } - break; - - default: - qWarning(modelformat) << "Unexpected attribute type" << _url; - return false; - } - - - return true; -} - template bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray) { bool success = true; @@ -2006,7 +1957,7 @@ bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector& ou if (success) { for (int i = 0; i < accessor.sparse.count; ++i) { - if ((i * 3) + 2 < out_sparse_values_array.size()) { + if ((i * 3) + 2 < out_sparse_values_array.size()) { if ((out_sparse_indices_array[i] * 3) + 2 < outarray.length()) { for (int j = 0; j < 3; ++j) { outarray[(out_sparse_indices_array[i] * 3) + j] = out_sparse_values_array[(i * 3) + j]; @@ -2028,16 +1979,14 @@ bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector& ou return success; } -void GLTFSerializer::retriangulate(const QVector& inIndices, - const QVector& in_vertices, - const QVector& in_normals, - QVector& outIndices, - QVector& out_vertices, - QVector& out_normals) { +void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector& in_vertices, + const QVector& in_normals, QVector& outIndices, + QVector& out_vertices, QVector& out_normals) { for (int i = 0; i < inIndices.size(); i = i + 3) { + int idx1 = inIndices[i]; - int idx2 = inIndices[i + 1]; - int idx3 = inIndices[i + 2]; + int idx2 = inIndices[i+1]; + int idx3 = inIndices[i+2]; out_vertices.push_back(in_vertices[idx1]); out_vertices.push_back(in_vertices[idx2]); @@ -2048,8 +1997,8 @@ void GLTFSerializer::retriangulate(const QVector& inIndices, out_normals.push_back(in_normals[idx3]); outIndices.push_back(i); - outIndices.push_back(i + 1); - outIndices.push_back(i + 2); + outIndices.push_back(i+1); + outIndices.push_back(i+2); } } @@ -2058,7 +2007,7 @@ void GLTFSerializer::glTFDebugDump() { for (GLTFNode node : _file.nodes) { if (node.defined["mesh"]) { qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " node_transform" << node.transform; + qCDebug(modelformat) << " node_transforms" << node.transforms; qCDebug(modelformat) << "\n"; } } @@ -2086,7 +2035,144 @@ void GLTFSerializer::glTFDebugDump() { } void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) { - hfmModel.debugDump(); + qCDebug(modelformat) << "---------------- hfmModel ----------------"; + qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints; + qCDebug(modelformat) << " offset =" << hfmModel.offset; + + qCDebug(modelformat) << " neckPivot = " << hfmModel.neckPivot; + + qCDebug(modelformat) << " bindExtents.size() = " << hfmModel.bindExtents.size(); + qCDebug(modelformat) << " meshExtents.size() = " << hfmModel.meshExtents.size(); + + qCDebug(modelformat) << " jointIndices.size() =" << hfmModel.jointIndices.size(); + qCDebug(modelformat) << " joints.count() =" << hfmModel.joints.count(); + qCDebug(modelformat) << "---------------- Meshes ----------------"; + qCDebug(modelformat) << " meshes.count() =" << hfmModel.meshes.count(); + qCDebug(modelformat) << " blendshapeChannelNames = " << hfmModel.blendshapeChannelNames; + foreach(HFMMesh mesh, hfmModel.meshes) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " meshpointer =" << mesh._mesh.get(); + qCDebug(modelformat) << " meshindex =" << mesh.meshIndex; + qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.size(); + qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); + qCDebug(modelformat) << " normals.count() =" << mesh.normals.size(); + qCDebug(modelformat) << " tangents.count() =" << mesh.tangents.size(); + qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); + qCDebug(modelformat) << " texCoords.count() =" << mesh.texCoords.count(); + qCDebug(modelformat) << " texCoords1.count() =" << mesh.texCoords1.count(); + qCDebug(modelformat) << " clusterIndices.count() =" << mesh.clusterIndices.count(); + qCDebug(modelformat) << " clusterWeights.count() =" << mesh.clusterWeights.count(); + qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; + qCDebug(modelformat) << " parts.count() =" << mesh.parts.count(); + qCDebug(modelformat) << "---------------- Meshes (blendshapes)--------"; + foreach(HFMBlendshape bshape, mesh.blendshapes) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " bshape.indices.count() =" << bshape.indices.count(); + qCDebug(modelformat) << " bshape.vertices.count() =" << bshape.vertices.count(); + qCDebug(modelformat) << " bshape.normals.count() =" << bshape.normals.count(); + qCDebug(modelformat) << "\n"; + } + qCDebug(modelformat) << "---------------- Meshes (meshparts)--------"; + foreach(HFMMeshPart meshPart, mesh.parts) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); + qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); + qCDebug(modelformat) << " materialID =" << meshPart.materialID; + qCDebug(modelformat) << "\n"; + + } + qCDebug(modelformat) << "---------------- Meshes (clusters)--------"; + qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count(); + foreach(HFMCluster cluster, mesh.clusters) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " jointIndex =" << cluster.jointIndex; + qCDebug(modelformat) << " inverseBindMatrix =" << cluster.inverseBindMatrix; + qCDebug(modelformat) << "\n"; + } + qCDebug(modelformat) << "\n"; + } + qCDebug(modelformat) << "---------------- AnimationFrames ----------------"; + foreach(HFMAnimationFrame anim, hfmModel.animationFrames) { + qCDebug(modelformat) << " anim.translations = " << anim.translations; + qCDebug(modelformat) << " anim.rotations = " << anim.rotations; + } + QList mitomona_keys = hfmModel.meshIndicesToModelNames.keys(); + foreach(int key, mitomona_keys) { + qCDebug(modelformat) << " meshIndicesToModelNames key =" << key << " val =" << hfmModel.meshIndicesToModelNames[key]; + } + + qCDebug(modelformat) << "---------------- Materials ----------------"; + + foreach(HFMMaterial mat, hfmModel.materials) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " mat.materialID =" << mat.materialID; + qCDebug(modelformat) << " diffuseColor =" << mat.diffuseColor; + qCDebug(modelformat) << " diffuseFactor =" << mat.diffuseFactor; + qCDebug(modelformat) << " specularColor =" << mat.specularColor; + qCDebug(modelformat) << " specularFactor =" << mat.specularFactor; + qCDebug(modelformat) << " emissiveColor =" << mat.emissiveColor; + qCDebug(modelformat) << " emissiveFactor =" << mat.emissiveFactor; + qCDebug(modelformat) << " shininess =" << mat.shininess; + qCDebug(modelformat) << " opacity =" << mat.opacity; + qCDebug(modelformat) << " metallic =" << mat.metallic; + qCDebug(modelformat) << " roughness =" << mat.roughness; + qCDebug(modelformat) << " emissiveIntensity =" << mat.emissiveIntensity; + qCDebug(modelformat) << " ambientFactor =" << mat.ambientFactor; + + qCDebug(modelformat) << " materialID =" << mat.materialID; + qCDebug(modelformat) << " name =" << mat.name; + qCDebug(modelformat) << " shadingModel =" << mat.shadingModel; + qCDebug(modelformat) << " _material =" << mat._material.get(); + + qCDebug(modelformat) << " normalTexture =" << mat.normalTexture.filename; + qCDebug(modelformat) << " albedoTexture =" << mat.albedoTexture.filename; + qCDebug(modelformat) << " opacityTexture =" << mat.opacityTexture.filename; + + qCDebug(modelformat) << " lightmapParams =" << mat.lightmapParams; + + qCDebug(modelformat) << " isPBSMaterial =" << mat.isPBSMaterial; + qCDebug(modelformat) << " useNormalMap =" << mat.useNormalMap; + qCDebug(modelformat) << " useAlbedoMap =" << mat.useAlbedoMap; + qCDebug(modelformat) << " useOpacityMap =" << mat.useOpacityMap; + qCDebug(modelformat) << " useRoughnessMap =" << mat.useRoughnessMap; + qCDebug(modelformat) << " useSpecularMap =" << mat.useSpecularMap; + qCDebug(modelformat) << " useMetallicMap =" << mat.useMetallicMap; + qCDebug(modelformat) << " useEmissiveMap =" << mat.useEmissiveMap; + qCDebug(modelformat) << " useOcclusionMap =" << mat.useOcclusionMap; + qCDebug(modelformat) << "\n"; + } + + qCDebug(modelformat) << "---------------- Joints ----------------"; + + foreach (HFMJoint joint, hfmModel.joints) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " shapeInfo.avgPoint =" << joint.shapeInfo.avgPoint; + qCDebug(modelformat) << " shapeInfo.debugLines =" << joint.shapeInfo.debugLines; + qCDebug(modelformat) << " shapeInfo.dots =" << joint.shapeInfo.dots; + qCDebug(modelformat) << " shapeInfo.points =" << joint.shapeInfo.points; + + qCDebug(modelformat) << " parentIndex" << joint.parentIndex; + qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent; + qCDebug(modelformat) << " translation" << joint.translation; + qCDebug(modelformat) << " preTransform" << joint.preTransform; + qCDebug(modelformat) << " preRotation" << joint.preRotation; + qCDebug(modelformat) << " rotation" << joint.rotation; + qCDebug(modelformat) << " postRotation" << joint.postRotation; + qCDebug(modelformat) << " postTransform" << joint.postTransform; + qCDebug(modelformat) << " transform" << joint.transform; + qCDebug(modelformat) << " rotationMin" << joint.rotationMin; + qCDebug(modelformat) << " rotationMax" << joint.rotationMax; + qCDebug(modelformat) << " inverseDefaultRotation" << joint.inverseDefaultRotation; + qCDebug(modelformat) << " inverseBindRotation" << joint.inverseBindRotation; + qCDebug(modelformat) << " bindTransform" << joint.bindTransform; + qCDebug(modelformat) << " name" << joint.name; + qCDebug(modelformat) << " isSkeletonJoint" << joint.isSkeletonJoint; + qCDebug(modelformat) << " bindTransformFoundInCluster" << joint.hasGeometricOffset; + qCDebug(modelformat) << " bindTransformFoundInCluster" << joint.geometricTranslation; + qCDebug(modelformat) << " bindTransformFoundInCluster" << joint.geometricRotation; + qCDebug(modelformat) << " bindTransformFoundInCluster" << joint.geometricScaling; + qCDebug(modelformat) << "\n"; + } qCDebug(modelformat) << "---------------- GLTF Model ----------------"; glTFDebugDump(); diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index d59df615e5..b1020f7154 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -38,15 +38,15 @@ struct GLTFAsset { struct GLTFNode { QString name; - int camera{ -1 }; - int mesh{ -1 }; + int camera; + int mesh; QVector children; QVector translation; QVector rotation; QVector scale; QVector matrix; - glm::mat4 transform; - int skin { -1 }; + QVector transforms; + int skin; QVector skeletons; QString jointName; QMap defined; @@ -85,8 +85,6 @@ struct GLTFNode { qCDebug(modelformat) << "skeletons: " << skeletons; } } - - void normalizeTransform(); }; // Meshes @@ -460,56 +458,15 @@ struct GLTFMaterial { // Accesors namespace GLTFAccessorType { - enum Value { - SCALAR = 1, - VEC2 = 2, - VEC3 = 3, - VEC4 = 4, - MAT2 = 5, - MAT3 = 9, - MAT4 = 16 + enum Values { + SCALAR = 0, + VEC2, + VEC3, + VEC4, + MAT2, + MAT3, + MAT4 }; - - inline int count(Value value) { - if (value == MAT2) { - return 4; - } - return (int)value; - } -} - -namespace GLTFVertexAttribute { - enum Value { - UNKNOWN = -1, - POSITION = 0, - NORMAL, - TANGENT, - TEXCOORD_0, - TEXCOORD_1, - COLOR_0, - JOINTS_0, - WEIGHTS_0, - }; - inline Value fromString(const QString& key) { - if (key == "POSITION") { - return POSITION; - } else if (key == "NORMAL") { - return NORMAL; - } else if (key == "TANGENT") { - return TANGENT; - } else if (key == "TEXCOORD_0") { - return TEXCOORD_0; - } else if (key == "TEXCOORD_1") { - return TEXCOORD_1; - } else if (key == "COLOR_0") { - return COLOR_0; - } else if (key == "JOINTS_0") { - return JOINTS_0; - } else if (key == "WEIGHTS_0") { - return WEIGHTS_0; - } - return UNKNOWN; - } } namespace GLTFAccessorComponentType { enum Values { @@ -801,13 +758,6 @@ struct GLTFFile { foreach(auto tex, textures) tex.dump(); } } - - - void populateMaterialNames(); - void sortNodes(); - void normalizeNodeTransforms(); -private: - void reorderNodes(const std::unordered_map& reorderMap); }; class GLTFSerializer : public QObject, public HFMSerializer { @@ -822,7 +772,7 @@ private: hifi::URL _url; hifi::ByteArray _glbBinary; - const glm::mat4& getModelTransform(const GLTFNode& node); + glm::mat4 getModelTransform(const GLTFNode& node); void getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); void generateTargetData(int index, float weight, QVector& returnVector); @@ -891,9 +841,6 @@ private: template bool addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray); - template - bool addArrayFromAttribute(GLTFVertexAttribute::Value vertexAttribute, GLTFAccessor& accessor, QVector& outarray); - void retriangulate(const QVector& in_indices, const QVector& in_vertices, const QVector& in_normals, QVector& out_indices, QVector& out_vertices, QVector& out_normals); diff --git a/libraries/fbx/src/OBJSerializer.cpp b/libraries/fbx/src/OBJSerializer.cpp index 4da7351b42..416f343a47 100644 --- a/libraries/fbx/src/OBJSerializer.cpp +++ b/libraries/fbx/src/OBJSerializer.cpp @@ -174,6 +174,11 @@ glm::vec2 OBJTokenizer::getVec2() { return v; } + +void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID) { + meshPart.materialID = materialID; +} + // OBJFace // NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just // pairing it with the vertices vector for consistency. @@ -487,7 +492,8 @@ bool OBJSerializer::parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantHa float& scaleGuess, bool combineParts) { FaceGroup faces; HFMMesh& mesh = hfmModel.meshes[0]; - mesh.parts.push_back(HFMMeshPart()); + mesh.parts.append(HFMMeshPart()); + HFMMeshPart& meshPart = mesh.parts.last(); bool sawG = false; bool result = true; int originalFaceCountForDebugging = 0; @@ -495,6 +501,8 @@ bool OBJSerializer::parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantHa bool anyVertexColor { false }; int vertexCount { 0 }; + setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.count())); + while (true) { int tokenType = tokenizer.nextToken(); if (tokenType == OBJTokenizer::COMMENT_TOKEN) { @@ -667,19 +675,17 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V _url = url; bool combineParts = mapping.value("combineParts").toBool(); - hfmModel.meshes.push_back(HFMMesh()); + hfmModel.meshExtents.reset(); + hfmModel.meshes.append(HFMMesh()); - std::vector materialNamePerShape; try { // call parseOBJGroup as long as it's returning true. Each successful call will // add a new meshPart to the model's single mesh. while (parseOBJGroup(tokenizer, mapping, hfmModel, scaleGuess, combineParts)) {} - uint32_t meshIndex = 0; - HFMMesh& mesh = hfmModel.meshes[meshIndex]; - mesh.meshIndex = meshIndex; + HFMMesh& mesh = hfmModel.meshes[0]; + mesh.meshIndex = 0; - uint32_t jointIndex = 0; hfmModel.joints.resize(1); hfmModel.joints[0].parentIndex = -1; hfmModel.joints[0].distanceToParent = 0; @@ -691,11 +697,19 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V hfmModel.jointIndices["x"] = 1; + HFMCluster cluster; + cluster.jointIndex = 0; + cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + mesh.clusters.append(cluster); + QMap materialMeshIdMap; - std::vector hfmMeshParts; - for (uint32_t meshPartIndex = 0; meshPartIndex < (uint32_t)mesh.parts.size(); ++meshPartIndex) { - HFMMeshPart& meshPart = mesh.parts[meshPartIndex]; - FaceGroup faceGroup = faceGroups[meshPartIndex]; + QVector hfmMeshParts; + for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { + HFMMeshPart& meshPart = mesh.parts[i]; + FaceGroup faceGroup = faceGroups[meshPartCount]; bool specifiesUV = false; foreach(OBJFace face, faceGroup) { // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). @@ -704,13 +718,12 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V // Create a new HFMMesh for this material mapping. materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); - uint32_t partIndex = (int)hfmMeshParts.size(); - hfmMeshParts.push_back(HFMMeshPart()); - HFMMeshPart& meshPartNew = hfmMeshParts.back(); + hfmMeshParts.append(HFMMeshPart()); + HFMMeshPart& meshPartNew = hfmMeshParts.last(); meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. - + // Do some of the material logic (which previously lived below) now. // All the faces in the same group will have the same name and material. QString groupMaterialName = face.materialName; @@ -732,26 +745,19 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V needsMaterialLibrary = groupMaterialName != SMART_DEFAULT_MATERIAL_NAME; } materials[groupMaterialName] = material; + meshPartNew.materialID = groupMaterialName; } - materialNamePerShape.push_back(groupMaterialName); - - - hfm::Shape shape; - shape.mesh = meshIndex; - shape.joint = jointIndex; - shape.meshPart = partIndex; - hfmModel.shapes.push_back(shape); } } } // clean up old mesh parts. - auto unmodifiedMeshPartCount = (uint32_t)mesh.parts.size(); + int unmodifiedMeshPartCount = mesh.parts.count(); mesh.parts.clear(); - mesh.parts = hfmMeshParts; + mesh.parts = QVector(hfmMeshParts); - for (uint32_t meshPartIndex = 0; meshPartIndex < unmodifiedMeshPartCount; meshPartIndex++) { - FaceGroup faceGroup = faceGroups[meshPartIndex]; + for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { + FaceGroup faceGroup = faceGroups[meshPartCount]; // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). foreach(OBJFace face, faceGroup) { @@ -817,13 +823,18 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V } } } + + mesh.meshExtents.reset(); + foreach(const glm::vec3& vertex, mesh.vertices) { + mesh.meshExtents.addPoint(vertex); + hfmModel.meshExtents.addPoint(vertex); + } + + // hfmDebugDump(hfmModel); } catch(const std::exception& e) { qCDebug(modelformat) << "OBJSerializer fail: " << e.what(); } - // At this point, the hfmModel joint, mesh, parts and shpaes have been defined - // only no material assigned - QString queryPart = _url.query(); bool suppressMaterialsHack = queryPart.contains("hifiusemat"); // If this appears in query string, don't fetch mtl even if used. OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME]; @@ -875,23 +886,17 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V } } - // As we are populating the material list in the hfmModel, let s also create the reverse map (from materialName to index) - QMap materialNameToIndex; foreach (QString materialID, materials.keys()) { OBJMaterial& objMaterial = materials[materialID]; if (!objMaterial.used) { continue; } - // capture the name to index map - materialNameToIndex[materialID] = (uint32_t) hfmModel.materials.size(); - - hfmModel.materials.emplace_back(objMaterial.diffuseColor, - objMaterial.specularColor, - objMaterial.emissiveColor, - objMaterial.shininess, - objMaterial.opacity); - HFMMaterial& hfmMaterial = hfmModel.materials.back(); + HFMMaterial& hfmMaterial = hfmModel.materials[materialID] = HFMMaterial(objMaterial.diffuseColor, + objMaterial.specularColor, + objMaterial.emissiveColor, + objMaterial.shininess, + objMaterial.opacity); hfmMaterial.name = materialID; hfmMaterial.materialID = materialID; @@ -991,16 +996,77 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V modelMaterial->setOpacity(hfmMaterial.opacity); } - // GO over the shapes once more to assign the material index correctly - for (uint32_t i = 0; i < (uint32_t)hfmModel.shapes.size(); ++i) { - const auto& materialName = materialNamePerShape[i]; - if (!materialName.isEmpty()) { - auto foundMaterialIndex = materialNameToIndex.find(materialName); - if (foundMaterialIndex != materialNameToIndex.end()) { - hfmModel.shapes[i].material = foundMaterialIndex.value(); + return hfmModelPtr; +} + +void hfmDebugDump(const HFMModel& hfmModel) { + qCDebug(modelformat) << "---------------- hfmModel ----------------"; + qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints; + qCDebug(modelformat) << " offset =" << hfmModel.offset; + qCDebug(modelformat) << " meshes.count() =" << hfmModel.meshes.count(); + foreach (HFMMesh mesh, hfmModel.meshes) { + qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); + qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); + qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); + /*if (mesh.normals.count() == mesh.vertices.count()) { + for (int i = 0; i < mesh.normals.count(); i++) { + qCDebug(modelformat) << " " << mesh.vertices[ i ] << mesh.normals[ i ]; } + }*/ + qCDebug(modelformat) << " tangents.count() =" << mesh.tangents.count(); + qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); + qCDebug(modelformat) << " texCoords.count() =" << mesh.texCoords.count(); + qCDebug(modelformat) << " texCoords1.count() =" << mesh.texCoords1.count(); + qCDebug(modelformat) << " clusterIndices.count() =" << mesh.clusterIndices.count(); + qCDebug(modelformat) << " clusterWeights.count() =" << mesh.clusterWeights.count(); + qCDebug(modelformat) << " meshExtents =" << mesh.meshExtents; + qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; + qCDebug(modelformat) << " parts.count() =" << mesh.parts.count(); + foreach (HFMMeshPart meshPart, mesh.parts) { + qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); + qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); + /* + qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor << "mat =" << meshPart._material->getDiffuse(); + qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getMetallic(); + qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor << "mat =" << meshPart._material->getEmissive(); + qCDebug(modelformat) << " emissiveParams =" << meshPart.emissiveParams; + qCDebug(modelformat) << " gloss =" << meshPart.shininess << "mat =" << meshPart._material->getRoughness(); + qCDebug(modelformat) << " opacity =" << meshPart.opacity << "mat =" << meshPart._material->getOpacity(); + */ + qCDebug(modelformat) << " materialID =" << meshPart.materialID; + /* qCDebug(modelformat) << " diffuse texture =" << meshPart.diffuseTexture.filename; + qCDebug(modelformat) << " specular texture =" << meshPart.specularTexture.filename; + */ + } + qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count(); + foreach (HFMCluster cluster, mesh.clusters) { + qCDebug(modelformat) << " jointIndex =" << cluster.jointIndex; + qCDebug(modelformat) << " inverseBindMatrix =" << cluster.inverseBindMatrix; } } - return hfmModelPtr; + qCDebug(modelformat) << " jointIndices =" << hfmModel.jointIndices; + qCDebug(modelformat) << " joints.count() =" << hfmModel.joints.count(); + + foreach (HFMJoint joint, hfmModel.joints) { + + qCDebug(modelformat) << " parentIndex" << joint.parentIndex; + qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent; + qCDebug(modelformat) << " translation" << joint.translation; + qCDebug(modelformat) << " preTransform" << joint.preTransform; + qCDebug(modelformat) << " preRotation" << joint.preRotation; + qCDebug(modelformat) << " rotation" << joint.rotation; + qCDebug(modelformat) << " postRotation" << joint.postRotation; + qCDebug(modelformat) << " postTransform" << joint.postTransform; + qCDebug(modelformat) << " transform" << joint.transform; + qCDebug(modelformat) << " rotationMin" << joint.rotationMin; + qCDebug(modelformat) << " rotationMax" << joint.rotationMax; + qCDebug(modelformat) << " inverseDefaultRotation" << joint.inverseDefaultRotation; + qCDebug(modelformat) << " inverseBindRotation" << joint.inverseBindRotation; + qCDebug(modelformat) << " bindTransform" << joint.bindTransform; + qCDebug(modelformat) << " name" << joint.name; + qCDebug(modelformat) << " isSkeletonJoint" << joint.isSkeletonJoint; + } + + qCDebug(modelformat) << "\n"; } diff --git a/libraries/fbx/src/OBJSerializer.h b/libraries/fbx/src/OBJSerializer.h index 462d32a119..6fdd95e2c3 100644 --- a/libraries/fbx/src/OBJSerializer.h +++ b/libraries/fbx/src/OBJSerializer.h @@ -120,5 +120,6 @@ private: // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID); +void hfmDebugDump(const HFMModel& hfmModel); #endif // hifi_OBJSerializer_h diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 87d19ec6a2..88caa3cea4 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -120,50 +120,6 @@ template<> glm::uint32 forEach(const gpu::BufferView& view, std::func return forEachGlmVec(view, func); } -template -QVariant glmVecToVariant(const T& v, bool asArray /*= false*/) { - static const auto len = T().length(); - if (asArray) { - QVariantList list; - for (int i = 0; i < len ; i++) { - list << v[i]; - } - return list; - } else { - QVariantMap obj; - for (int i = 0; i < len ; i++) { - obj[XYZW[i]] = v[i]; - } - return obj; - } -} - -template -const T glmVecFromVariant(const QVariant& v) { - auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; - static const auto len = T().length(); - const auto& components = isMap ? XYZW : ZERO123; - T result; - QVariantMap map; - QVariantList list; - if (isMap) map = v.toMap(); else list = v.toList(); - for (int i = 0; i < len ; i++) { - float value; - if (isMap) { - value = map.value(components[i]).toFloat(); - } else { - value = list.value(i).toFloat(); - } -#ifdef DEBUG_BUFFERVIEW_HELPERS - if (value != value) { // NAN - qWarning().nospace()<< "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); - } -#endif - result[i] = value; - } - return result; -} - // QVector => BufferView template gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index 3635ef64e5..c42bb0426d 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -27,8 +27,54 @@ namespace buffer_helpers { extern const std::array XYZW; extern const std::array ZERO123; - template QVariant glmVecToVariant(const T& v, bool asArray = false); - template const T glmVecFromVariant(const QVariant& v); + template + QVariant glmVecToVariant(const T& v, bool asArray = false) { + static const auto len = T().length(); + if (asArray) { + QVariantList list; + for (int i = 0; i < len; i++) { + list << v[i]; + } + return list; + } else { + QVariantMap obj; + for (int i = 0; i < len; i++) { + obj[XYZW[i]] = v[i]; + } + return obj; + } + } + + template + const T glmVecFromVariant(const QVariant& v) { + auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; + static const auto len = T().length(); + const auto& components = isMap ? XYZW : ZERO123; + T result; + QVariantMap map; + QVariantList list; + if (isMap) { + map = v.toMap(); + } else { + list = v.toList(); + } + for (int i = 0; i < len; i++) { + float value; + if (isMap) { + value = map.value(components[i]).toFloat(); + } else { + value = list.value(i).toFloat(); + } +#ifdef DEBUG_BUFFERVIEW_HELPERS + if (value != value) { // NAN + qWarning().nospace() << "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); + } +#endif + result[i] = value; + } + return result; + } + glm::uint32 forEachVariant(const gpu::BufferView& view, std::function func, const char* hint = ""); template glm::uint32 forEach(const gpu::BufferView& view, std::function func); @@ -36,7 +82,7 @@ namespace buffer_helpers { template gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType); template gpu::BufferView newFromVariantList(const QVariantList& list, const gpu::Element& elementType); - template QVector variantToVector(const QVariant& list); + template QVector variantToVector(const QVariant& value); template QVector bufferToVector(const gpu::BufferView& view, const char *hint = ""); // note: these do value conversions from the underlying buffer type into the template type diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index 328ff4a3af..274dbc1cdd 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -13,7 +13,6 @@ <@include graphics/ShaderConstants.h@> - const int MAX_TEXCOORDS = 2; struct TexMapArray { diff --git a/libraries/hfm/src/hfm/HFM.cpp b/libraries/hfm/src/hfm/HFM.cpp index 500aaaa842..236445bfda 100644 --- a/libraries/hfm/src/hfm/HFM.cpp +++ b/libraries/hfm/src/hfm/HFM.cpp @@ -76,7 +76,7 @@ QStringList HFMModel::getJointNames() const { } bool HFMModel::hasBlendedMeshes() const { - if (!meshes.empty()) { + if (!meshes.isEmpty()) { foreach (const HFMMesh& mesh, meshes) { if (!mesh.blendshapes.isEmpty()) { return true; @@ -166,16 +166,16 @@ void HFMModel::computeKdops() { glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) }; - if (joints.size() != shapeVertices.size()) { + if (joints.size() != (int)shapeVertices.size()) { return; } // now that all joints have been scanned compute a k-Dop bounding volume of mesh - for (size_t i = 0; i < joints.size(); ++i) { + for (int i = 0; i < joints.size(); ++i) { HFMJoint& joint = joints[i]; // NOTE: points are in joint-frame ShapeVertices& points = shapeVertices.at(i); - glm::quat rotOffset = jointRotationOffsets.contains((int)i) ? glm::inverse(jointRotationOffsets[(int)i]) : quat(); + glm::quat rotOffset = jointRotationOffsets.contains(i) ? glm::inverse(jointRotationOffsets[i]) : quat(); if (points.size() > 0) { // compute average point glm::vec3 avgPoint = glm::vec3(0.0f); @@ -208,164 +208,3 @@ void HFMModel::computeKdops() { } } } - -void hfm::Model::debugDump() const { - qCDebug(modelformat) << "---------------- hfmModel ----------------"; - qCDebug(modelformat) << " hasSkeletonJoints =" << hasSkeletonJoints; - qCDebug(modelformat) << " offset =" << offset; - - qCDebug(modelformat) << " neckPivot = " << neckPivot; - - qCDebug(modelformat) << " bindExtents.size() = " << bindExtents.size(); - qCDebug(modelformat) << " meshExtents.size() = " << meshExtents.size(); - - qCDebug(modelformat) << "---------------- Shapes ----------------"; - qCDebug(modelformat) << " shapes.size() =" << shapes.size(); - for (const hfm::Shape& shape : shapes) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " mesh =" << shape.mesh; - qCDebug(modelformat) << " meshPart =" << shape.meshPart; - qCDebug(modelformat) << " material =" << shape.material; - qCDebug(modelformat) << " joint =" << shape.joint; - qCDebug(modelformat) << " transformedExtents =" << shape.transformedExtents; - qCDebug(modelformat) << " skinDeformer =" << shape.skinDeformer; - } - - qCDebug(modelformat) << " jointIndices.size() =" << jointIndices.size(); - qCDebug(modelformat) << " joints.size() =" << joints.size(); - qCDebug(modelformat) << "---------------- Meshes ----------------"; - qCDebug(modelformat) << " meshes.size() =" << meshes.size(); - qCDebug(modelformat) << " blendshapeChannelNames = " << blendshapeChannelNames; - for (const HFMMesh& mesh : meshes) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " meshpointer =" << mesh._mesh.get(); - qCDebug(modelformat) << " meshindex =" << mesh.meshIndex; - qCDebug(modelformat) << " vertices.size() =" << mesh.vertices.size(); - qCDebug(modelformat) << " colors.size() =" << mesh.colors.size(); - qCDebug(modelformat) << " normals.size() =" << mesh.normals.size(); - qCDebug(modelformat) << " tangents.size() =" << mesh.tangents.size(); - qCDebug(modelformat) << " colors.size() =" << mesh.colors.size(); - qCDebug(modelformat) << " texCoords.size() =" << mesh.texCoords.size(); - qCDebug(modelformat) << " texCoords1.size() =" << mesh.texCoords1.size(); - qCDebug(modelformat) << " clusterIndices.size() =" << mesh.clusterIndices.size(); - qCDebug(modelformat) << " clusterWeights.size() =" << mesh.clusterWeights.size(); - qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; - qCDebug(modelformat) << " parts.size() =" << mesh.parts.size(); - qCDebug(modelformat) << "---------------- Meshes (blendshapes)--------"; - for (HFMBlendshape bshape : mesh.blendshapes) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " bshape.indices.size() =" << bshape.indices.size(); - qCDebug(modelformat) << " bshape.vertices.size() =" << bshape.vertices.size(); - qCDebug(modelformat) << " bshape.normals.size() =" << bshape.normals.size(); - qCDebug(modelformat) << "\n"; - } - qCDebug(modelformat) << "---------------- Meshes (meshparts)--------"; - for (HFMMeshPart meshPart : mesh.parts) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " quadIndices.size() =" << meshPart.quadIndices.size(); - qCDebug(modelformat) << " triangleIndices.size() =" << meshPart.triangleIndices.size(); - qCDebug(modelformat) << "\n"; - } - } - qCDebug(modelformat) << "---------------- AnimationFrames ----------------"; - for (HFMAnimationFrame anim : animationFrames) { - qCDebug(modelformat) << " anim.translations = " << anim.translations; - qCDebug(modelformat) << " anim.rotations = " << anim.rotations; - } - QList mitomona_keys = meshIndicesToModelNames.keys(); - for (int key : mitomona_keys) { - qCDebug(modelformat) << " meshIndicesToModelNames key =" << key - << " val =" << meshIndicesToModelNames[key]; - } - - qCDebug(modelformat) << "---------------- Materials ----------------"; - - for (HFMMaterial mat : materials) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " mat.materialID =" << mat.materialID; - qCDebug(modelformat) << " diffuseColor =" << mat.diffuseColor; - qCDebug(modelformat) << " diffuseFactor =" << mat.diffuseFactor; - qCDebug(modelformat) << " specularColor =" << mat.specularColor; - qCDebug(modelformat) << " specularFactor =" << mat.specularFactor; - qCDebug(modelformat) << " emissiveColor =" << mat.emissiveColor; - qCDebug(modelformat) << " emissiveFactor =" << mat.emissiveFactor; - qCDebug(modelformat) << " shininess =" << mat.shininess; - qCDebug(modelformat) << " opacity =" << mat.opacity; - qCDebug(modelformat) << " metallic =" << mat.metallic; - qCDebug(modelformat) << " roughness =" << mat.roughness; - qCDebug(modelformat) << " emissiveIntensity =" << mat.emissiveIntensity; - qCDebug(modelformat) << " ambientFactor =" << mat.ambientFactor; - - qCDebug(modelformat) << " materialID =" << mat.materialID; - qCDebug(modelformat) << " name =" << mat.name; - qCDebug(modelformat) << " shadingModel =" << mat.shadingModel; - qCDebug(modelformat) << " _material =" << mat._material.get(); - - qCDebug(modelformat) << " normalTexture =" << mat.normalTexture.filename; - qCDebug(modelformat) << " albedoTexture =" << mat.albedoTexture.filename; - qCDebug(modelformat) << " opacityTexture =" << mat.opacityTexture.filename; - - qCDebug(modelformat) << " lightmapParams =" << mat.lightmapParams; - - qCDebug(modelformat) << " isPBSMaterial =" << mat.isPBSMaterial; - qCDebug(modelformat) << " useNormalMap =" << mat.useNormalMap; - qCDebug(modelformat) << " useAlbedoMap =" << mat.useAlbedoMap; - qCDebug(modelformat) << " useOpacityMap =" << mat.useOpacityMap; - qCDebug(modelformat) << " useRoughnessMap =" << mat.useRoughnessMap; - qCDebug(modelformat) << " useSpecularMap =" << mat.useSpecularMap; - qCDebug(modelformat) << " useMetallicMap =" << mat.useMetallicMap; - qCDebug(modelformat) << " useEmissiveMap =" << mat.useEmissiveMap; - qCDebug(modelformat) << " useOcclusionMap =" << mat.useOcclusionMap; - qCDebug(modelformat) << "\n"; - } - - qCDebug(modelformat) << "---------------- Joints ----------------"; - - for (const HFMJoint& joint : joints) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " shapeInfo.avgPoint =" << joint.shapeInfo.avgPoint; - qCDebug(modelformat) << " shapeInfo.debugLines =" << joint.shapeInfo.debugLines; - qCDebug(modelformat) << " shapeInfo.dots =" << joint.shapeInfo.dots; - qCDebug(modelformat) << " shapeInfo.points =" << joint.shapeInfo.points; - - qCDebug(modelformat) << " ---"; - - qCDebug(modelformat) << " parentIndex" << joint.parentIndex; - qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent; - qCDebug(modelformat) << " localTransform" << joint.localTransform; - qCDebug(modelformat) << " transform" << joint.transform; - qCDebug(modelformat) << " globalTransform" << joint.globalTransform; - - qCDebug(modelformat) << " ---"; - - qCDebug(modelformat) << " translation" << joint.translation; - qCDebug(modelformat) << " preTransform" << joint.preTransform; - qCDebug(modelformat) << " preRotation" << joint.preRotation; - qCDebug(modelformat) << " rotation" << joint.rotation; - qCDebug(modelformat) << " postRotation" << joint.postRotation; - qCDebug(modelformat) << " postTransform" << joint.postTransform; - qCDebug(modelformat) << " rotationMin" << joint.rotationMin; - qCDebug(modelformat) << " rotationMax" << joint.rotationMax; - qCDebug(modelformat) << " inverseDefaultRotation" << joint.inverseDefaultRotation; - qCDebug(modelformat) << " inverseBindRotation" << joint.inverseBindRotation; - qCDebug(modelformat) << " bindTransformFoundInCluster" << joint.bindTransformFoundInCluster; - qCDebug(modelformat) << " bindTransform" << joint.bindTransform; - qCDebug(modelformat) << " name" << joint.name; - qCDebug(modelformat) << " isSkeletonJoint" << joint.isSkeletonJoint; - qCDebug(modelformat) << " geometricOffset" << joint.geometricOffset; - qCDebug(modelformat) << "\n"; - } - - qCDebug(modelformat) << "------------- SkinDeformers ------------"; - qCDebug(modelformat) << " skinDeformers.size() =" << skinDeformers.size(); - for(const hfm::SkinDeformer& skinDeformer : skinDeformers) { - qCDebug(modelformat) << "------- SkinDeformers (Clusters) -------"; - for (const hfm::Cluster& cluster : skinDeformer.clusters) { - qCDebug(modelformat) << "\n"; - qCDebug(modelformat) << " jointIndex =" << cluster.jointIndex; - qCDebug(modelformat) << " inverseBindMatrix =" << cluster.inverseBindMatrix; - qCDebug(modelformat) << "\n"; - } - } - qCDebug(modelformat) << "\n"; -} diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index ca73676f86..7111ad2e65 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -66,8 +66,6 @@ static const int DRACO_ATTRIBUTE_ORIGINAL_INDEX = DRACO_BEGIN_CUSTOM_HIFI_ATTRIB // High Fidelity Model namespace namespace hfm { -static const uint32_t UNDEFINED_KEY = (uint32_t)-1; - /// A single blendshape. class Blendshape { public: @@ -113,22 +111,19 @@ public: bool isSkeletonJoint; bool bindTransformFoundInCluster; - // geometric offset is applied in local space but does NOT affect children. - // TODO: Apply hfm::Joint.geometricOffset to transforms in the model preparation step - glm::mat4 geometricOffset; - - // globalTransform is the transform of the joint with all parent transforms applied, plus the geometric offset - glm::mat4 localTransform; - glm::mat4 globalTransform; + bool hasGeometricOffset; + glm::vec3 geometricTranslation; + glm::quat geometricRotation; + glm::vec3 geometricScaling; }; /// A single binding to a joint. class Cluster { public: - static const uint32_t INVALID_JOINT_INDEX { (uint32_t)-1 }; - uint32_t jointIndex { INVALID_JOINT_INDEX }; + + int jointIndex; glm::mat4 inverseBindMatrix; Transform inverseBindTransform; }; @@ -160,6 +155,8 @@ public: QVector quadIndices; // original indices from the FBX mesh QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles QVector triangleIndices; // original indices from the FBX mesh + + QString materialID; }; class Material { @@ -230,20 +227,11 @@ public: bool needTangentSpace() const; }; - -/// Simple Triangle List Mesh -struct TriangleListMesh { - std::vector vertices; - std::vector indices; - std::vector parts; // Offset in the indices, Number of indices - std::vector partExtents; // Extents of each part with no transform applied. Same length as parts. -}; - /// A single mesh (with optional blendshapes). class Mesh { public: - std::vector parts; + QVector parts; QVector vertices; QVector normals; @@ -251,27 +239,21 @@ public: QVector colors; QVector texCoords; QVector texCoords1; + QVector clusterIndices; + QVector clusterWeights; + QVector originalIndices; - Extents meshExtents; // DEPRECATED (see hfm::Shape::transformedExtents) - glm::mat4 modelTransform; // DEPRECATED (see hfm::Joint::globalTransform, hfm::Shape::transform, hfm::Model::joints) + QVector clusters; - // Skinning cluster attributes - std::vector clusterIndices; - std::vector clusterWeights; - uint16_t clusterWeightsPerVertex { 0 }; + Extents meshExtents; + glm::mat4 modelTransform; - // Blendshape attributes QVector blendshapes; - // Simple Triangle List Mesh generated during baking - hfm::TriangleListMesh triangleListMesh; - - QVector originalIndices; // Original indices of the vertices unsigned int meshIndex; // the order the meshes appeared in the object file graphics::MeshPointer _mesh; bool wasCompressed { false }; - }; /// A single animation frame. @@ -308,30 +290,6 @@ public: bool shouldInitCollisions() const { return _collisionsConfig.size() > 0; } }; -// A different skinning representation, used by FBXSerializer. We convert this to our graphics-optimized runtime representation contained within the mesh. -class SkinCluster { -public: - std::vector indices; - std::vector weights; -}; - -class SkinDeformer { -public: - std::vector clusters; -}; - -// The lightweight model part description. -class Shape { -public: - uint32_t mesh { UNDEFINED_KEY }; - uint32_t meshPart { UNDEFINED_KEY }; - uint32_t material { UNDEFINED_KEY }; - uint32_t joint { UNDEFINED_KEY }; // The hfm::Joint associated with this shape, containing transform information - // TODO: Have all serializers calculate hfm::Shape::transformedExtents in world space where they previously calculated hfm::Mesh::meshExtents. Change all code that uses hfm::Mesh::meshExtents to use this instead. - Extents transformedExtents; // The precise extents of the meshPart vertices in world space, after transform information is applied, while not taking into account rigging/skinning - uint32_t skinDeformer { UNDEFINED_KEY }; -}; - /// The runtime model format. class Model { public: @@ -342,18 +300,15 @@ public: QString author; QString applicationName; ///< the name of the application that generated the model - std::vector shapes; - - std::vector meshes; - std::vector materials; - - std::vector skinDeformers; - - std::vector joints; + QVector joints; QHash jointIndices; ///< 1-based, so as to more easily detect missing indices bool hasSkeletonJoints; + + QVector meshes; QVector scripts; + QHash materials; + glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file glm::vec3 neckPivot; @@ -385,12 +340,19 @@ public: QMap jointRotationOffsets; std::vector shapeVertices; FlowData flowData; - - void debugDump() const; }; }; +class ExtractedMesh { +public: + hfm::Mesh mesh; + QMultiHash newIndices; + QVector > blendshapeIndexMaps; + QVector > partMaterialTextures; + QHash texcoordSetMap; +}; + typedef hfm::Blendshape HFMBlendshape; typedef hfm::JointShapeInfo HFMJointShapeInfo; typedef hfm::Joint HFMJoint; @@ -399,10 +361,8 @@ typedef hfm::Texture HFMTexture; typedef hfm::MeshPart HFMMeshPart; typedef hfm::Material HFMMaterial; typedef hfm::Mesh HFMMesh; -typedef hfm::SkinDeformer HFMSkinDeformer; typedef hfm::AnimationFrame HFMAnimationFrame; typedef hfm::Light HFMLight; -typedef hfm::Shape HFMShape; typedef hfm::Model HFMModel; typedef hfm::FlowData FlowData; diff --git a/libraries/hfm/src/hfm/HFMModelMath.cpp b/libraries/hfm/src/hfm/HFMModelMath.cpp deleted file mode 100644 index 436e520643..0000000000 --- a/libraries/hfm/src/hfm/HFMModelMath.cpp +++ /dev/null @@ -1,212 +0,0 @@ -// -// HFMModelMath.cpp -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/10/04. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HFMModelMath.h" - -#include - -#include - -#include -#include - -namespace hfm { - -void forEachIndex(const hfm::MeshPart& meshPart, std::function func) { - for (int i = 0; i <= meshPart.quadIndices.size() - 4; i += 4) { - func((uint32_t)meshPart.quadIndices[i]); - func((uint32_t)meshPart.quadIndices[i+1]); - func((uint32_t)meshPart.quadIndices[i+2]); - func((uint32_t)meshPart.quadIndices[i+3]); - } - for (int i = 0; i <= meshPart.triangleIndices.size() - 3; i += 3) { - func((uint32_t)meshPart.triangleIndices[i]); - func((uint32_t)meshPart.triangleIndices[i+1]); - func((uint32_t)meshPart.triangleIndices[i+2]); - } -} - -void thickenFlatExtents(Extents& extents) { - // Add epsilon to extents to compensate for flat plane - extents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON); - extents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON); -} - -void calculateExtentsForTriangleListMesh(TriangleListMesh& triangleListMesh) { - triangleListMesh.partExtents.resize(triangleListMesh.parts.size()); - for (size_t partIndex = 0; partIndex < triangleListMesh.parts.size(); ++partIndex) { - const auto& part = triangleListMesh.parts[partIndex]; - auto& extents = triangleListMesh.partExtents[partIndex]; - int partEnd = part.x + part.y; - for (int i = part.x; i < partEnd; ++i) { - auto index = triangleListMesh.indices[i]; - const auto& position = triangleListMesh.vertices[index]; - extents.addPoint(position); - } - } -} - -void calculateExtentsForShape(hfm::Shape& shape, const std::vector& triangleListMeshes, const std::vector& joints) { - auto& shapeExtents = shape.transformedExtents; - shapeExtents.reset(); - - const auto& triangleListMesh = triangleListMeshes[shape.mesh]; - const auto& partExtent = triangleListMesh.partExtents[shape.meshPart]; - - const glm::mat4& transform = joints[shape.joint].transform; - shapeExtents = partExtent; - shapeExtents.transform(transform); - - thickenFlatExtents(shapeExtents); -} - -void calculateExtentsForModel(Extents& modelExtents, const std::vector& shapes) { - modelExtents.reset(); - - for (size_t i = 0; i < shapes.size(); ++i) { - const auto& shape = shapes[i]; - const auto& shapeExtents = shape.transformedExtents; - modelExtents.addExtents(shapeExtents); - } -} - -ReweightedDeformers getReweightedDeformers(const size_t numMeshVertices, const std::vector skinClusters, const uint16_t weightsPerVertex) { - ReweightedDeformers reweightedDeformers; - if (skinClusters.size() == 0) { - return reweightedDeformers; - } - - size_t numClusterIndices = numMeshVertices * weightsPerVertex; - reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(skinClusters.size() - 1)); - reweightedDeformers.weights.resize(numClusterIndices, 0); - reweightedDeformers.weightsPerVertex = weightsPerVertex; - - std::vector weightAccumulators; - weightAccumulators.resize(numClusterIndices, 0.0f); - for (uint16_t i = 0; i < (uint16_t)skinClusters.size(); ++i) { - const hfm::SkinCluster& skinCluster = skinClusters[i]; - - if (skinCluster.indices.size() != skinCluster.weights.size()) { - reweightedDeformers.trimmedToMatch = true; - } - size_t numIndicesOrWeights = std::min(skinCluster.indices.size(), skinCluster.weights.size()); - for (size_t j = 0; j < numIndicesOrWeights; ++j) { - uint32_t index = skinCluster.indices[j]; - float weight = skinCluster.weights[j]; - - // look for an unused slot in the weights vector - uint32_t weightIndex = index * weightsPerVertex; - uint32_t lowestIndex = -1; - float lowestWeight = FLT_MAX; - uint16_t k = 0; - for (; k < weightsPerVertex; k++) { - if (weightAccumulators[weightIndex + k] == 0.0f) { - reweightedDeformers.indices[weightIndex + k] = i; - weightAccumulators[weightIndex + k] = weight; - break; - } - if (weightAccumulators[weightIndex + k] < lowestWeight) { - lowestIndex = k; - lowestWeight = weightAccumulators[weightIndex + k]; - } - } - if (k == weightsPerVertex && weight > lowestWeight) { - // no space for an additional weight; we must replace the lowest - weightAccumulators[weightIndex + lowestIndex] = weight; - reweightedDeformers.indices[weightIndex + lowestIndex] = i; - } - } - } - - // now that we've accumulated the most relevant weights for each vertex - // normalize and compress to 16-bits - for (size_t i = 0; i < numMeshVertices; ++i) { - size_t j = i * weightsPerVertex; - - // normalize weights into uint16_t - float totalWeight = 0.0f; - for (size_t k = j; k < j + weightsPerVertex; ++k) { - totalWeight += weightAccumulators[k]; - } - - const float ALMOST_HALF = 0.499f; - if (totalWeight > 0.0f) { - float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; - for (size_t k = j; k < j + weightsPerVertex; ++k) { - reweightedDeformers.weights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); - } - } else { - reweightedDeformers.weights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); - } - } - - return reweightedDeformers; -} - -const TriangleListMesh generateTriangleListMesh(const std::vector& srcVertices, const std::vector& srcParts) { - - TriangleListMesh dest; - - // copy vertices for now - dest.vertices = srcVertices; - - std::vector oldToNewIndex(srcVertices.size()); - { - std::unordered_map uniqueVertexToNewIndex; - int oldIndex = 0; - int newIndex = 0; - for (const auto& srcVertex : srcVertices) { - auto foundIndex = uniqueVertexToNewIndex.find(srcVertex); - if (foundIndex != uniqueVertexToNewIndex.end()) { - oldToNewIndex[oldIndex] = foundIndex->second; - } else { - uniqueVertexToNewIndex[srcVertex] = newIndex; - oldToNewIndex[oldIndex] = newIndex; - dest.vertices[newIndex] = srcVertex; - ++newIndex; - } - ++oldIndex; - } - if (uniqueVertexToNewIndex.size() < srcVertices.size()) { - dest.vertices.resize(uniqueVertexToNewIndex.size()); - dest.vertices.shrink_to_fit(); - } - } - - auto newIndicesCount = 0; - for (const auto& part : srcParts) { - newIndicesCount += part.triangleIndices.size() + part.quadTrianglesIndices.size(); - } - - { - dest.indices.resize(newIndicesCount); - int i = 0; - for (const auto& part : srcParts) { - glm::ivec2 spart(i, 0); - for (const auto& qti : part.quadTrianglesIndices) { - dest.indices[i] = oldToNewIndex[qti]; - ++i; - } - for (const auto& ti : part.triangleIndices) { - dest.indices[i] = oldToNewIndex[ti]; - ++i; - } - spart.y = i - spart.x; - dest.parts.push_back(spart); - } - } - - calculateExtentsForTriangleListMesh(dest); - - return dest; -} - -}; diff --git a/libraries/hfm/src/hfm/HFMModelMath.h b/libraries/hfm/src/hfm/HFMModelMath.h deleted file mode 100644 index ef86e7379a..0000000000 --- a/libraries/hfm/src/hfm/HFMModelMath.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// HFMModelMath.h -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/10/04. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_hfm_ModelMath_h -#define hifi_hfm_ModelMath_h - -#include "HFM.h" - -namespace hfm { - -void forEachIndex(const hfm::MeshPart& meshPart, std::function func); - -void initializeExtents(Extents& extents); - -void calculateExtentsForTriangleListMesh(TriangleListMesh& triangleListMesh); - -// This can't be moved to model-baker until -void calculateExtentsForShape(hfm::Shape& shape, const std::vector& triangleListMeshes, const std::vector& joints); - -void calculateExtentsForModel(Extents& modelExtents, const std::vector& shapes); - -struct ReweightedDeformers { - std::vector indices; - std::vector weights; - uint16_t weightsPerVertex { 0 }; - bool trimmedToMatch { false }; -}; - -const uint16_t DEFAULT_SKINNING_WEIGHTS_PER_VERTEX = 4; - -ReweightedDeformers getReweightedDeformers(const size_t numMeshVertices, const std::vector skinClusters, const uint16_t weightsPerVertex = DEFAULT_SKINNING_WEIGHTS_PER_VERTEX); - -const TriangleListMesh generateTriangleListMesh(const std::vector& srcVertices, const std::vector& srcParts); - -}; - -#endif // #define hifi_hfm_ModelMath_h diff --git a/libraries/hfm/src/hfm/HFMSerializer.h b/libraries/hfm/src/hfm/HFMSerializer.h index f28ef9f9c3..d0be588d60 100644 --- a/libraries/hfm/src/hfm/HFMSerializer.h +++ b/libraries/hfm/src/hfm/HFMSerializer.h @@ -1,5 +1,5 @@ // -// HFMSerializer.h +// FBXSerializer.h // libraries/hfm/src/hfm // // Created by Sabrina Shanman on 2018/11/07. diff --git a/libraries/image/src/image/TextureProcessing.h b/libraries/image/src/image/TextureProcessing.h index 677d48c61f..326ec700a9 100644 --- a/libraries/image/src/image/TextureProcessing.h +++ b/libraries/image/src/image/TextureProcessing.h @@ -33,7 +33,7 @@ namespace TextureUsage { /**jsdoc *

Describes the type of texture.

*

See also: {@link Material} and - * {@link https://docs.projectathena.dev/create/3d-models/pbr-materials-guide.html|PBR Materials Guide}.

+ * {@link https://docs.vircadia.dev/create/3d-models/pbr-materials-guide.html|PBR Materials Guide}.

*
CameraIndependentnumbernumberThe camera is in independent mode.
CameraEntitynumbernumberThe camera is in entity mode.
InHMDnumbernumberThe user is in HMD mode.
AdvancedMovementnumbernumberAdvanced movement (walking) controls are + *
AdvancedMovementnumbernumberAdvanced movement (walking) controls are * enabled.
StrafeEnablednumbernumberStrafing is enabled
LeftHandDominantnumbernumberDominant hand set to left.
Set your avatar to it's default T-pose for a while.
- * Avatar in T-pose
Set your avatar to its default T-pose then rotate its right arm.
- * Avatar in T-pose with arm rotated
Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between * the head and body or you will see the neck stretched.
- * Avatar with neck stretched
Set your avatar to its default T-pose then rotate its right arm.
- * Avatar in T-pose
Open your avatar's mouth wide.Color a sphere using a Material entity.Rez a Vive tracker puck.Create a ball of green smoke.Draw a textured "V".Create a textured PolyVox sphere.Create a Web entity displaying at 1920 x 1080 resolution.
* * diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h index 617e40ce06..cd3eb109d7 100644 --- a/libraries/ktx/src/khronos/KHR.h +++ b/libraries/ktx/src/khronos/KHR.h @@ -11,6 +11,7 @@ #define khronos_khr_hpp #include +#include namespace khronos { diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 986682d7f9..e2b84ec333 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -23,7 +23,7 @@ /**jsdoc * The Midi API provides the ability to connect Interface with musical instruments and other external or virtual * devices via the MIDI protocol. For further information and examples, see the tutorial: - * Use MIDI to Control Your Environment. + * Use MIDI to Control Your Environment. * *

Note: Only works on Windows.

* diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index d200df211d..47a8db82b8 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -13,61 +13,34 @@ #include "BakerTypes.h" #include "ModelMath.h" -#include "CollectShapeVerticesTask.h" #include "BuildGraphicsMeshTask.h" #include "CalculateMeshNormalsTask.h" #include "CalculateMeshTangentsTask.h" #include "CalculateBlendshapeNormalsTask.h" #include "CalculateBlendshapeTangentsTask.h" #include "PrepareJointsTask.h" -#include "CalculateTransformedExtentsTask.h" #include "BuildDracoMeshTask.h" #include "ParseFlowDataTask.h" -#include namespace baker { class GetModelPartsTask { public: using Input = hfm::Model::Pointer; - using Output = VaryingSet9, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector, std::vector, std::vector, Extents, std::vector>; + using Output = VaryingSet5, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector>; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { const auto& hfmModelIn = input; - output.edit0() = hfmModelIn->meshes; + output.edit0() = hfmModelIn->meshes.toStdVector(); output.edit1() = hfmModelIn->originalURL; output.edit2() = hfmModelIn->meshIndicesToModelNames; auto& blendshapesPerMesh = output.edit3(); blendshapesPerMesh.reserve(hfmModelIn->meshes.size()); - for (size_t i = 0; i < hfmModelIn->meshes.size(); i++) { + for (int i = 0; i < hfmModelIn->meshes.size(); i++) { blendshapesPerMesh.push_back(hfmModelIn->meshes[i].blendshapes.toStdVector()); } - output.edit4() = hfmModelIn->joints; - output.edit5() = hfmModelIn->shapes; - output.edit6() = hfmModelIn->skinDeformers; - output.edit7() = hfmModelIn->meshExtents; - output.edit8() = hfmModelIn->materials; - } - }; - - class BuildMeshTriangleListTask { - public: - using Input = std::vector; - using Output = std::vector; - using JobModel = Job::ModelIO; - - void run(const BakeContextPointer& context, const Input& input, Output& output) { - const auto& meshesIn = input; - auto& indexedTrianglesMeshOut = output; - indexedTrianglesMeshOut.clear(); - indexedTrianglesMeshOut.resize(meshesIn.size()); - - for (size_t i = 0; i < meshesIn.size(); i++) { - auto& mesh = meshesIn[i]; - const auto verticesStd = mesh.vertices.toStdVector(); - indexedTrianglesMeshOut[i] = hfm::generateTriangleListMesh(verticesStd, mesh.parts); - } + output.edit4() = hfmModelIn->joints.toStdVector(); } }; @@ -102,23 +75,21 @@ namespace baker { class BuildMeshesTask { public: - using Input = VaryingSet6, std::vector, std::vector, NormalsPerMesh, TangentsPerMesh, BlendshapesPerMesh>; + using Input = VaryingSet5, std::vector, NormalsPerMesh, TangentsPerMesh, BlendshapesPerMesh>; using Output = std::vector; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { auto& meshesIn = input.get0(); int numMeshes = (int)meshesIn.size(); - auto& triangleListMeshesIn = input.get1(); - auto& graphicsMeshesIn = input.get2(); - auto& normalsPerMeshIn = input.get3(); - auto& tangentsPerMeshIn = input.get4(); - auto& blendshapesPerMeshIn = input.get5(); + auto& graphicsMeshesIn = input.get1(); + auto& normalsPerMeshIn = input.get2(); + auto& tangentsPerMeshIn = input.get3(); + auto& blendshapesPerMeshIn = input.get4(); auto meshesOut = meshesIn; for (int i = 0; i < numMeshes; i++) { auto& meshOut = meshesOut[i]; - meshOut.triangleListMesh = triangleListMeshesIn[i]; meshOut._mesh = safeGet(graphicsMeshesIn, i); meshOut.normals = QVector::fromStdVector(safeGet(normalsPerMeshIn, i)); meshOut.tangents = QVector::fromStdVector(safeGet(tangentsPerMeshIn, i)); @@ -130,22 +101,17 @@ namespace baker { class BuildModelTask { public: - using Input = VaryingSet9, std::vector, QMap, QHash, FlowData, std::vector, std::vector, Extents>; + using Input = VaryingSet6, std::vector, QMap, QHash, FlowData>; using Output = hfm::Model::Pointer; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { auto hfmModelOut = input.get0(); - hfmModelOut->meshes = input.get1(); - hfmModelOut->joints = input.get2(); + hfmModelOut->meshes = QVector::fromStdVector(input.get1()); + hfmModelOut->joints = QVector::fromStdVector(input.get2()); hfmModelOut->jointRotationOffsets = input.get3(); hfmModelOut->jointIndices = input.get4(); hfmModelOut->flowData = input.get5(); - hfmModelOut->shapeVertices = input.get6(); - hfmModelOut->shapes = input.get7(); - hfmModelOut->meshExtents = input.get8(); - // These depend on the ShapeVertices - // TODO: Create a task for this rather than calculating it here hfmModelOut->computeKdops(); output = hfmModelOut; } @@ -168,10 +134,6 @@ namespace baker { const auto meshIndicesToModelNames = modelPartsIn.getN(2); const auto blendshapesPerMeshIn = modelPartsIn.getN(3); const auto jointsIn = modelPartsIn.getN(4); - const auto shapesIn = modelPartsIn.getN(5); - const auto skinDeformersIn = modelPartsIn.getN(6); - const auto modelExtentsIn = modelPartsIn.getN(7); - const auto materialsIn = modelPartsIn.getN(8); // Calculate normals and tangents for meshes and blendshapes if they do not exist // Note: Normals are never calculated here for OBJ models. OBJ files optionally define normals on a per-face basis, so for consistency normals are calculated beforehand in OBJSerializer. @@ -183,15 +145,8 @@ namespace baker { const auto calculateBlendshapeTangentsInputs = CalculateBlendshapeTangentsTask::Input(normalsPerBlendshapePerMesh, blendshapesPerMeshIn, meshesIn).asVarying(); const auto tangentsPerBlendshapePerMesh = model.addJob("CalculateBlendshapeTangents", calculateBlendshapeTangentsInputs); - // Calculate shape vertices. These rely on the weight-normalized clusterIndices/clusterWeights in the mesh, and are used later for computing the joint kdops - const auto collectShapeVerticesInputs = CollectShapeVerticesTask::Input(meshesIn, shapesIn, jointsIn, skinDeformersIn).asVarying(); - const auto shapeVerticesPerJoint = model.addJob("CollectShapeVertices", collectShapeVerticesInputs); - - // Build the slim triangle list mesh for each hfm::mesh - const auto triangleListMeshes = model.addJob("BuildMeshTriangleListTask", meshesIn); - // Build the graphics::MeshPointer for each hfm::Mesh - const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh, shapesIn, skinDeformersIn).asVarying(); + const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh).asVarying(); const auto graphicsMeshes = model.addJob("BuildGraphicsMesh", buildGraphicsMeshInputs); // Prepare joint information @@ -201,12 +156,6 @@ namespace baker { const auto jointRotationOffsets = jointInfoOut.getN(1); const auto jointIndices = jointInfoOut.getN(2); - // Use transform information to compute extents - const auto calculateExtentsInputs = CalculateTransformedExtentsTask::Input(modelExtentsIn, triangleListMeshes, shapesIn, jointsOut).asVarying(); - const auto calculateExtentsOutputs = model.addJob("CalculateExtents", calculateExtentsInputs); - const auto modelExtentsOut = calculateExtentsOutputs.getN(0); - const auto shapesOut = calculateExtentsOutputs.getN(1); - // Parse material mapping const auto parseMaterialMappingInputs = ParseMaterialMappingTask::Input(mapping, materialMappingBaseURL).asVarying(); const auto materialMapping = model.addJob("ParseMaterialMapping", parseMaterialMappingInputs); @@ -216,7 +165,7 @@ namespace baker { // TODO: Tangent support (Needs changes to FBXSerializer_Mesh as well) // NOTE: Due to an unresolved linker error, BuildDracoMeshTask is not functional on Android // TODO: Figure out why BuildDracoMeshTask.cpp won't link with draco on Android - const auto buildDracoMeshInputs = BuildDracoMeshTask::Input(shapesOut, meshesIn, materialsIn, normalsPerMesh, tangentsPerMesh).asVarying(); + const auto buildDracoMeshInputs = BuildDracoMeshTask::Input(meshesIn, normalsPerMesh, tangentsPerMesh).asVarying(); const auto buildDracoMeshOutputs = model.addJob("BuildDracoMesh", buildDracoMeshInputs); const auto dracoMeshes = buildDracoMeshOutputs.getN(0); const auto dracoErrors = buildDracoMeshOutputs.getN(1); @@ -228,9 +177,9 @@ namespace baker { // Combine the outputs into a new hfm::Model const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying(); const auto blendshapesPerMeshOut = model.addJob("BuildBlendshapes", buildBlendshapesInputs); - const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, triangleListMeshes, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying(); + const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying(); const auto meshesOut = model.addJob("BuildMeshes", buildMeshesInputs); - const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData, shapeVerticesPerJoint, shapesOut, modelExtentsOut).asVarying(); + const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData).asVarying(); const auto hfmModelOut = model.addJob("BuildModel", buildModelInputs); output = Output(hfmModelOut, materialMapping, dracoMeshes, dracoErrors, materialList); diff --git a/libraries/model-baker/src/model-baker/BakerTypes.h b/libraries/model-baker/src/model-baker/BakerTypes.h index 8760fa6db4..3d16afab2e 100644 --- a/libraries/model-baker/src/model-baker/BakerTypes.h +++ b/libraries/model-baker/src/model-baker/BakerTypes.h @@ -36,14 +36,6 @@ namespace baker { using TangentsPerBlendshape = std::vector>; using MeshIndicesToModelNames = QHash; - - class ReweightedDeformers { - public: - std::vector indices; - std::vector weights; - uint16_t weightsPerVertex { 0 }; - bool trimmedToMatch { false }; - }; }; #endif // hifi_BakerTypes_h diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp index 5c9d1dac25..12347c30b1 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp @@ -39,47 +39,19 @@ #include "ModelMath.h" #ifndef Q_OS_ANDROID - -void reindexMaterials(const std::vector& originalMaterialIndices, std::vector& materials, std::vector& materialIndices) { - materialIndices.resize(originalMaterialIndices.size()); - for (size_t i = 0; i < originalMaterialIndices.size(); ++i) { - uint32_t material = originalMaterialIndices[i]; - auto foundMaterial = std::find(materials.cbegin(), materials.cend(), material); - if (foundMaterial == materials.cend()) { - materials.push_back(material); - materialIndices[i] = (uint16_t)(materials.size() - 1); - } else { - materialIndices[i] = (uint16_t)(foundMaterial - materials.cbegin()); +std::vector createMaterialList(const hfm::Mesh& mesh) { + std::vector materialList; + for (const auto& meshPart : mesh.parts) { + auto materialID = QVariant(meshPart.materialID).toByteArray(); + const auto materialIt = std::find(materialList.cbegin(), materialList.cend(), materialID); + if (materialIt == materialList.cend()) { + materialList.push_back(materialID); } } + return materialList; } -void createMaterialLists(const std::vector& shapes, const std::vector& meshes, const std::vector& hfmMaterials, std::vector>& materialIndexLists, std::vector>& partMaterialIndicesPerMesh) { - std::vector> materialsPerMesh; - for (const auto& mesh : meshes) { - materialsPerMesh.emplace_back(mesh.parts.size(), hfm::UNDEFINED_KEY); - } - for (const auto& shape : shapes) { - materialsPerMesh[shape.mesh][shape.meshPart] = shape.material; - } - - materialIndexLists.resize(materialsPerMesh.size()); - partMaterialIndicesPerMesh.resize(materialsPerMesh.size()); - for (size_t i = 0; i < materialsPerMesh.size(); ++i) { - const std::vector& materials = materialsPerMesh[i]; - std::vector uniqueMaterials; - - reindexMaterials(materials, uniqueMaterials, partMaterialIndicesPerMesh[i]); - - materialIndexLists[i].reserve(uniqueMaterials.size()); - for (const uint32_t material : uniqueMaterials) { - const auto& hfmMaterial = hfmMaterials[material]; - materialIndexLists[i].push_back(QVariant(hfmMaterial.materialID).toByteArray()); - } - } -} - -std::tuple, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& partMaterialIndices) { +std::tuple, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& materialList) { Q_ASSERT(normals.size() == 0 || (int)normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); @@ -150,9 +122,11 @@ std::tuple, bool> createDracoMesh(const hfm::Mesh& auto partIndex = 0; draco::FaceIndex face; + uint16_t materialID; for (auto& part : mesh.parts) { - uint16_t materialID = partMaterialIndices[partIndex]; + auto materialIt = std::find(materialList.cbegin(), materialList.cend(), QVariant(part.materialID).toByteArray()); + materialID = (uint16_t)(materialIt - materialList.cbegin()); auto addFace = [&](const QVector& indices, int index, draco::FaceIndex face) { int32_t idx0 = indices[index]; @@ -240,33 +214,30 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp #ifdef Q_OS_ANDROID qCWarning(model_baker) << "BuildDracoMesh is disabled on Android. Output meshes will be empty."; #else - const auto& shapes = input.get0(); - const auto& meshes = input.get1(); - const auto& materials = input.get2(); - const auto& normalsPerMesh = input.get3(); - const auto& tangentsPerMesh = input.get4(); + const auto& meshes = input.get0(); + const auto& normalsPerMesh = input.get1(); + const auto& tangentsPerMesh = input.get2(); auto& dracoBytesPerMesh = output.edit0(); auto& dracoErrorsPerMesh = output.edit1(); - auto& materialLists = output.edit2(); - std::vector> partMaterialIndicesPerMesh; - createMaterialLists(shapes, meshes, materials, materialLists, partMaterialIndicesPerMesh); dracoBytesPerMesh.reserve(meshes.size()); // vector is an exception to the std::vector conventions as it is a bit field // So a bool reference to an element doesn't work dracoErrorsPerMesh.resize(meshes.size()); + materialLists.reserve(meshes.size()); for (size_t i = 0; i < meshes.size(); i++) { const auto& mesh = meshes[i]; const auto& normals = baker::safeGet(normalsPerMesh, i); const auto& tangents = baker::safeGet(tangentsPerMesh, i); dracoBytesPerMesh.emplace_back(); auto& dracoBytes = dracoBytesPerMesh.back(); - const auto& partMaterialIndices = partMaterialIndicesPerMesh[i]; + materialLists.push_back(createMaterialList(mesh)); + const auto& materialList = materialLists.back(); bool dracoError; std::unique_ptr dracoMesh; - std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, partMaterialIndices); + std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList); dracoErrorsPerMesh[i] = dracoError; if (dracoMesh) { diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h index a83f2ae163..ac9ad648ab 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h @@ -33,7 +33,7 @@ public: class BuildDracoMeshTask { public: using Config = BuildDracoMeshConfig; - using Input = baker::VaryingSet5, std::vector, std::vector, baker::NormalsPerMesh, baker::TangentsPerMesh>; + using Input = baker::VaryingSet3, baker::NormalsPerMesh, baker::TangentsPerMesh>; using Output = baker::VaryingSet3, std::vector, std::vector>>; using JobModel = baker::Job::ModelIO; diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp index 66429ed2c4..2467da7656 100644 --- a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp @@ -2,8 +2,8 @@ // BuildGraphicsMeshTask.h // model-baker/src/model-baker // -// Created by Sabrina Shanman on 2019/09/16. -// Copyright 2019 High Fidelity, Inc. +// Created by Sabrina Shanman on 2018/12/06. +// Copyright 2018 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -15,7 +15,6 @@ #include #include "ModelBakerLogging.h" -#include #include "ModelMath.h" using vec2h = glm::tvec2; @@ -28,7 +27,7 @@ glm::vec3 normalizeDirForPacking(const glm::vec3& dir) { return dir; } -void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, const baker::MeshNormals& meshNormals, const baker::MeshTangents& meshTangentsIn, uint16_t numDeformerControllers) { +void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, const baker::MeshNormals& meshNormals, const baker::MeshTangents& meshTangentsIn) { auto graphicsMesh = std::make_shared(); // Fill tangents with a dummy value to force tangents to be present if there are normals @@ -87,24 +86,25 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics // Support for 4 skinning clusters: // 4 Indices are uint8 ideally, uint16 if more than 256. - const auto clusterIndiceElement = ((numDeformerControllers < (uint16_t)UINT8_MAX) ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)); + const auto clusterIndiceElement = (hfmMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)); // 4 Weights are normalized 16bits const auto clusterWeightElement = gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW); - // Record cluster sizes - const size_t numVertClusters = hfmMesh.clusterWeightsPerVertex == 0 ? 0 : hfmMesh.clusterIndices.size() / hfmMesh.clusterWeightsPerVertex; - const size_t clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize(); - const size_t clusterWeightsSize = numVertClusters * clusterWeightElement.getSize(); + // Cluster indices and weights must be the same sizes + const int NUM_CLUSTERS_PER_VERT = 4; + const int numVertClusters = (hfmMesh.clusterIndices.size() == hfmMesh.clusterWeights.size() ? hfmMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0); + const int clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize(); + const int clusterWeightsSize = numVertClusters * clusterWeightElement.getSize(); // Decide on where to put what seequencially in a big buffer: - const size_t positionsOffset = 0; - const size_t normalsAndTangentsOffset = positionsOffset + positionsSize; - const size_t colorsOffset = normalsAndTangentsOffset + normalsAndTangentsSize; - const size_t texCoordsOffset = colorsOffset + colorsSize; - const size_t texCoords1Offset = texCoordsOffset + texCoordsSize; - const size_t clusterIndicesOffset = texCoords1Offset + texCoords1Size; - const size_t clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; - const size_t totalVertsSize = clusterWeightsOffset + clusterWeightsSize; + const int positionsOffset = 0; + const int normalsAndTangentsOffset = positionsOffset + positionsSize; + const int colorsOffset = normalsAndTangentsOffset + normalsAndTangentsSize; + const int texCoordsOffset = colorsOffset + colorsSize; + const int texCoords1Offset = texCoordsOffset + texCoordsSize; + const int clusterIndicesOffset = texCoords1Offset + texCoords1Size; + const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; + const int totalVertsSize = clusterWeightsOffset + clusterWeightsSize; // Copy all vertex data in a single buffer auto vertBuffer = std::make_shared(); @@ -181,22 +181,22 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics // Clusters data if (clusterIndicesSize > 0) { - if (numDeformerControllers < (uint16_t)UINT8_MAX) { + if (hfmMesh.clusters.size() < UINT8_MAX) { // yay! we can fit the clusterIndices within 8-bits - int32_t numIndices = (int32_t)hfmMesh.clusterIndices.size(); - std::vector packedDeformerIndices; - packedDeformerIndices.resize(numIndices); + int32_t numIndices = hfmMesh.clusterIndices.size(); + QVector clusterIndices; + clusterIndices.resize(numIndices); for (int32_t i = 0; i < numIndices; ++i) { assert(hfmMesh.clusterIndices[i] <= UINT8_MAX); - packedDeformerIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]); + clusterIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]); } - vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) packedDeformerIndices.data()); + vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData()); } else { - vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.data()); + vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.constData()); } } if (clusterWeightsSize > 0) { - vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.data()); + vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.constData()); } @@ -206,7 +206,7 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics auto vertexBufferStream = std::make_shared(); gpu::BufferPointer attribBuffer; - size_t totalAttribBufferSize = totalVertsSize; + int totalAttribBufferSize = totalVertsSize; gpu::uint8 posChannel = 0; gpu::uint8 tangentChannel = posChannel; gpu::uint8 attribChannel = posChannel; @@ -377,17 +377,6 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const const auto& meshIndicesToModelNames = input.get2(); const auto& normalsPerMesh = input.get3(); const auto& tangentsPerMesh = input.get4(); - const auto& shapes = input.get5(); - const auto& skinDeformers = input.get6(); - - // Currently, there is only (at most) one skinDeformer per mesh - // An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY - std::vector skinDeformerPerMesh; - skinDeformerPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY); - for (const auto& shape : shapes) { - uint32_t skinDeformerIndex = shape.skinDeformer; - skinDeformerPerMesh[shape.mesh] = skinDeformerIndex; - } auto& graphicsMeshes = output; @@ -395,16 +384,9 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const for (int i = 0; i < n; i++) { graphicsMeshes.emplace_back(); auto& graphicsMesh = graphicsMeshes[i]; - - uint16_t numDeformerControllers = 0; - uint32_t skinDeformerIndex = skinDeformerPerMesh[i]; - if (skinDeformerIndex != hfm::UNDEFINED_KEY) { - const hfm::SkinDeformer& skinDeformer = skinDeformers[skinDeformerIndex]; - numDeformerControllers = (uint16_t)skinDeformer.clusters.size(); - } - + // Try to create the graphics::Mesh - buildGraphicsMesh(meshes[i], graphicsMesh, baker::safeGet(normalsPerMesh, i), baker::safeGet(tangentsPerMesh, i), numDeformerControllers); + buildGraphicsMesh(meshes[i], graphicsMesh, baker::safeGet(normalsPerMesh, i), baker::safeGet(tangentsPerMesh, i)); // Choose a name for the mesh if (graphicsMesh) { diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h index 34128eabe8..bb4136c086 100644 --- a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h @@ -2,8 +2,8 @@ // BuildGraphicsMeshTask.h // model-baker/src/model-baker // -// Created by Sabrina Shanman on 2019/09/16. -// Copyright 2019 High Fidelity, Inc. +// Created by Sabrina Shanman on 2018/12/06. +// Copyright 2018 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -20,7 +20,7 @@ class BuildGraphicsMeshTask { public: - using Input = baker::VaryingSet7, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector, std::vector>; + using Input = baker::VaryingSet5, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh>; using Output = std::vector; using JobModel = baker::Job::ModelIO; diff --git a/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp b/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp index 6147ce72e7..297d8cbde7 100644 --- a/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp +++ b/libraries/model-baker/src/model-baker/CalculateMeshTangentsTask.cpp @@ -30,7 +30,7 @@ void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, co // Otherwise confirm if we have the normals and texcoords needed if (!tangentsIn.empty()) { tangentsOut = tangentsIn.toStdVector(); - } else if (!normals.empty() && mesh.vertices.size() <= mesh.texCoords.size()) { + } else if (!normals.empty() && mesh.vertices.size() == mesh.texCoords.size()) { tangentsOut.resize(normals.size()); baker::calculateTangents(mesh, [&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) { diff --git a/libraries/model-baker/src/model-baker/CalculateTransformedExtentsTask.cpp b/libraries/model-baker/src/model-baker/CalculateTransformedExtentsTask.cpp deleted file mode 100644 index 028dba4939..0000000000 --- a/libraries/model-baker/src/model-baker/CalculateTransformedExtentsTask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// CalculateTransformedExtentsTask.cpp -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/10/04. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CalculateTransformedExtentsTask.h" - -#include "hfm/HFMModelMath.h" - -void CalculateTransformedExtentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) { - const auto& modelExtentsIn = input.get0(); - const auto& triangleListMeshes = input.get1(); - const auto& shapesIn = input.get2(); - const auto& joints = input.get3(); - auto& modelExtentsOut = output.edit0(); - auto& shapesOut = output.edit1(); - - shapesOut.reserve(shapesIn.size()); - for (size_t i = 0; i < shapesIn.size(); ++i) { - shapesOut.push_back(shapesIn[i]); - auto& shapeOut = shapesOut.back(); - - auto& shapeExtents = shapeOut.transformedExtents; - if (shapeExtents.isValid()) { - continue; - } - - hfm::calculateExtentsForShape(shapeOut, triangleListMeshes, joints); - } - - modelExtentsOut = modelExtentsIn; - if (!modelExtentsOut.isValid()) { - hfm::calculateExtentsForModel(modelExtentsOut, shapesOut); - } -} diff --git a/libraries/model-baker/src/model-baker/CalculateTransformedExtentsTask.h b/libraries/model-baker/src/model-baker/CalculateTransformedExtentsTask.h deleted file mode 100644 index aed089a13d..0000000000 --- a/libraries/model-baker/src/model-baker/CalculateTransformedExtentsTask.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// CalculateTransformedExtentsTask.h -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/10/04. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CalculateExtentsTask_h -#define hifi_CalculateExtentsTask_h - -#include "Engine.h" -#include "hfm/HFM.h" - -// Calculates any undefined extents in the shapes and the model. Precalculated extents will be left alone. -// Bind extents will currently not be calculated -class CalculateTransformedExtentsTask { -public: - using Input = baker::VaryingSet4, std::vector, std::vector>; - using Output = baker::VaryingSet2>; - using JobModel = baker::Job::ModelIO; - - void run(const baker::BakeContextPointer& context, const Input& input, Output& output); -}; - -#endif // hifi_CalculateExtentsTask_h diff --git a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp deleted file mode 100644 index 13bc75ced9..0000000000 --- a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// -// CollectShapeVerticesTask.h -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/09/27. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CollectShapeVerticesTask.h" - -#include - -#include - -// Used to track and avoid duplicate shape vertices, as multiple shapes can have the same mesh and skinDeformer -class VertexSource { -public: - uint32_t mesh; - uint32_t skinDeformer; - - bool operator==(const VertexSource& other) const { - return mesh == other.mesh && - skinDeformer == other.skinDeformer; - } -}; - -void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) { - const auto& meshes = input.get0(); - const auto& shapes = input.get1(); - const auto& joints = input.get2(); - const auto& skinDeformers = input.get3(); - auto& shapeVerticesPerJoint = output; - - shapeVerticesPerJoint.resize(joints.size()); - std::vector> vertexSourcesPerJoint; - vertexSourcesPerJoint.resize(joints.size()); - for (size_t i = 0; i < shapes.size(); ++i) { - const auto& shape = shapes[i]; - const uint32_t skinDeformerKey = shape.skinDeformer; - if (skinDeformerKey == hfm::UNDEFINED_KEY) { - continue; - } - - VertexSource vertexSource; - vertexSource.mesh = shape.mesh; - vertexSource.skinDeformer = skinDeformerKey; - - const auto& skinDeformer = skinDeformers[skinDeformerKey]; - for (size_t j = 0; j < skinDeformer.clusters.size(); ++j) { - const auto& cluster = skinDeformer.clusters[j]; - const uint32_t jointIndex = cluster.jointIndex; - - auto& vertexSources = vertexSourcesPerJoint[jointIndex]; - if (std::find(vertexSources.cbegin(), vertexSources.cend(), vertexSource) == vertexSources.cend()) { - vertexSources.push_back(vertexSource); - auto& shapeVertices = shapeVerticesPerJoint[jointIndex]; - - const auto& mesh = meshes[shape.mesh]; - const auto& vertices = mesh.vertices; - const glm::mat4 meshToJoint = cluster.inverseBindMatrix; - - const uint16_t weightsPerVertex = mesh.clusterWeightsPerVertex; - if (weightsPerVertex == 0) { - for (int vertexIndex = 0; vertexIndex < (int)vertices.size(); ++vertexIndex) { - const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertices[vertexIndex]); - shapeVertices.push_back(extractTranslation(vertexTransform)); - } - } else { - for (int vertexIndex = 0; vertexIndex < (int)vertices.size(); ++vertexIndex) { - for (uint16_t weightIndex = 0; weightIndex < weightsPerVertex; ++weightIndex) { - const size_t index = vertexIndex*weightsPerVertex + weightIndex; - const uint16_t clusterIndex = mesh.clusterIndices[index]; - const uint16_t clusterWeight = mesh.clusterWeights[index]; - // Remember vertices associated with this joint with at least 1/4 weight - const uint16_t EXPANSION_WEIGHT_THRESHOLD = std::numeric_limits::max() / 4; - if (clusterIndex != j || clusterWeight < EXPANSION_WEIGHT_THRESHOLD) { - continue; - } - - const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertices[vertexIndex]); - shapeVertices.push_back(extractTranslation(vertexTransform)); - } - } - } - } - } - } -} diff --git a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h deleted file mode 100644 index a665004d6b..0000000000 --- a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// CollectShapeVerticesTask.h -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/09/27. -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CollectShapeVerticesTask_h -#define hifi_CollectShapeVerticesTask_h - -#include - -#include "Engine.h" -#include "BakerTypes.h" - -class CollectShapeVerticesTask { -public: - using Input = baker::VaryingSet4, std::vector, std::vector, std::vector>; - using Output = std::vector; - using JobModel = baker::Job::ModelIO; - - void run(const baker::BakeContextPointer& context, const Input& input, Output& output); -}; - -#endif // hifi_CollectShapeVerticesTask_h - diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index bb911c6914..1fcfcfcc70 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -203,23 +203,23 @@ QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) { return textureBaseUrl.isValid() ? textureBaseUrl : url; } -ModelResource::ModelResource(const ModelResource& other) : +GeometryResource::GeometryResource(const GeometryResource& other) : Resource(other), - NetworkModel(other), + Geometry(other), _modelLoader(other._modelLoader), _mappingPair(other._mappingPair), _textureBaseURL(other._textureBaseURL), _combineParts(other._combineParts), _isCacheable(other._isCacheable) { - if (other._modelResource) { + if (other._geometryResource) { _startedLoading = false; } } -void ModelResource::downloadFinished(const QByteArray& data) { +void GeometryResource::downloadFinished(const QByteArray& data) { if (_effectiveBaseURL.fileName().toLower().endsWith(".fst")) { - PROFILE_ASYNC_BEGIN(resource_parse_geometry, "ModelResource::downloadFinished", _url.toString(), { { "url", _url.toString() } }); + PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString(), { { "url", _url.toString() } }); // store parsed contents of FST file _mapping = FSTReader::readMapping(data); @@ -267,19 +267,19 @@ void ModelResource::downloadFinished(const QByteArray& data) { auto modelCache = DependencyManager::get(); GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; - // Get the raw ModelResource - _modelResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); + // Get the raw GeometryResource + _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); // Avoid caching nested resources - their references will be held by the parent - _modelResource->_isCacheable = false; + _geometryResource->_isCacheable = false; - if (_modelResource->isLoaded()) { - onGeometryMappingLoaded(!_modelResource->getURL().isEmpty()); + if (_geometryResource->isLoaded()) { + onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty()); } else { if (_connection) { disconnect(_connection); } - _connection = connect(_modelResource.data(), &Resource::finished, this, &ModelResource::onGeometryMappingLoaded); + _connection = connect(_geometryResource.data(), &Resource::finished, this, &GeometryResource::onGeometryMappingLoaded); } } } else { @@ -291,31 +291,32 @@ void ModelResource::downloadFinished(const QByteArray& data) { } } -void ModelResource::onGeometryMappingLoaded(bool success) { - if (success && _modelResource) { - _hfmModel = _modelResource->_hfmModel; - _materialMapping = _modelResource->_materialMapping; - _meshes = _modelResource->_meshes; - _materials = _modelResource->_materials; +void GeometryResource::onGeometryMappingLoaded(bool success) { + if (success && _geometryResource) { + _hfmModel = _geometryResource->_hfmModel; + _materialMapping = _geometryResource->_materialMapping; + _meshParts = _geometryResource->_meshParts; + _meshes = _geometryResource->_meshes; + _materials = _geometryResource->_materials; // Avoid holding onto extra references - _modelResource.reset(); + _geometryResource.reset(); // Make sure connection will not trigger again disconnect(_connection); // FIXME Should not have to do this } - PROFILE_ASYNC_END(resource_parse_geometry, "ModelResource::downloadFinished", _url.toString()); + PROFILE_ASYNC_END(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString()); finishedLoading(success); } -void ModelResource::setExtra(void* extra) { +void GeometryResource::setExtra(void* extra) { const GeometryExtra* geometryExtra = static_cast(extra); _mappingPair = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash()); _textureBaseURL = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl(); _combineParts = geometryExtra ? geometryExtra->combineParts : true; } -void ModelResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) { +void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) { // Assume ownership of the processed HFMModel _hfmModel = hfmModel; _materialMapping = materialMapping; @@ -328,23 +329,31 @@ void ModelResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const Mate } std::shared_ptr meshes = std::make_shared(); + std::shared_ptr parts = std::make_shared(); int meshID = 0; for (const HFMMesh& mesh : _hfmModel->meshes) { // Copy mesh pointers meshes->emplace_back(mesh._mesh); + int partID = 0; + for (const HFMMeshPart& part : mesh.parts) { + // Construct local parts + parts->push_back(std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID])); + partID++; + } meshID++; } _meshes = meshes; + _meshParts = parts; finishedLoading(true); } -void ModelResource::deleter() { +void GeometryResource::deleter() { resetTextures(); Resource::deleter(); } -void ModelResource::setTextures() { +void GeometryResource::setTextures() { if (_hfmModel) { for (const HFMMaterial& material : _hfmModel->materials) { _materials.push_back(std::make_shared(material, _textureBaseURL)); @@ -352,7 +361,7 @@ void ModelResource::setTextures() { } } -void ModelResource::resetTextures() { +void GeometryResource::resetTextures() { _materials.clear(); } @@ -368,17 +377,17 @@ ModelCache::ModelCache() { } QSharedPointer ModelCache::createResource(const QUrl& url) { - return QSharedPointer(new ModelResource(url, _modelLoader), &ModelResource::deleter); + return QSharedPointer(new GeometryResource(url, _modelLoader), &GeometryResource::deleter); } QSharedPointer ModelCache::createResourceCopy(const QSharedPointer& resource) { - return QSharedPointer(new ModelResource(*resource.staticCast()), &ModelResource::deleter); + return QSharedPointer(new GeometryResource(*resource.staticCast()), &GeometryResource::deleter); } -ModelResource::Pointer ModelCache::getModelResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) { +GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) { bool combineParts = true; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; - ModelResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); + GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); if (resource) { if (resource->isLoaded() && resource->shouldSetTextures()) { resource->setTextures(); @@ -387,12 +396,12 @@ ModelResource::Pointer ModelCache::getModelResource(const QUrl& url, const Geome return resource; } -ModelResource::Pointer ModelCache::getCollisionModelResource(const QUrl& url, +GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) { bool combineParts = false; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; - ModelResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); + GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); if (resource) { if (resource->isLoaded() && resource->shouldSetTextures()) { resource->setTextures(); @@ -401,7 +410,7 @@ ModelResource::Pointer ModelCache::getCollisionModelResource(const QUrl& url, return resource; } -const QVariantMap NetworkModel::getTextures() const { +const QVariantMap Geometry::getTextures() const { QVariantMap textures; for (const auto& material : _materials) { for (const auto& texture : material->_textures) { @@ -415,21 +424,22 @@ const QVariantMap NetworkModel::getTextures() const { } // FIXME: The materials should only be copied when modified, but the Model currently caches the original -NetworkModel::NetworkModel(const NetworkModel& networkModel) { - _hfmModel = networkModel._hfmModel; - _materialMapping = networkModel._materialMapping; - _meshes = networkModel._meshes; +Geometry::Geometry(const Geometry& geometry) { + _hfmModel = geometry._hfmModel; + _materialMapping = geometry._materialMapping; + _meshes = geometry._meshes; + _meshParts = geometry._meshParts; - _materials.reserve(networkModel._materials.size()); - for (const auto& material : networkModel._materials) { + _materials.reserve(geometry._materials.size()); + for (const auto& material : geometry._materials) { _materials.push_back(std::make_shared(*material)); } - _animGraphOverrideUrl = networkModel._animGraphOverrideUrl; - _mapping = networkModel._mapping; + _animGraphOverrideUrl = geometry._animGraphOverrideUrl; + _mapping = geometry._mapping; } -void NetworkModel::setTextures(const QVariantMap& textureMap) { +void Geometry::setTextures(const QVariantMap& textureMap) { if (_meshes->size() > 0) { for (auto& material : _materials) { // Check if any material textures actually changed @@ -437,7 +447,7 @@ void NetworkModel::setTextures(const QVariantMap& textureMap) { [&textureMap](const NetworkMaterial::Textures::value_type& it) { return it.second.texture && textureMap.contains(it.second.name); })) { // FIXME: The Model currently caches the materials (waste of space!) - // so they must be copied in the NetworkModel copy-ctor + // so they must be copied in the Geometry copy-ctor // if (material->isOriginal()) { // // Copy the material to avoid mutating the cached version // material = std::make_shared(*material); @@ -451,11 +461,11 @@ void NetworkModel::setTextures(const QVariantMap& textureMap) { // If we only use cached textures, they should all be loaded areTexturesLoaded(); } else { - qCWarning(modelnetworking) << "Ignoring setTextures(); NetworkModel not ready"; + qCWarning(modelnetworking) << "Ignoring setTextures(); geometry not ready"; } } -bool NetworkModel::areTexturesLoaded() const { +bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { for (auto& material : _materials) { if (material->isMissingTexture()) { @@ -490,28 +500,30 @@ bool NetworkModel::areTexturesLoaded() const { return true; } -const std::shared_ptr NetworkModel::getShapeMaterial(int shapeID) const { - uint32_t materialID = getHFMModel().shapes[shapeID].material; - if (materialID < (uint32_t)_materials.size()) { - return _materials[materialID]; +const std::shared_ptr Geometry::getShapeMaterial(int partID) const { + if ((partID >= 0) && (partID < (int)_meshParts->size())) { + int materialID = _meshParts->at(partID)->materialID; + if ((materialID >= 0) && (materialID < (int)_materials.size())) { + return _materials[materialID]; + } } return nullptr; } -void ModelResourceWatcher::startWatching() { - connect(_resource.data(), &Resource::finished, this, &ModelResourceWatcher::resourceFinished); - connect(_resource.data(), &Resource::onRefresh, this, &ModelResourceWatcher::resourceRefreshed); +void GeometryResourceWatcher::startWatching() { + connect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); + connect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); if (_resource->isLoaded()) { resourceFinished(!_resource->getURL().isEmpty()); } } -void ModelResourceWatcher::stopWatching() { - disconnect(_resource.data(), &Resource::finished, this, &ModelResourceWatcher::resourceFinished); - disconnect(_resource.data(), &Resource::onRefresh, this, &ModelResourceWatcher::resourceRefreshed); +void GeometryResourceWatcher::stopWatching() { + disconnect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); + disconnect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); } -void ModelResourceWatcher::setResource(ModelResource::Pointer resource) { +void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) { if (_resource) { stopWatching(); } @@ -525,14 +537,14 @@ void ModelResourceWatcher::setResource(ModelResource::Pointer resource) { } } -void ModelResourceWatcher::resourceFinished(bool success) { +void GeometryResourceWatcher::resourceFinished(bool success) { if (success) { - _networkModelRef = std::make_shared(*_resource); + _geometryRef = std::make_shared(*_resource); } emit finished(success); } -void ModelResourceWatcher::resourceRefreshed() { +void GeometryResourceWatcher::resourceRefreshed() { // FIXME: Model is not set up to handle a refresh // _instance.reset(); } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index d61bd61696..615951345f 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -22,20 +22,23 @@ #include #include "ModelLoader.h" +class MeshPart; + using GeometryMappingPair = std::pair; Q_DECLARE_METATYPE(GeometryMappingPair) -class NetworkModel { +class Geometry { public: - using Pointer = std::shared_ptr; - using WeakPointer = std::weak_ptr; + using Pointer = std::shared_ptr; + using WeakPointer = std::weak_ptr; - NetworkModel() = default; - NetworkModel(const NetworkModel& geometry); - virtual ~NetworkModel() = default; + Geometry() = default; + Geometry(const Geometry& geometry); + virtual ~Geometry() = default; // Immutable over lifetime using GeometryMeshes = std::vector>; + using GeometryMeshParts = std::vector>; // Mutable, but must retain structure of vector using NetworkMaterials = std::vector>; @@ -60,6 +63,7 @@ protected: HFMModel::ConstPointer _hfmModel; MaterialMapping _materialMapping; std::shared_ptr _meshes; + std::shared_ptr _meshParts; // Copied to each geometry, mutable throughout lifetime via setTextures NetworkMaterials _materials; @@ -72,22 +76,22 @@ private: }; /// A geometry loaded from the network. -class ModelResource : public Resource, public NetworkModel { +class GeometryResource : public Resource, public Geometry { Q_OBJECT public: - using Pointer = QSharedPointer; + using Pointer = QSharedPointer; - ModelResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {} - ModelResource(const ModelResource& other); + GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {} + GeometryResource(const GeometryResource& other); - QString getType() const override { return "Model"; } + QString getType() const override { return "Geometry"; } virtual void deleter() override; virtual void downloadFinished(const QByteArray& data) override; void setExtra(void* extra) override; - virtual bool areTexturesLoaded() const override { return isLoaded() && NetworkModel::areTexturesLoaded(); } + virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); } private slots: void onGeometryMappingLoaded(bool success); @@ -111,21 +115,21 @@ private: QUrl _textureBaseURL; bool _combineParts; - ModelResource::Pointer _modelResource; + GeometryResource::Pointer _geometryResource; QMetaObject::Connection _connection; bool _isCacheable{ true }; }; -class ModelResourceWatcher : public QObject { +class GeometryResourceWatcher : public QObject { Q_OBJECT public: - using Pointer = std::shared_ptr; + using Pointer = std::shared_ptr; - ModelResourceWatcher() = delete; - ModelResourceWatcher(NetworkModel::Pointer& geometryPtr) : _networkModelRef(geometryPtr) {} + GeometryResourceWatcher() = delete; + GeometryResourceWatcher(Geometry::Pointer& geometryPtr) : _geometryRef(geometryPtr) {} - void setResource(ModelResource::Pointer resource); + void setResource(GeometryResource::Pointer resource); QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); } int getResourceDownloadAttempts() { return _resource ? _resource->getDownloadAttempts() : 0; } @@ -143,8 +147,8 @@ private slots: void resourceRefreshed(); private: - ModelResource::Pointer _resource; - NetworkModel::Pointer& _networkModelRef; + GeometryResource::Pointer _resource; + Geometry::Pointer& _geometryRef; }; /// Stores cached model geometries. @@ -154,18 +158,18 @@ class ModelCache : public ResourceCache, public Dependency { public: - ModelResource::Pointer getModelResource(const QUrl& url, + GeometryResource::Pointer getGeometryResource(const QUrl& url, const GeometryMappingPair& mapping = GeometryMappingPair(QUrl(), QVariantHash()), const QUrl& textureBaseUrl = QUrl()); - ModelResource::Pointer getCollisionModelResource(const QUrl& url, + GeometryResource::Pointer getCollisionGeometryResource(const QUrl& url, const GeometryMappingPair& mapping = GeometryMappingPair(QUrl(), QVariantHash()), const QUrl& textureBaseUrl = QUrl()); protected: - friend class ModelResource; + friend class GeometryResource; virtual QSharedPointer createResource(const QUrl& url) override; QSharedPointer createResourceCopy(const QSharedPointer& resource) override; @@ -176,4 +180,12 @@ private: ModelLoader _modelLoader; }; +class MeshPart { +public: + MeshPart(int mesh, int part, int material) : meshID { mesh }, partID { part }, materialID { material } {} + int meshID { -1 }; + int partID { -1 }; + int materialID { -1 }; +}; + #endif // hifi_ModelCache_h diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index fabf8b0e44..3bd84bc977 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -26,7 +26,7 @@ namespace NetworkingConstants { // at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true const QUrl METAVERSE_SERVER_URL_STABLE { "https://metaverse.highfidelity.com" }; - const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging.projectathena.io" }; + const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging-metaverse.vircadia.com" }; } const QString HIFI_URL_SCHEME_ABOUT = "about"; diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 269ff94b80..94d6371ba4 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -34,6 +34,10 @@ void UserActivityLogger::disable(bool disable) { _disabled.set(disable); } +void UserActivityLogger::crashMonitorDisable(bool disable) { + _crashMonitorDisabled.set(disable); +} + void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCallbackParameters params) { // qCDebug(networking).nospace() << ">>> UserActivityLogger::logAction(" << action << "," << QJsonDocument(details).toJson(); // This logs what the UserActivityLogger would normally send to centralized servers. diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index e4b91b1e81..39efee03c4 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -35,7 +35,11 @@ public slots: bool isEnabled() { return !_disabled.get(); } bool isDisabledSettingSet() const { return _disabled.isSet(); } + bool isCrashMonitorEnabled() { return !_crashMonitorDisabled.get(); } + bool isCrashMonitorDisabledSettingSet() const { return _crashMonitorDisabled.isSet(); } + void disable(bool disable); + void crashMonitorDisable(bool disable); void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); void launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime); @@ -55,6 +59,7 @@ private slots: private: UserActivityLogger(); Setting::Handle _disabled { "UserActivityLoggerDisabled", true }; + Setting::Handle _crashMonitorDisabled { "CrashMonitorDisabled2", true }; QElapsedTimer _timer; }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 805e5d3a09..a34be09db7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -212,10 +212,11 @@ using PacketType = PacketTypeEnum::Value; const int NUM_BYTES_MD5_HASH = 16; -typedef char PacketVersion; +// NOTE: There is a max limit of 255, hopefully we have a better way to manage this by then. +typedef uint8_t PacketVersion; PacketVersion versionForPacketType(PacketType packetType); -QByteArray protocolVersionsSignature(); /// returns a unqiue signature for all the current protocols +QByteArray protocolVersionsSignature(); /// returns a unique signature for all the current protocols QString protocolVersionsSignatureBase64(); #if (PR_BUILD || DEV_BUILD) @@ -226,7 +227,7 @@ uint qHash(const PacketType& key, uint seed); QDebug operator<<(QDebug debug, const PacketType& type); // Due to the different legacy behaviour, we need special processing for domains that were created before -// the zone inheritance modes were added. These have version numbers up to 80 +// the zone inheritance modes were added. These have version numbers up to 80. enum class EntityVersion : PacketVersion { StrokeColorProperty = 0, HasDynamicOwnershipTests, @@ -278,6 +279,8 @@ enum class EntityVersion : PacketVersion { TextEntityFonts, ScriptServerKinematicMotion, ScreenshareZone, + ZoneOcclusion, + ModelBlendshapes, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 8ab502e951..5e6825c886 100755 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -482,6 +482,19 @@ bool OctreePacketData::appendValue(const QVector& value) { return success; } +bool OctreePacketData::appendValue(const QVector& value) { + uint16_t qVecSize = value.size(); + bool success = appendValue(qVecSize); + if (success) { + success = append((const unsigned char*)value.constData(), qVecSize * sizeof(QUuid)); + if (success) { + _bytesOfValues += qVecSize * sizeof(QUuid); + _totalBytesOfValues += qVecSize * sizeof(QUuid); + } + } + return success; +} + bool OctreePacketData::appendValue(const glm::quat& value) { const size_t VALUES_PER_QUAT = 4; const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT; @@ -774,6 +787,15 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto return (dataBytes - start) + (int)sizeof(uint16_t); } +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVector& result) { + uint16_t length; + memcpy(&length, dataBytes, sizeof(uint16_t)); + dataBytes += sizeof(length); + result.resize(length); + memcpy(result.data(), dataBytes, length * sizeof(QUuid)); + return sizeof(uint16_t) + length * sizeof(QUuid); +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 46e5de9bda..2050dd1487 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -186,6 +186,9 @@ public: /// appends a QVector of bools to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QVector& value); + /// appends a QVector of QUuids to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const QVector& value); + /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::quat& value); @@ -284,6 +287,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QRect& result); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 43c6fc27dc..ef5213df8f 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -109,7 +109,7 @@ btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) { glm::vec3 center = points[0]; glm::vec3 maxCorner = center; glm::vec3 minCorner = center; - for (size_t i = 1; i < points.size(); i++) { + for (int i = 1; i < points.size(); i++) { center += points[i]; maxCorner = glm::max(maxCorner, points[i]); minCorner = glm::min(minCorner, points[i]); @@ -149,7 +149,7 @@ btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) { // add the points, correcting for margin glm::vec3 relativeScale = (diagonal - glm::vec3(2.0f * margin)) / diagonal; glm::vec3 correctedPoint; - for (size_t i = 0; i < points.size(); ++i) { + for (int i = 0; i < points.size(); ++i) { correctedPoint = (points[i] - center) * relativeScale + center; hull->addPoint(btVector3(correctedPoint[0], correctedPoint[1], correctedPoint[2]), false); } @@ -217,7 +217,7 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { } const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - int32_t numIndices = (int32_t)triangleIndices.size(); + int32_t numIndices = triangleIndices.size(); if (numIndices < 3) { // not enough indices to make a single triangle return nullptr; @@ -237,7 +237,7 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { mesh.m_indexType = PHY_INTEGER; mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int32_t); } - mesh.m_numVertices = (int)pointList.size(); + mesh.m_numVertices = pointList.size(); mesh.m_vertexBase = new unsigned char[VERTICES_PER_TRIANGLE * sizeof(btScalar) * (size_t)mesh.m_numVertices]; mesh.m_vertexStride = VERTICES_PER_TRIANGLE * sizeof(btScalar); mesh.m_vertexType = PHY_FLOAT; @@ -362,7 +362,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); uint32_t numSubShapes = info.getNumSubShapes(); if (numSubShapes == 1) { - if (!pointCollection.empty()) { + if (!pointCollection.isEmpty()) { shape = createConvexHull(pointCollection[0]); } } else { @@ -380,7 +380,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) case SHAPE_TYPE_SIMPLE_COMPOUND: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - uint32_t numIndices = (uint32_t)triangleIndices.size(); + uint32_t numIndices = triangleIndices.size(); uint32_t numMeshes = info.getNumSubShapes(); const uint32_t MIN_NUM_SIMPLE_COMPOUND_INDICES = 2; // END_OF_MESH_PART + END_OF_MESH if (numMeshes > 0 && numIndices > MIN_NUM_SIMPLE_COMPOUND_INDICES) { diff --git a/libraries/plugins/src/plugins/SteamClientPlugin.h b/libraries/plugins/src/plugins/SteamClientPlugin.h index 07e320f8eb..e4d765882f 100644 --- a/libraries/plugins/src/plugins/SteamClientPlugin.h +++ b/libraries/plugins/src/plugins/SteamClientPlugin.h @@ -73,7 +73,7 @@ public slots: /**jsdoc * Opens Steam's "Choose Friends to invite" dialog if Interface is running under Steam. * @function Steam.openInviteOverlay - * @example
+ * @example * if (Steam.running) { * print("Invite Steam friends to joint you..."); * Steam.openInviteOverlay(); diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.h b/libraries/procedural/src/procedural/ProceduralMaterialCache.h index bf4b5191c2..0a44ccf0ef 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.h +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.h @@ -50,7 +50,7 @@ public: Textures getTextures() { return _textures; } protected: - friend class NetworkModel; + friend class Geometry; Textures _textures; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 4c2c487193..94b7661b2f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -32,11 +32,16 @@ bool CauterizedModel::updateGeometry() { bool needsFullUpdate = Model::updateGeometry(); if (_isCauterized && needsFullUpdate) { assert(_cauterizeMeshStates.empty()); - - // initialize the cauterizedDeforemrStates as a copy of the standard deformerStates - _cauterizeMeshStates.resize(_meshStates.size()); - for (int i = 0; i < (int) _meshStates.size(); ++i) { - _cauterizeMeshStates[i] = _meshStates[i]; + const HFMModel& hfmModel = getHFMModel(); + foreach (const HFMMesh& mesh, hfmModel.meshes) { + Model::MeshState state; + if (_useDualQuaternionSkinning) { + state.clusterDualQuaternions.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } else { + state.clusterMatrices.resize(mesh.clusters.size()); + _cauterizeMeshStates.append(state); + } } } return needsFullUpdate; @@ -45,12 +50,20 @@ bool CauterizedModel::updateGeometry() { void CauterizedModel::createRenderItemSet() { if (_isCauterized) { assert(isLoaded()); + const auto& meshes = _renderGeometry->getMeshes(); + + // all of our mesh vectors must match in size + if (meshes.size() != _meshStates.size()) { + qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + return; + } // We should not have any existing renderItems if we enter this section of code Q_ASSERT(_modelMeshRenderItems.isEmpty()); _modelMeshRenderItems.clear(); _modelMeshMaterialNames.clear(); + _modelMeshRenderItemShapes.clear(); Transform transform; transform.setTranslation(_translation); @@ -60,17 +73,25 @@ void CauterizedModel::createRenderItemSet() { offset.setScale(_scale); offset.postTranslate(_offset); - Transform::mult(transform, transform, offset); - // Run through all of the meshes, and place them into their segregated, but unsorted buckets - const auto& shapes = _renderGeometry->getHFMModel().shapes; - for (int shapeID = 0; shapeID < (int) shapes.size(); shapeID++) { - const auto& shape = shapes[shapeID]; + int shapeID = 0; + uint32_t numMeshes = (uint32_t)meshes.size(); + for (uint32_t i = 0; i < numMeshes; i++) { + const auto& mesh = meshes.at(i); + if (!mesh) { + continue; + } - _modelMeshRenderItems << std::make_shared(shared_from_this(), shape.mesh, shape.meshPart, shapeID, transform, offset, _created); - - auto material = getNetworkModel()->getShapeMaterial(shapeID); - _modelMeshMaterialNames.push_back(material ? material->getName() : ""); + // Create the render payloads + int numParts = (int)mesh->getNumParts(); + for (int partIndex = 0; partIndex < numParts; partIndex++) { + auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset, _created); + _modelMeshRenderItems << std::static_pointer_cast(ptr); + auto material = getGeometry()->getShapeMaterial(shapeID); + _modelMeshMaterialNames.push_back(material ? material->getName() : ""); + _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); + shapeID++; + } } } else { Model::createRenderItemSet(); @@ -83,26 +104,28 @@ void CauterizedModel::updateClusterMatrices() { if (!_needsUpdateClusterMatrices || !isLoaded()) { return; } - - updateShapeStatesFromRig(); - _needsUpdateClusterMatrices = false; + const HFMModel& hfmModel = getHFMModel(); - for (int skinDeformerIndex = 0; skinDeformerIndex < (int)_meshStates.size(); skinDeformerIndex++) { - MeshState& state = _meshStates[skinDeformerIndex]; - auto numClusters = state.getNumClusters(); - for (uint32_t clusterIndex = 0; clusterIndex < numClusters; clusterIndex++) { - const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(skinDeformerIndex, clusterIndex); + for (int i = 0; i < (int)_meshStates.size(); i++) { + Model::MeshState& state = _meshStates[i]; + const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; + + for (int j = 0; j < mesh.clusters.size(); j++) { + const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; if (_useDualQuaternionSkinning) { - auto jointPose = _rig.getJointPose(cbmov.jointIndex); + auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cbmov.inverseBindTransform); - state.clusterDualQuaternions[clusterIndex] = Model::TransformDualQuaternion(clusterTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { - auto jointMatrix = _rig.getJointTransform(cbmov.jointIndex); - glm_mat4u_mul(jointMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[clusterIndex]); + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -112,7 +135,6 @@ void CauterizedModel::updateClusterMatrices() { AnimPose cauterizePose = _rig.getJointPose(_rig.indexOfJoint("Neck")); cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); - Transform cauterizedDQTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); static const glm::mat4 zeroScale( glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), @@ -121,41 +143,39 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(_rig.indexOfJoint("Neck")) * zeroScale; - for (int skinDeformerIndex = 0; skinDeformerIndex < (int) _cauterizeMeshStates.size(); skinDeformerIndex++) { - Model::MeshState& nonCauterizedState = _meshStates[skinDeformerIndex]; - Model::MeshState& state = _cauterizeMeshStates[skinDeformerIndex]; + for (int i = 0; i < _cauterizeMeshStates.size(); i++) { + Model::MeshState& state = _cauterizeMeshStates[i]; + const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; - // Just reset cauterized state with normal state memcpy style - if (_useDualQuaternionSkinning) { - state.clusterDualQuaternions = nonCauterizedState.clusterDualQuaternions; - } else { - state.clusterMatrices = nonCauterizedState.clusterMatrices; - } - - // ANd only cauterize affected joints - auto numClusters = state.getNumClusters(); - for (uint32_t clusterIndex = 0; clusterIndex < numClusters; clusterIndex++) { - const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(skinDeformerIndex, clusterIndex); - if (_cauterizeBoneSet.find(cbmov.jointIndex) != _cauterizeBoneSet.end()) { - if (_useDualQuaternionSkinning) { - Transform clusterTransform; - Transform::mult(clusterTransform, cauterizedDQTransform, cbmov.inverseBindTransform); - state.clusterDualQuaternions[clusterIndex] = Model::TransformDualQuaternion(clusterTransform); - state.clusterDualQuaternions[clusterIndex].setCauterizationParameters(1.0f, cauterizePose.trans()); + for (int j = 0; j < mesh.clusters.size(); j++) { + const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; + + if (_useDualQuaternionSkinning) { + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterDualQuaternions[j] = _meshStates[i].clusterDualQuaternions[j]; } else { - glm_mat4u_mul(cauterizeMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[clusterIndex]); + Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); + state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); + } + } else { + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; + } else { + glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } } } - // post the blender if we're not currently waiting for one to finish - auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && getHFMModel().hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { - _blendedBlendshapeCoefficients = _blendshapeCoefficients; - modelBlender->noteRequiresBlend(getThisPointer()); - } + updateBlendshapes(); } void CauterizedModel::updateRenderItems() { @@ -192,60 +212,65 @@ void CauterizedModel::updateRenderItems() { render::Transaction transaction; for (int i = 0; i < (int)self->_modelMeshRenderItemIDs.size(); i++) { + auto itemID = self->_modelMeshRenderItemIDs[i]; + auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - const auto& shapeState = self->getShapeState(i); + const auto& meshState = self->getMeshState(meshIndex); + const auto& cauterizedMeshState = self->getCauterizeMeshState(meshIndex); - auto skinDeformerIndex = shapeState._skinDeformerIndex; - - bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(shapeState._meshIndex); + bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - if (skinDeformerIndex != hfm::UNDEFINED_KEY) { + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, + primitiveMode, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) { + CauterizedMeshPartPayload& data = static_cast(mmppData); + if (useDualQuaternionSkinning) { + data.updateClusterBuffer(meshState.clusterDualQuaternions, + cauterizedMeshState.clusterDualQuaternions); + data.computeAdjustedLocalBound(meshState.clusterDualQuaternions); + } else { + data.updateClusterBuffer(meshState.clusterMatrices, + cauterizedMeshState.clusterMatrices); + data.computeAdjustedLocalBound(meshState.clusterMatrices); + } - const auto& meshState = self->getMeshState(skinDeformerIndex); - const auto& cauterizedMeshState = self->getCauterizeMeshState(skinDeformerIndex); - - transaction.updateItem(itemID, - [modelTransform, shapeState, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, - primitiveMode, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) { - CauterizedMeshPartPayload& data = static_cast(mmppData); - if (useDualQuaternionSkinning) { - data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); - } else { - data.updateClusterBuffer(meshState.clusterMatrices, cauterizedMeshState.clusterMatrices); + Transform renderTransform = modelTransform; + if (useDualQuaternionSkinning) { + if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) { + const auto& dq = meshState.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = modelTransform.worldTransform(transform); } + } else { + if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) { + renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); + } + } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - Transform renderTransform = modelTransform; - // if (meshState.clusterMatrices.size() <= 2) { - // renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform); - // } - data.updateTransform(renderTransform); - data.updateTransformForCauterizedMesh(renderTransform); - data.updateTransformAndBound(modelTransform.worldTransform(shapeState._rootFromJointTransform)); + renderTransform = modelTransform; + if (useDualQuaternionSkinning) { + if (cauterizedMeshState.clusterDualQuaternions.size() == 1 || cauterizedMeshState.clusterDualQuaternions.size() == 2) { + const auto& dq = cauterizedMeshState.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); + } + } else { + if (cauterizedMeshState.clusterMatrices.size() == 1 || cauterizedMeshState.clusterMatrices.size() == 2) { + renderTransform = modelTransform.worldTransform(Transform(cauterizedMeshState.clusterMatrices[0])); + } + } + data.updateTransformForCauterizedMesh(renderTransform); - data.setEnableCauterization(enableCauterization); - data.updateKey(renderItemKeyGlobalFlags); - data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); - }); - } else { - transaction.updateItem(itemID, - [modelTransform, shapeState, invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, enableCauterization] - (ModelMeshPartPayload& mmppData) { - CauterizedMeshPartPayload& data = static_cast(mmppData); - - Transform renderTransform = modelTransform; - - renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform); - data.updateTransform(renderTransform); - data.updateTransformForCauterizedMesh(renderTransform); - - data.setEnableCauterization(enableCauterization); - data.updateKey(renderItemKeyGlobalFlags); - data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false); - }); - - } + data.setEnableCauterization(enableCauterization); + data.updateKey(renderItemKeyGlobalFlags); + data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); + }); } scene->enqueueTransaction(transaction); diff --git a/libraries/render-utils/src/CauterizedModel.h b/libraries/render-utils/src/CauterizedModel.h index 7d241d7ac6..36a96fb006 100644 --- a/libraries/render-utils/src/CauterizedModel.h +++ b/libraries/render-utils/src/CauterizedModel.h @@ -40,7 +40,7 @@ public: protected: std::unordered_set _cauterizeBoneSet; - std::vector _cauterizeMeshStates; + QVector _cauterizeMeshStates; bool _isCauterized { false }; bool _enableCauterization { false }; }; diff --git a/libraries/render-utils/src/CullFace.slh b/libraries/render-utils/src/CullFace.slh new file mode 100644 index 0000000000..b077095fc7 --- /dev/null +++ b/libraries/render-utils/src/CullFace.slh @@ -0,0 +1,20 @@ + +<@if not CULL_FACE_SLH@> +<@def CULL_FACE_SLH@> + +// NOTE: this calculation happens once per fragment. this could be optimized by creating different shaders (via defines) +// for front, back, and double-sided. for front/back-only triangles, this will simplify to always 1 or always -1 +vec3 evalFrontOrBackFaceNormal(vec3 normal) { + return (2.0 * float(gl_FrontFacing) - 1.0) * normal; +} + +<@endif@> diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index e0d23535e4..ea66ac19ec 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -116,7 +116,7 @@ static const uint SHAPE_TANGENT_OFFSET = offsetof(GeometryCache::ShapeVertex, ta std::map, gpu::PipelinePointer> GeometryCache::_webPipelines; std::map, gpu::PipelinePointer> GeometryCache::_gridPipelines; -void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, ShapeInfo::PointList &outPointList) { +void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector &outPointList) { auto geometryCache = DependencyManager::get(); const GeometryCache::Shape geometryShape = GeometryCache::getShapeForEntityShape( entityShape ); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 03865c6cc7..179d49c076 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -155,7 +155,7 @@ public: static GeometryCache::Shape getShapeForEntityShape(int entityShapeEnum); static QString stringFromShape(GeometryCache::Shape geoShape); - static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, ShapeInfo::PointList &outPointList); + static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector &outPointList); int allocateID() { return _nextID++; } void releaseID(int id); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 0786ef62ca..d150e18406 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -55,6 +55,13 @@ template <> const ShapeKey shapeGetShapeKey(const MeshPartPayload::Pointer& payl template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { return payload->render(args); } + +template <> bool payloadPassesZoneOcclusionTest(const MeshPartPayload::Pointer& payload, const std::unordered_set& containingZones) { + if (payload) { + return payload->passesZoneOcclusionTest(containingZones); + } + return false; +} } MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material, const uint64_t& created) : @@ -74,15 +81,11 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr } } -void MeshPartPayload::updateTransform(const Transform& transform) { - _worldFromLocalTransform = transform; +void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { + _transform = transform; + Transform::mult(_drawTransform, _transform, offsetTransform); _worldBound = _localBound; - _worldBound.transform(_worldFromLocalTransform); -} - -void MeshPartPayload::updateTransformAndBound(const Transform& transform) { - _worldBound = _localBound; - _worldBound.transform(transform); + _worldBound.transform(_drawTransform); } void MeshPartPayload::addMaterial(graphics::MaterialLayer material) { @@ -171,10 +174,23 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputStream(0, _drawMesh->getVertexStream()); } - void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { - batch.setModelTransform(_worldFromLocalTransform); +void MeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const { + batch.setModelTransform(_drawTransform); } +bool MeshPartPayload::passesZoneOcclusionTest(const std::unordered_set& containingZones) const { + if (!_renderWithZones.isEmpty()) { + if (!containingZones.empty()) { + for (auto renderWithZone : _renderWithZones) { + if (containingZones.find(renderWithZone) != containingZones.end()) { + return true; + } + } + } + return false; + } + return true; +} void MeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("MeshPartPayload::render"); @@ -200,7 +216,7 @@ void MeshPartPayload::render(RenderArgs* args) { auto& schema = _drawMaterials.getSchemaBuffer().get(); glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); outColor = procedural->getColor(outColor); - procedural->prepare(batch, _worldFromLocalTransform.getTranslation(), _worldFromLocalTransform.getScale(), _worldFromLocalTransform.getRotation(), _created, + procedural->prepare(batch, _drawTransform.getTranslation(), _drawTransform.getScale(), _drawTransform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); } else { @@ -246,6 +262,12 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren return payload->render(args); } +template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones) { + if (payload) { + return payload->passesZoneOcclusionTest(containingZones); + } + return false; +} } ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, @@ -255,21 +277,36 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in assert(model && model->isLoaded()); - auto shape = model->getHFMModel().shapes[shapeIndex]; - assert(shape.mesh == meshIndex); - assert(shape.meshPart == partIndex); + bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); - auto& modelMesh = model->getNetworkModel()->getMeshes().at(_meshIndex); + auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); _meshNumVertices = (int)modelMesh->getNumVertices(); + const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); - Transform renderTransform = transform; - const Model::ShapeState& shapeState = model->getShapeState(shapeIndex); - renderTransform = transform.worldTransform(shapeState._rootFromJointTransform); - updateTransform(renderTransform); + if (useDualQuaternionSkinning) { + computeAdjustedLocalBound(state.clusterDualQuaternions); + } else { + computeAdjustedLocalBound(state.clusterMatrices); + } - _deformerIndex = shape.skinDeformer; + updateTransform(transform, offsetTransform); + Transform renderTransform = transform; + if (useDualQuaternionSkinning) { + if (state.clusterDualQuaternions.size() == 1) { + const auto& dq = state.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = transform.worldTransform(Transform(transform)); + } + } else { + if (state.clusterMatrices.size() == 1) { + renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + } + } + updateTransformForSkinnedMesh(renderTransform, transform); initCache(model); @@ -293,9 +330,7 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) { if (_drawMesh) { auto vertexFormat = _drawMesh->getVertexFormat(); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); - if (_deformerIndex != hfm::UNDEFINED_KEY) { - _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - } + _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); const HFMModel& hfmModel = model->getHFMModel(); const HFMMesh& mesh = hfmModel.meshes.at(_meshIndex); @@ -304,7 +339,7 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) { _hasTangents = !mesh.tangents.isEmpty(); } - auto networkMaterial = model->getNetworkModel()->getShapeMaterial(_shapeID); + auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID); if (networkMaterial) { addMaterial(graphics::MaterialLayer(networkMaterial, 0)); } @@ -354,6 +389,12 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector(); glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); outColor = procedural->getColor(outColor); - procedural->prepare(batch, _worldFromLocalTransform.getTranslation(), _worldFromLocalTransform.getScale(), _worldFromLocalTransform.getRotation(), _created, + procedural->prepare(batch, _drawTransform.getTranslation(), _drawTransform.getScale(), _drawTransform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); } else { @@ -507,11 +549,51 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterMatrices) { + _adjustedLocalBound = _localBound; + if (clusterMatrices.size() > 0) { + _adjustedLocalBound.transform(clusterMatrices.back()); + + for (int i = 0; i < (int)clusterMatrices.size() - 1; ++i) { + AABox clusterBound = _localBound; + clusterBound.transform(clusterMatrices[i]); + _adjustedLocalBound += clusterBound; + } + } +} + +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterDualQuaternions) { + _adjustedLocalBound = _localBound; + if (clusterDualQuaternions.size() > 0) { + Transform rootTransform(clusterDualQuaternions.back().getRotation(), + clusterDualQuaternions.back().getScale(), + clusterDualQuaternions.back().getTranslation()); + _adjustedLocalBound.transform(rootTransform); + + for (int i = 0; i < (int)clusterDualQuaternions.size() - 1; ++i) { + AABox clusterBound = _localBound; + Transform transform(clusterDualQuaternions[i].getRotation(), + clusterDualQuaternions[i].getScale(), + clusterDualQuaternions[i].getTranslation()); + clusterBound.transform(transform); + _adjustedLocalBound += clusterBound; + } + } +} + void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map& blendshapeBuffers, const QVector& blendedMeshSizes) { if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); if (blendshapeBuffer != blendshapeBuffers.end()) { _meshBlendshapeBuffer = blendshapeBuffer->second; + if (_isSkinned || (_isBlendShaped && _meshBlendshapeBuffer)) { + ShapeKey::Builder builder(_shapeKey); + builder.withDeformed(); + if (_prevUseDualQuaternionSkinning) { + builder.withDualQuatSkinned(); + } + _shapeKey = builder.build(); + } } } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 92dbae2e01..8ed799bd74 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -38,8 +38,7 @@ public: virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); virtual void notifyLocationChanged() {} - void updateTransform(const Transform& transform); - void updateTransformAndBound(const Transform& transform ); + void updateTransform(const Transform& transform, const Transform& offsetTransform); // Render Item interface virtual render::ItemKey getKey() const; @@ -53,11 +52,13 @@ public: virtual void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const; // Payload resource cached values - Transform _worldFromLocalTransform; + Transform _drawTransform; + Transform _transform; int _partIndex = 0; bool _hasColorAttrib { false }; graphics::Box _localBound; + graphics::Box _adjustedLocalBound; mutable graphics::Box _worldBound; std::shared_ptr _drawMesh; @@ -74,11 +75,15 @@ public: void setCullWithParent(bool value) { _cullWithParent = value; } + void setRenderWithZones(const QVector& renderWithZones) { _renderWithZones = renderWithZones; } + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const; + static bool enableMaterialProceduralShaders; protected: render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; bool _cullWithParent { false }; + QVector _renderWithZones; uint64_t _created; }; @@ -87,6 +92,7 @@ namespace render { template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const MeshPartPayload::Pointer& payload); template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); + template <> bool payloadPassesZoneOcclusionTest(const MeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); } class ModelMeshPartPayload : public MeshPartPayload { @@ -105,6 +111,7 @@ public: // dual quaternion skinning void updateClusterBuffer(const std::vector& clusterDualQuaternions); + void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface render::ShapeKey getShapeKey() const override; @@ -117,6 +124,12 @@ public: void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override; + // matrix palette skinning + void computeAdjustedLocalBound(const std::vector& clusterMatrices); + + // dual quaternion skinning + void computeAdjustedLocalBound(const std::vector& clusterDualQuaternions); + gpu::BufferPointer _clusterBuffer; enum class ClusterBufferType { Matrices, DualQuaternions }; @@ -124,7 +137,6 @@ public: int _meshIndex; int _shapeID; - uint32_t _deformerIndex; bool _isSkinned{ false }; bool _isBlendShaped { false }; @@ -138,6 +150,7 @@ private: gpu::BufferPointer _meshBlendshapeBuffer; int _meshNumVertices; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; + bool _prevUseDualQuaternionSkinning { false }; bool _cauterized { false }; }; @@ -147,6 +160,7 @@ namespace render { template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload); template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload); template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args); + template <> bool payloadPassesZoneOcclusionTest(const ModelMeshPartPayload::Pointer& payload, const std::unordered_set& containingZones); } #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7a371af202..13617c0bc1 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -44,7 +44,7 @@ using namespace std; int nakedModelPointerTypeId = qRegisterMetaType(); -int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType(); +int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType(); int vec3VectorTypeId = qRegisterMetaType>(); int normalTypeVecTypeId = qRegisterMetaType>("QVector"); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; @@ -74,7 +74,7 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride, uint setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); - connect(&_renderWatcher, &ModelResourceWatcher::finished, this, &Model::loadURLFinished); + connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished); } Model::~Model() { @@ -154,7 +154,7 @@ void Model::setOffset(const glm::vec3& offset) { } void Model::calculateTextureInfo() { - if (!_hasCalculatedTextureInfo && isLoaded() && getNetworkModel()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) { + if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) { size_t textureSize = 0; int textureCount = 0; bool allTexturesLoaded = true; @@ -181,15 +181,15 @@ int Model::getRenderInfoTextureCount() { } bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { - if (!getNetworkModel()) { + if (!getGeometry()) { return true; } const HFMModel& hfmModel = getHFMModel(); - const auto& networkMeshes = getNetworkModel()->getMeshes(); + const auto& networkMeshes = getGeometry()->getMeshes(); // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size()) { + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() || meshIndex >= (int)_meshStates.size()) { _needsFixupInScene = true; // trigger remove/add cycle invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return true; @@ -231,45 +231,46 @@ void Model::updateRenderItems() { render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { + auto itemID = self->_modelMeshRenderItemIDs[i]; + auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - const auto& shapeState = self->getShapeState(i); + const auto& meshState = self->getMeshState(meshIndex); - auto skinDeformerIndex = shapeState._skinDeformerIndex; + bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); + bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(shapeState._meshIndex); + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, + invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) { + if (useDualQuaternionSkinning) { + data.updateClusterBuffer(meshState.clusterDualQuaternions); + data.computeAdjustedLocalBound(meshState.clusterDualQuaternions); + } else { + data.updateClusterBuffer(meshState.clusterMatrices); + data.computeAdjustedLocalBound(meshState.clusterMatrices); + } - if (skinDeformerIndex != hfm::UNDEFINED_KEY) { - const auto& meshState = self->getMeshState(skinDeformerIndex); - bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); + Transform renderTransform = modelTransform; - transaction.updateItem(itemID, [modelTransform, shapeState, meshState, useDualQuaternionSkinning, - invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) { - if (useDualQuaternionSkinning) { - data.updateClusterBuffer(meshState.clusterDualQuaternions); - } else { - data.updateClusterBuffer(meshState.clusterMatrices); + if (useDualQuaternionSkinning) { + if (meshState.clusterDualQuaternions.size() == 1 || meshState.clusterDualQuaternions.size() == 2) { + const auto& dq = meshState.clusterDualQuaternions[0]; + Transform transform(dq.getRotation(), + dq.getScale(), + dq.getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); } + } else { + if (meshState.clusterMatrices.size() == 1 || meshState.clusterMatrices.size() == 2) { + renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); + } + } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - Transform renderTransform = modelTransform; - data.updateTransform(renderTransform); - data.updateTransformAndBound(modelTransform.worldTransform(shapeState._rootFromJointTransform)); - - data.setCauterized(cauterized); - data.updateKey(renderItemKeyGlobalFlags); - data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); - }); - } else { - transaction.updateItem(itemID, [modelTransform, shapeState, invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { - - Transform renderTransform = modelTransform; - renderTransform = modelTransform.worldTransform(shapeState._rootFromJointTransform); - data.updateTransform(renderTransform); - - data.updateKey(renderItemKeyGlobalFlags); - data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, false); - }); - } + data.setCauterized(cauterized); + data.updateKey(renderItemKeyGlobalFlags); + data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); + }); } AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); @@ -290,15 +291,6 @@ void Model::reset() { } } -void Model::updateShapeStatesFromRig() { - for (auto& shape : _shapeStates) { - uint32_t jointId = shape._jointIndex; - if (jointId < (uint32_t) _rig.getJointStateCount()) { - shape._rootFromJointTransform = _rig.getJointTransform(jointId); - } - } -} - bool Model::updateGeometry() { bool needFullUpdate = false; @@ -314,27 +306,14 @@ bool Model::updateGeometry() { assert(_meshStates.empty()); const HFMModel& hfmModel = getHFMModel(); - - const auto& shapes = hfmModel.shapes; - _shapeStates.resize(shapes.size()); - for (uint32_t s = 0; s < (uint32_t) shapes.size(); ++s) { - auto& shapeState = _shapeStates[s]; - shapeState._jointIndex = shapes[s].joint; - shapeState._meshIndex = shapes[s].mesh; - shapeState._meshPartIndex = shapes[s].meshPart; - shapeState._skinDeformerIndex = shapes[s].skinDeformer; - } - updateShapeStatesFromRig(); - - const auto& hfmSkinDeformers = hfmModel.skinDeformers; - for (uint32_t i = 0; i < (uint32_t) hfmSkinDeformers.size(); i++) { - const auto& dynT = hfmSkinDeformers[i]; + int i = 0; + foreach (const HFMMesh& mesh, hfmModel.meshes) { MeshState state; - state.clusterDualQuaternions.resize(dynT.clusters.size()); - state.clusterMatrices.resize(dynT.clusters.size()); + state.clusterDualQuaternions.resize(mesh.clusters.size()); + state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); + i++; } - needFullUpdate = true; emit rigReady(); } @@ -662,8 +641,8 @@ glm::mat4 Model::getWorldToHFMMatrix() const { // TODO: deprecate and remove MeshProxyList Model::getMeshes() const { MeshProxyList result; - const NetworkModel::Pointer& renderGeometry = getNetworkModel(); - const NetworkModel::GeometryMeshes& meshes = renderGeometry->getMeshes(); + const Geometry::Pointer& renderGeometry = getGeometry(); + const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); if (!isLoaded()) { return result; @@ -732,9 +711,9 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe render::Transaction transaction; for (int i = 0; i < (int) _modelMeshRenderItemIDs.size(); i++) { auto itemID = _modelMeshRenderItemIDs[i]; - auto& shape = _shapeStates[i]; + auto shape = _modelMeshRenderItemShapes[i]; // TODO: check to see if .partIndex matches too - if (shape._meshIndex == (uint32_t) meshIndex) { + if (shape.meshIndex == meshIndex) { transaction.updateItem(itemID, [=](ModelMeshPartPayload& data) { data.updateMeshPart(mesh, partIndex); }); @@ -753,7 +732,7 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe for (int partID = 0; partID < numParts; partID++) { HFMMeshPart part; part.triangleIndices = buffer_helpers::bufferToVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); - mesh.parts.push_back(part); + mesh.parts << part; } { foreach (const glm::vec3& vertex, mesh.vertices) { @@ -764,7 +743,7 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); } } - hfmModel.meshes.push_back(mesh); + hfmModel.meshes << mesh; } calculateTriangleSets(hfmModel); } @@ -781,9 +760,9 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { } const HFMModel& hfmModel = getHFMModel(); - uint32_t numberOfMeshes = (uint32_t)hfmModel.meshes.size(); + int numberOfMeshes = hfmModel.meshes.size(); int shapeID = 0; - for (uint32_t i = 0; i < numberOfMeshes; i++) { + for (int i = 0; i < numberOfMeshes; i++) { const HFMMesh& hfmMesh = hfmModel.meshes.at(i); if (auto mesh = hfmMesh._mesh) { result.append(mesh); @@ -791,7 +770,7 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { auto& materialName = _modelMeshMaterialNames[shapeID]; - result.appendMaterial(graphics::MaterialLayer(getNetworkModel()->getShapeMaterial(shapeID), 0), shapeID, materialName); + result.appendMaterial(graphics::MaterialLayer(getGeometry()->getShapeMaterial(shapeID), 0), shapeID, materialName); { std::unique_lock lock(_materialMappingMutex); @@ -814,69 +793,77 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { void Model::calculateTriangleSets(const HFMModel& hfmModel) { PROFILE_RANGE(render, __FUNCTION__); - uint32_t meshInstanceCount = 0; - uint32_t lastMeshForInstanceCount = hfm::UNDEFINED_KEY; - for (const auto& shape : hfmModel.shapes) { - if (shape.mesh != lastMeshForInstanceCount) { - ++meshInstanceCount; - } - lastMeshForInstanceCount = shape.mesh; - } + int numberOfMeshes = hfmModel.meshes.size(); _triangleSetsValid = true; _modelSpaceMeshTriangleSets.clear(); - _modelSpaceMeshTriangleSets.reserve(meshInstanceCount); + _modelSpaceMeshTriangleSets.resize(numberOfMeshes); - uint32_t lastMeshForTriangleBuilding = hfm::UNDEFINED_KEY; - glm::mat4 lastTransformForTriangleBuilding { 0 }; - std::vector transformedPoints; - for (const auto& shape : hfmModel.shapes) { - const uint32_t meshIndex = shape.mesh; - const hfm::Mesh& mesh = hfmModel.meshes.at(meshIndex); - const auto& triangleListMesh = mesh.triangleListMesh; - const glm::vec2 part = triangleListMesh.parts[shape.meshPart]; - glm::mat4 worldFromMeshTransform; - if (shape.joint != hfm::UNDEFINED_KEY) { - // globalTransform includes hfmModel.offset, - // which includes the scaling, rotation, and translation specified by the FST, - // and the scaling from the unit conversion in FBX. - // This can't change at runtime, so we can safely store these in our TriangleSet. - worldFromMeshTransform = hfmModel.joints[shape.joint].globalTransform; - } + for (int i = 0; i < numberOfMeshes; i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); - if (meshIndex != lastMeshForTriangleBuilding || worldFromMeshTransform != lastTransformForTriangleBuilding) { - lastMeshForTriangleBuilding = meshIndex; - lastTransformForTriangleBuilding = worldFromMeshTransform; - _modelSpaceMeshTriangleSets.emplace_back(); - _modelSpaceMeshTriangleSets.back().reserve(mesh.parts.size()); + const int numberOfParts = mesh.parts.size(); + auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; + meshTriangleSets.resize(numberOfParts); - transformedPoints = triangleListMesh.vertices; - if (worldFromMeshTransform != glm::mat4()) { - for (auto& point : transformedPoints) { - point = glm::vec3(worldFromMeshTransform * glm::vec4(point, 1.0f)); + for (int j = 0; j < numberOfParts; j++) { + const HFMMeshPart& part = mesh.parts.at(j); + + auto& partTriangleSet = meshTriangleSets[j]; + + const int INDICES_PER_TRIANGLE = 3; + const int INDICES_PER_QUAD = 4; + const int TRIANGLES_PER_QUAD = 2; + + // tell our triangleSet how many triangles to expect. + int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD; + int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE; + int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; + partTriangleSet.reserve(totalTriangles); + + auto meshTransform = hfmModel.offset * mesh.modelTransform; + + if (part.quadIndices.size() > 0) { + int vIndex = 0; + for (int q = 0; q < numberOfQuads; q++) { + int i0 = part.quadIndices[vIndex++]; + int i1 = part.quadIndices[vIndex++]; + int i2 = part.quadIndices[vIndex++]; + int i3 = part.quadIndices[vIndex++]; + + // track the model space version... these points will be transformed by the FST's offset, + // which includes the scaling, rotation, and translation specified by the FST/FBX, + // this can't change at runtime, so we can safely store these in our TriangleSet + glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f)); + glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f)); + glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); + glm::vec3 v3 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i3], 1.0f)); + + Triangle tri1 = { v0, v1, v3 }; + Triangle tri2 = { v1, v2, v3 }; + partTriangleSet.insert(tri1); + partTriangleSet.insert(tri2); } } - } - auto& meshTriangleSets = _modelSpaceMeshTriangleSets.back(); - meshTriangleSets.emplace_back(); - auto& partTriangleSet = meshTriangleSets.back(); - const static size_t INDICES_PER_TRIANGLE = 3; - const size_t triangleCount = (size_t)(part.y) / INDICES_PER_TRIANGLE; - partTriangleSet.reserve(triangleCount); - const size_t indexStart = (uint32_t)part.x; - const size_t indexEnd = indexStart + (triangleCount * INDICES_PER_TRIANGLE); - for (size_t i = indexStart; i < indexEnd; i += INDICES_PER_TRIANGLE) { - const int i0 = triangleListMesh.indices[i]; - const int i1 = triangleListMesh.indices[i + 1]; - const int i2 = triangleListMesh.indices[i + 2]; + if (part.triangleIndices.size() > 0) { + int vIndex = 0; + for (int t = 0; t < numberOfTris; t++) { + int i0 = part.triangleIndices[vIndex++]; + int i1 = part.triangleIndices[vIndex++]; + int i2 = part.triangleIndices[vIndex++]; - const glm::vec3 v0 = transformedPoints[i0]; - const glm::vec3 v1 = transformedPoints[i1]; - const glm::vec3 v2 = transformedPoints[i2]; + // track the model space version... these points will be transformed by the FST's offset, + // which includes the scaling, rotation, and translation specified by the FST/FBX, + // this can't change at runtime, so we can safely store these in our TriangleSet + glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f)); + glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f)); + glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); - const Triangle tri = { v0, v1, v2 }; - partTriangleSet.insert(tri); + Triangle tri = { v0, v1, v2 }; + partTriangleSet.insert(tri); + } + } } } } @@ -888,7 +875,7 @@ void Model::updateRenderItemsKey(const render::ScenePointer& scene) { } auto renderItemsKey = _renderItemKeyGlobalFlags; render::Transaction transaction; - for(auto itemID: _modelMeshRenderItemIDs) { + for (auto itemID: _modelMeshRenderItemIDs) { transaction.updateItem(itemID, [renderItemsKey](ModelMeshPartPayload& data) { data.updateKey(renderItemsKey); }); @@ -966,8 +953,8 @@ void Model::setCauterized(bool cauterized, const render::ScenePointer& scene) { return; } render::Transaction transaction; - for (auto itemID : _modelMeshRenderItemIDs) { - transaction.updateItem(itemID, [cauterized](ModelMeshPartPayload& data) { + foreach (auto item, _modelMeshRenderItemsMap.keys()) { + transaction.updateItem(item, [cauterized](ModelMeshPartPayload& data) { data.setCauterized(cauterized); }); } @@ -981,7 +968,7 @@ void Model::setCullWithParent(bool cullWithParent) { render::Transaction transaction; auto renderItemsKey = _renderItemKeyGlobalFlags; - for(auto item : _modelMeshRenderItemIDs) { + for (auto item : _modelMeshRenderItemIDs) { transaction.updateItem(item, [cullWithParent, renderItemsKey](ModelMeshPartPayload& data) { data.setCullWithParent(cullWithParent); data.updateKey(renderItemsKey); @@ -991,6 +978,16 @@ void Model::setCullWithParent(bool cullWithParent) { } } +void Model::setRenderWithZones(const QVector& renderWithZones) { + render::Transaction transaction; + for (auto item : _modelMeshRenderItemIDs) { + transaction.updateItem(item, [renderWithZones](ModelMeshPartPayload& data) { + data.setRenderWithZones(renderWithZones); + }); + } + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} + const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { return _renderItemKeyGlobalFlags; } @@ -1012,25 +1009,26 @@ bool Model::addToScene(const render::ScenePointer& scene, bool somethingAdded = false; - if (_modelMeshRenderItemIDs.empty()) { + if (_modelMeshRenderItemsMap.empty()) { bool hasTransparent = false; size_t verticesCount = 0; foreach(auto renderItem, _modelMeshRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); - if (_modelMeshRenderItemIDs.empty() && statusGetters.size()) { + if (_modelMeshRenderItemsMap.empty() && statusGetters.size()) { renderPayload->addStatusGetters(statusGetters); } transaction.resetItem(item, renderPayload); hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); verticesCount += renderItem.get()->getVerticesCount(); + _modelMeshRenderItemsMap.insert(item, renderPayload); _modelMeshRenderItemIDs.emplace_back(item); } - somethingAdded = !_modelMeshRenderItemIDs.empty(); + somethingAdded = !_modelMeshRenderItemsMap.empty(); _renderInfoVertexCount = verticesCount; - _renderInfoDrawCalls = (uint32_t) _modelMeshRenderItemIDs.size(); + _renderInfoDrawCalls = _modelMeshRenderItemsMap.count(); _renderInfoHasTransparent = hasTransparent; } @@ -1045,12 +1043,14 @@ bool Model::addToScene(const render::ScenePointer& scene, } void Model::removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction) { - for (auto itemID: _modelMeshRenderItemIDs) { - transaction.removeItem(itemID); + foreach (auto item, _modelMeshRenderItemsMap.keys()) { + transaction.removeItem(item); } _modelMeshRenderItemIDs.clear(); + _modelMeshRenderItemsMap.clear(); _modelMeshRenderItems.clear(); _modelMeshMaterialNames.clear(); + _modelMeshRenderItemShapes.clear(); _priorityMap.clear(); _addedToScene = false; @@ -1229,7 +1229,7 @@ void Model::setURL(const QUrl& url) { invalidCalculatedMeshBoxes(); deleteGeometry(); - auto resource = DependencyManager::get()->getModelResource(url); + auto resource = DependencyManager::get()->getGeometryResource(url); if (resource) { resource->setLoadPriority(this, _loadingPriority); _renderWatcher.setResource(resource); @@ -1418,29 +1418,33 @@ void Model::updateClusterMatrices() { return; } - updateShapeStatesFromRig(); - _needsUpdateClusterMatrices = false; - - for (int skinDeformerIndex = 0; skinDeformerIndex < (int)_meshStates.size(); skinDeformerIndex++) { - MeshState& state = _meshStates[skinDeformerIndex]; - auto numClusters = state.getNumClusters(); - for (uint32_t clusterIndex = 0; clusterIndex < numClusters; clusterIndex++) { - const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(skinDeformerIndex, clusterIndex); + const HFMModel& hfmModel = getHFMModel(); + for (int i = 0; i < (int) _meshStates.size(); i++) { + MeshState& state = _meshStates[i]; + int meshIndex = i; + const HFMMesh& mesh = hfmModel.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; if (_useDualQuaternionSkinning) { - auto jointPose = _rig.getJointPose(cbmov.jointIndex); + auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cbmov.inverseBindTransform); - state.clusterDualQuaternions[clusterIndex] = Model::TransformDualQuaternion(clusterTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); } else { - auto jointMatrix = _rig.getJointTransform(cbmov.jointIndex); - glm_mat4u_mul(jointMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[clusterIndex]); + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } + updateBlendshapes(); +} + +void Model::updateBlendshapes() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); if (modelBlender->shouldComputeBlendshapes() && getHFMModel().hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { @@ -1451,7 +1455,6 @@ void Model::updateClusterMatrices() { void Model::deleteGeometry() { _deleteGeometryCounter++; - _shapeStates.clear(); _meshStates.clear(); _rig.destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); @@ -1485,12 +1488,20 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { void Model::createRenderItemSet() { assert(isLoaded()); + const auto& meshes = _renderGeometry->getMeshes(); + + // all of our mesh vectors must match in size + if (meshes.size() != _meshStates.size()) { + qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! " << meshes.size() << _meshStates.size() << " We will not segregate mesh groups yet."; + return; + } // We should not have any existing renderItems if we enter this section of code Q_ASSERT(_modelMeshRenderItems.isEmpty()); _modelMeshRenderItems.clear(); _modelMeshMaterialNames.clear(); + _modelMeshRenderItemShapes.clear(); Transform transform; transform.setTranslation(_translation); @@ -1501,19 +1512,28 @@ void Model::createRenderItemSet() { offset.postTranslate(_offset); // Run through all of the meshes, and place them into their segregated, but unsorted buckets - const auto& shapes = _renderGeometry->getHFMModel().shapes; - for (uint32_t shapeID = 0; shapeID < shapes.size(); shapeID++) { - const auto& shape = shapes[shapeID]; + int shapeID = 0; + uint32_t numMeshes = (uint32_t)meshes.size(); + for (uint32_t i = 0; i < numMeshes; i++) { + const auto& mesh = meshes.at(i); + if (!mesh) { + continue; + } - _modelMeshRenderItems << std::make_shared(shared_from_this(), shape.mesh, shape.meshPart, shapeID, transform, offset, _created); - - auto material = getNetworkModel()->getShapeMaterial(shapeID); - _modelMeshMaterialNames.push_back(material ? material->getName() : ""); + // Create the render payloads + int numParts = (int)mesh->getNumParts(); + for (int partIndex = 0; partIndex < numParts; partIndex++) { + _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset, _created); + auto material = getGeometry()->getShapeMaterial(shapeID); + _modelMeshMaterialNames.push_back(material ? material->getName() : ""); + _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); + shapeID++; + } } } bool Model::isRenderable() const { - return (!_shapeStates.empty()) || (isLoaded() && _renderGeometry->getMeshes().empty()); + return !_meshStates.empty() || (isLoaded() && _renderGeometry->getMeshes().empty()); } std::set Model::getMeshIDsFromMaterialID(QString parentMaterialName) { @@ -1569,11 +1589,11 @@ void Model::applyMaterialMapping() { PrimitiveMode primitiveMode = getPrimitiveMode(); bool useDualQuaternionSkinning = _useDualQuaternionSkinning; auto modelMeshRenderItemIDs = _modelMeshRenderItemIDs; - auto shapeStates = _shapeStates; + auto modelMeshRenderItemShapes = _modelMeshRenderItemShapes; std::unordered_map shouldInvalidatePayloadShapeKeyMap; - for (auto& shape : _shapeStates) { - shouldInvalidatePayloadShapeKeyMap[shape._meshIndex] = shouldInvalidatePayloadShapeKey(shape._meshIndex); + for (auto& shape : _modelMeshRenderItemShapes) { + shouldInvalidatePayloadShapeKeyMap[shape.meshIndex] = shouldInvalidatePayloadShapeKey(shape.meshIndex); } auto& materialMapping = getMaterialMapping(); @@ -1596,7 +1616,7 @@ void Model::applyMaterialMapping() { std::weak_ptr weakSelf = shared_from_this(); auto materialLoaded = [networkMaterialResource, shapeIDs, priorityMapPerResource, renderItemsKey, primitiveMode, useDualQuaternionSkinning, - modelMeshRenderItemIDs, shapeStates, shouldInvalidatePayloadShapeKeyMap, weakSelf]() { + modelMeshRenderItemIDs, modelMeshRenderItemShapes, shouldInvalidatePayloadShapeKeyMap, weakSelf]() { std::shared_ptr self = weakSelf.lock(); if (!self || networkMaterialResource->isFailed() || networkMaterialResource->parsedMaterials.names.size() == 0) { return; @@ -1622,7 +1642,7 @@ void Model::applyMaterialMapping() { for (auto shapeID : shapeIDs) { if (shapeID < modelMeshRenderItemIDs.size()) { auto itemID = modelMeshRenderItemIDs[shapeID]; - auto meshIndex = shapeStates[shapeID]._meshIndex; + auto meshIndex = modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKeyMap.at(meshIndex); graphics::MaterialLayer material = graphics::MaterialLayer(networkMaterial, priorityMapPerResource.at(shapeID)); { @@ -1660,7 +1680,7 @@ void Model::addMaterial(graphics::MaterialLayer material, const std::string& par for (auto shapeID : shapeIDs) { if (shapeID < _modelMeshRenderItemIDs.size()) { auto itemID = _modelMeshRenderItemIDs[shapeID]; - auto meshIndex = _shapeStates[shapeID]._meshIndex; + auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); transaction.updateItem(itemID, [material, renderItemsKey, invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning](ModelMeshPartPayload& data) { @@ -1682,7 +1702,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string auto itemID = _modelMeshRenderItemIDs[shapeID]; auto renderItemsKey = _renderItemKeyGlobalFlags; PrimitiveMode primitiveMode = getPrimitiveMode(); - auto meshIndex = _shapeStates[shapeID]._meshIndex; + auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex; bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = _useDualQuaternionSkinning; transaction.updateItem(itemID, [material, renderItemsKey, @@ -1697,13 +1717,14 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); } -class CollisionRenderGeometry : public NetworkModel { +class CollisionRenderGeometry : public Geometry { public: CollisionRenderGeometry(graphics::MeshPointer mesh) { _hfmModel = std::make_shared(); std::shared_ptr meshes = std::make_shared(); meshes->push_back(mesh); _meshes = meshes; + _meshParts = std::shared_ptr(); } }; @@ -1854,7 +1875,7 @@ void Blender::run() { bool Model::maybeStartBlender() { if (isLoaded()) { - QThreadPool::globalInstance()->start(new Blender(getThisPointer(), getNetworkModel()->getConstHFMModelPointer(), + QThreadPool::globalInstance()->start(new Blender(getThisPointer(), getGeometry()->getConstHFMModelPointer(), ++_blendNumber, _blendshapeCoefficients)); return true; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 864de79c67..55b09bcce2 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -76,17 +76,6 @@ struct SortedTriangleSet { int subMeshIndex; }; -struct BlendshapeOffsetPacked { - glm::uvec4 packedPosNorTan; -}; - -struct BlendshapeOffsetUnpacked { - glm::vec3 positionOffset; - glm::vec3 normalOffset; - glm::vec3 tangentOffset; -}; - -using BlendshapeOffset = BlendshapeOffsetPacked; using BlendShapeOperator = std::function&, const QVector&, const render::ItemIDs&)>; /// A generic 3D model displaying geometry loaded from a URL. @@ -134,6 +123,8 @@ public: void setCullWithParent(bool value); + void setRenderWithZones(const QVector& renderWithZones); + // Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model. const render::ItemKey getRenderItemKeyGlobalFlags() const; @@ -178,9 +169,10 @@ public: virtual void simulate(float deltaTime, bool fullUpdate = true); virtual void updateClusterMatrices(); + virtual void updateBlendshapes(); /// Returns a reference to the shared geometry. - const NetworkModel::Pointer& getNetworkModel() const { return _renderGeometry; } + const Geometry::Pointer& getGeometry() const { return _renderGeometry; } const QVariantMap getTextures() const { assert(isLoaded()); return _renderGeometry->getTextures(); } Q_INVOKABLE virtual void setTextures(const QVariantMap& textures); @@ -299,16 +291,6 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } - class ShapeState { - public: - glm::mat4 _rootFromJointTransform; - uint32_t _jointIndex{ hfm::UNDEFINED_KEY }; - uint32_t _meshIndex{ hfm::UNDEFINED_KEY }; - uint32_t _meshPartIndex{ hfm::UNDEFINED_KEY }; - uint32_t _skinDeformerIndex{ hfm::UNDEFINED_KEY }; - }; - const ShapeState& getShapeState(int index) { return _shapeStates.at(index); } - class TransformDualQuaternion { public: TransformDualQuaternion() {} @@ -351,13 +333,12 @@ public: public: std::vector clusterDualQuaternions; std::vector clusterMatrices; - - uint32_t getNumClusters() const { return (uint32_t) std::max(clusterMatrices.size(), clusterMatrices.size()); } }; + const MeshState& getMeshState(int index) { return _meshStates.at(index); } uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } - + const QMap& getRenderItems() const { return _modelMeshRenderItemsMap; } BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; } void renderDebugMeshBoxes(gpu::Batch& batch, bool forward); @@ -376,6 +357,8 @@ public: void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); + void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } + public slots: void loadURLFinished(bool success); @@ -393,7 +376,6 @@ protected: std::mutex _materialMappingMutex; void applyMaterialMapping(); - void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } /// Clear the joint states @@ -404,9 +386,9 @@ protected: /// \return true if joint exists bool getJointPosition(int jointIndex, glm::vec3& position) const; - NetworkModel::Pointer _renderGeometry; // only ever set by its watcher + Geometry::Pointer _renderGeometry; // only ever set by its watcher - ModelResourceWatcher _renderWatcher; + GeometryResourceWatcher _renderWatcher; SpatiallyNestable* _spatiallyNestableOverride; @@ -432,10 +414,6 @@ protected: bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to - - std::vector _shapeStates; - void updateShapeStatesFromRig(); - std::vector _meshStates; virtual void initJointStates(); @@ -480,7 +458,10 @@ protected: static AbstractViewStateInterface* _viewState; QVector> _modelMeshRenderItems; + QMap _modelMeshRenderItemsMap; render::ItemIDs _modelMeshRenderItemIDs; + using ShapeInfo = struct { int meshIndex; }; + std::vector _modelMeshRenderItemShapes; std::vector _modelMeshMaterialNames; bool _addedToScene { false }; // has been added to scene @@ -532,7 +513,7 @@ private: }; Q_DECLARE_METATYPE(ModelPointer) -Q_DECLARE_METATYPE(NetworkModel::WeakPointer) +Q_DECLARE_METATYPE(Geometry::WeakPointer) Q_DECLARE_METATYPE(BlendshapeOffset) /// Handle management of pending models that need blending diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 7a58498e50..24d6081743 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -41,37 +41,33 @@ void SoftAttachmentModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; + const HFMModel& hfmModel = getHFMModel(); - for (int skinDeformerIndex = 0; skinDeformerIndex < (int)_meshStates.size(); skinDeformerIndex++) { - MeshState& state = _meshStates[skinDeformerIndex]; - auto numClusters = state.getNumClusters(); - for (uint32_t clusterIndex = 0; clusterIndex < numClusters; clusterIndex++) { - const auto& cbmov = _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(skinDeformerIndex, clusterIndex); + for (int i = 0; i < (int) _meshStates.size(); i++) { + MeshState& state = _meshStates[i]; + const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; + for (int j = 0; j < mesh.clusters.size(); j++) { + const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; // TODO: cache these look-ups as an optimization - int jointIndexOverride = getJointIndexOverride(cbmov.jointIndex); - auto rig = &_rigOverride; + int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); + glm::mat4 jointMatrix; if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - rig = &_rig; - } - - if (_useDualQuaternionSkinning) { - auto jointPose = rig->getJointPose(cbmov.jointIndex); - Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); - Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cbmov.inverseBindTransform); - state.clusterDualQuaternions[clusterIndex] = Model::TransformDualQuaternion(clusterTransform); + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); } else { - auto jointMatrix = rig->getJointTransform(cbmov.jointIndex); - glm_mat4u_mul(jointMatrix, cbmov.inverseBindMatrix, state.clusterMatrices[clusterIndex]); + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } + if (_useDualQuaternionSkinning) { + glm::mat4 m; + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, m); + state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); + } else { + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } - // post the blender if we're not currently waiting for one to finish - auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && getHFMModel().hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { - _blendedBlendshapeCoefficients = _blendshapeCoefficients; - modelBlender->noteRequiresBlend(getThisPointer()); - } + updateBlendshapes(); } diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index a6cc82e335..98abc29d8c 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -13,6 +13,7 @@ <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> +<@include CullFace.slh@> <@if not HIFI_USE_SHADOW@> <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> @@ -129,7 +130,7 @@ void main(void) { _fragColor0 = vec4(albedo * isUnlitEnabled(), opacity); <@else@> packDeferredFragmentUnlit( - normalize(_normalWS), + evalFrontOrBackFaceNormal(normalize(_normalWS)), opacity, albedo * isUnlitEnabled()); <@endif@> @@ -195,7 +196,7 @@ void main(void) { <@else@> vec3 fragNormalWS = _normalWS; <@endif@> - fragNormalWS = normalize(fragNormalWS); + fragNormalWS = evalFrontOrBackFaceNormal(normalize(fragNormalWS)); <@if HIFI_USE_FORWARD@> TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 9760216682..fabe85cb4f 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -13,6 +13,7 @@ <@include gpu/Color.slh@> <@include DefaultMaterials.slh@> <@include render-utils/ShaderConstants.h@> +<@include CullFace.slh@> <@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> <@if not HIFI_USE_UNLIT@> @@ -94,7 +95,7 @@ void main(void) { 1.0, DEFAULT_OCCLUSION, fragPosition, - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), texel.rgb, fresnel, metallic, @@ -111,7 +112,7 @@ void main(void) { 1.0, DEFAULT_OCCLUSION, fragPosition, - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), texel.rgb, fresnel, metallic, @@ -119,7 +120,7 @@ void main(void) { texel.a); <@else@> packDeferredFragment( - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), 1.0, texel.rgb, DEFAULT_ROUGHNESS, @@ -141,7 +142,7 @@ void main(void) { , texel.a); <@else@> packDeferredFragmentUnlit( - normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0), + evalFrontOrBackFaceNormal(normalize(_normalWS)), 1.0, texel.rgb <@if HIFI_USE_FADE@> diff --git a/libraries/render-utils/src/simple_procedural.slf b/libraries/render-utils/src/simple_procedural.slf index 5b0eb62cca..cc8edbb415 100644 --- a/libraries/render-utils/src/simple_procedural.slf +++ b/libraries/render-utils/src/simple_procedural.slf @@ -24,6 +24,7 @@ <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> +<@include CullFace.slh@> <@include render-utils/ShaderConstants.h@> layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _positionMS; @@ -66,7 +67,7 @@ float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition pro #line 2030 void main(void) { - vec3 normal = normalize(_normalWS.xyz) * (2.0 * float(gl_FrontFacing) - 1.0); + vec3 normal = evalFrontOrBackFaceNormal(normalize(_normalWS.xyz)); vec3 diffuse = _color.rgb; vec3 fresnel = DEFAULT_FRESNEL; float roughness = DEFAULT_ROUGHNESS; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index a75192bad7..5fee00b370 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -13,15 +13,16 @@ #include #include #include +#include #include #include #include +#include #include #include "Forward.h" - class AABox; namespace render { @@ -142,13 +143,6 @@ namespace render { bool _takingSnapshot { false }; StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; std::function _stencilMaskOperator; - - float _visionSqueezeX { 0.0f }; - float _visionSqueezeY { 0.0f }; - float _visionSqueezeTransition { 0.15f }; - int _visionSqueezePerEye { 0 }; - float _visionSqueezeGroundPlaneY { 0.0f }; - float _visionSqueezeSpotlightSize { 0.02f }; }; } diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 8cedbc7f3e..164d8e9f21 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -19,6 +19,9 @@ using namespace render; +std::unordered_set CullTest::_containingZones = std::unordered_set(); +std::unordered_set CullTest::_prevContainingZones = std::unordered_set(); + CullTest::CullTest(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum) : _functor(functor), _args(pargs), @@ -64,46 +67,8 @@ bool CullTest::solidAngleTest(const AABox& bound) { return true; } -void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemBounds& inItems, ItemBounds& outItems) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - - RenderArgs* args = renderContext->args; - const ViewFrustum& frustum = args->getViewFrustum(); - - details._considered += (int)inItems.size(); - - // Culling / LOD - for (auto item : inItems) { - if (item.bound.isNull()) { - outItems.emplace_back(item); // One more Item to render - continue; - } - - // TODO: some entity types (like lights) might want to be rendered even - // when they are outside of the view frustum... - bool inView; - { - PerformanceTimer perfTimer("boxIntersectsFrustum"); - inView = frustum.boxIntersectsFrustum(item.bound); - } - if (inView) { - bool bigEnoughToRender; - { - PerformanceTimer perfTimer("shouldRender"); - bigEnoughToRender = cullFunctor(args, item.bound); - } - if (bigEnoughToRender) { - outItems.emplace_back(item); // One more Item to render - } else { - details._tooSmall++; - } - } else { - details._outOfView++; - } - } - details._rendered += (int)outItems.size(); +bool CullTest::zoneOcclusionTest(const render::Item& item) { + return item.passesZoneOcclusionTest(_containingZones); } void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemBounds& outItems) { @@ -117,7 +82,7 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const outItems.reserve(items.size()); for (auto& id : items) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && item.passesZoneOcclusionTest(CullTest::_containingZones)) { outItems.emplace_back(ItemBound(id, item.getBound())); } } @@ -126,7 +91,6 @@ void FetchNonspatialItems::run(const RenderContextPointer& renderContext, const void FetchSpatialTree::configure(const Config& config) { _justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum); _freezeFrustum = config.freezeFrustum; - _lodAngle = config.lodAngle; } void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection) { @@ -223,7 +187,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -238,7 +202,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -253,7 +217,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -268,7 +232,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -285,7 +249,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideFitItems"); for (auto id : inSelection.insideItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { @@ -300,7 +264,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("insideSmallItems"); for (auto id : inSelection.insideSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -317,7 +281,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialFitItems"); for (auto id : inSelection.partialItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound); @@ -334,14 +298,12 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, PerformanceTimer perfTimer("partialSmallItems"); for (auto id : inSelection.partialSubcellItems) { auto& item = scene->getItem(id); - if (filter.test(item.getKey())) { + if (filter.test(item.getKey()) && test.zoneOcclusionTest(item)) { ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { - if (test.solidAngleTest(itemBound.bound)) { - outItems.emplace_back(itemBound); - if (item.getKey().isMetaCullGroup()) { - item.fetchMetaSubItemBounds(outItems, (*scene)); - } + if (test.frustumTest(itemBound.bound) && test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + if (item.getKey().isMetaCullGroup()) { + item.fetchMetaSubItemBounds(outItems, (*scene)); } } } @@ -452,3 +414,13 @@ void ApplyCullFunctorOnItemBounds::run(const RenderContextPointer& renderContext args->popViewFrustum(); } } + +void ClearContainingZones::run(const RenderContextPointer& renderContext) { + // This is a bit of a hack. We want to do zone culling as early as possible, so we do it + // during the RenderFetchCullSortTask (in CullSpatialSelection and FetchNonspatialItems), + // but the zones aren't collected until after (in SetupZones). To get around this, + // we actually use the zones from the previous frame to render, and then clear at the beginning + // of the next frame + CullTest::_prevContainingZones = CullTest::_containingZones; + CullTest::_containingZones.clear(); +} \ No newline at end of file diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index e67edd6666..9a7466223d 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -19,9 +19,6 @@ namespace render { using CullFunctor = std::function; - void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, - const ItemBounds& inItems, ItemBounds& outItems); - // Culling Frustum / solidAngle test helper class struct CullTest { CullFunctor _functor; @@ -36,6 +33,10 @@ namespace render { bool frustumTest(const AABox& bound); bool antiFrustumTest(const AABox& bound); bool solidAngleTest(const AABox& bound); + bool zoneOcclusionTest(const render::Item& item); + + static std::unordered_set _containingZones; + static std::unordered_set _prevContainingZones; }; class FetchNonspatialItems { @@ -48,7 +49,6 @@ namespace render { Q_OBJECT Q_PROPERTY(int numItems READ getNumItems) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) - Q_PROPERTY(float LODAngle MEMBER lodAngle NOTIFY dirty) public: int numItems{ 0 }; @@ -56,7 +56,6 @@ namespace render { bool freezeFrustum{ false }; - float lodAngle{ 2.0 }; public slots: void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); } @@ -68,7 +67,6 @@ namespace render { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; ViewFrustum _frozenFrustum; - float _lodAngle; public: using Config = FetchSpatialTreeConfig; @@ -159,6 +157,12 @@ namespace render { render::CullFunctor _cullFunctor; }; + + class ClearContainingZones { + public: + using JobModel = Job::Model; + void run(const RenderContextPointer& renderContext); + }; } #endif // hifi_render_CullTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index 532964777f..f21df58368 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -154,4 +154,10 @@ namespace render { return payload->metaFetchMetaSubItems(subItems); } + template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones) { + if (!payload) { + return false; + } + return payload->passesZoneOcclusionTest(containingZones); + } } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 3383101b5b..8a67108d0e 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -28,6 +28,8 @@ #include #include "ShapePipeline.h" +#include "BlendshapeConstants.h" + namespace render { typedef int32_t Index; @@ -436,6 +438,8 @@ public: virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const = 0; + virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + ~PayloadInterface() {} // Status interface is local to the base class @@ -487,6 +491,8 @@ public: uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); } uint32_t fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) const; + bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const { return _payload->passesZoneOcclusionTest(containingZones); } + // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -537,6 +543,10 @@ template const ShapeKey shapeGetShapeKey(const std::shared_ptr& pay // Meta items act as the grouping object for several sub items (typically shapes). template uint32_t metaFetchMetaSubItems(const std::shared_ptr& payloadData, ItemIDs& subItems) { return 0; } +// Zone Occlusion Interface +// Allows payloads to determine if they should render or not, based on the zones that contain the current camera +template bool payloadPassesZoneOcclusionTest(const std::shared_ptr& payloadData, const std::unordered_set& containingZones) { return true; } + // THe Payload class is the real Payload to be used // THis allow anything to be turned into a Payload as long as the required interface functions are available // When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff" @@ -561,6 +571,8 @@ public: // Meta Type Interface virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const override { return metaFetchMetaSubItems(_data, subItems); } + virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const override { return payloadPassesZoneOcclusionTest(_data, containingZones); } + protected: DataPointer _data; @@ -615,6 +627,11 @@ public: virtual Item::Bound getBound() = 0; virtual void render(RenderArgs* args) = 0; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0; + virtual bool passesZoneOcclusionTest(const std::unordered_set& containingZones) const = 0; + + // FIXME: this isn't the best place for this since it's only used for ModelEntities, but currently all Entities use PayloadProxyInterface + virtual void handleBlendedVertices(int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs) {}; }; template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload); @@ -622,7 +639,7 @@ template <> const Item::Bound payloadGetBound(const PayloadProxyInterface::Point template <> void payloadRender(const PayloadProxyInterface::Pointer& payload, RenderArgs* args); template <> uint32_t metaFetchMetaSubItems(const PayloadProxyInterface::Pointer& payload, ItemIDs& subItems); template <> const ShapeKey shapeGetShapeKey(const PayloadProxyInterface::Pointer& payload); - +template <> bool payloadPassesZoneOcclusionTest(const PayloadProxyInterface::Pointer& payload, const std::unordered_set& containingZones); typedef Item::PayloadPointer PayloadPointer; typedef std::vector Payloads; diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index ebcf5a432b..b2656a597f 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -74,6 +74,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin const auto filteredLayeredOpaque = task.addJob("FilterLayeredOpaque", layeredOpaques, ItemKey::Layer::LAYER_1); const auto filteredLayeredTransparent = task.addJob("FilterLayeredTransparent", layeredTransparents, ItemKey::Layer::LAYER_1); + task.addJob("ClearContainingZones"); output = Output(BucketList{ opaques, transparents, lights, metas, filteredLayeredOpaque.getN(0), filteredLayeredTransparent.getN(0), diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 44abe62b24..23bfe56309 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -223,7 +223,7 @@ public slots: /**jsdoc * Gets the "front" direction that the camera would face if its orientation was set to the quaternion value. * This is a synonym for {@link Quat(0).getForward|Quat.getForward}. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getFront * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -233,7 +233,7 @@ public slots: /**jsdoc * Gets the "forward" direction that the camera would face if its orientation was set to the quaternion value. * This is a synonym for {@link Quat(0).getFront|Quat.getFront}. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getForward * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The negative z-axis rotated by orientation. @@ -245,7 +245,7 @@ public slots: /**jsdoc * Gets the "right" direction that the camera would have if its orientation was set to the quaternion value. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getRight * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The x-axis rotated by orientation. @@ -254,7 +254,7 @@ public slots: /**jsdoc * Gets the "up" direction that the camera would have if its orientation was set to the quaternion value. - * The High Fidelity camera has axes x = right, y = up, -z = forward. + * The Vircadia camera has axes x = right, y = up, -z = forward. * @function Quat(0).getUp * @param {Quat} orientation - A quaternion representing an orientation. * @returns {Vec3} The y-axis rotated by orientation. diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 4265390995..15166d572f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -417,7 +417,7 @@ public: /**jsdoc * Provides access to methods or objects provided in an external JavaScript or JSON file. - * See {@link https://docs.projectathena.dev/script/js-tips.html} for further details. + * See {@link https://docs.vircadia.dev/script/js-tips.html} for further details. * @function Script.require * @param {string} module - The module to use. May be a JavaScript file, a JSON file, or the name of a system module such * as "appUi" (i.e., the "appUi.js" system module JavaScript file). @@ -528,7 +528,7 @@ public: Q_INVOKABLE QUrl resourcesPath() const; /**jsdoc - * Starts timing a section of code in order to send usage data about it to High Fidelity. Shouldn't be used outside of the + * Starts timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of the * standard scripts. * @function Script.beginProfileRange * @param {string} label - A name that identifies the section of code. @@ -536,7 +536,7 @@ public: Q_INVOKABLE void beginProfileRange(const QString& label) const; /**jsdoc - * Finishes timing a section of code in order to send usage data about it to High Fidelity. Shouldn't be used outside of + * Finishes timing a section of code in order to send usage data about it to Vircadia. Shouldn't be used outside of * the standard scripts. * @function Script.endProfileRange * @param {string} label - A name that identifies the section of code. diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index a8fe51efe8..a8a8adece3 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -22,9 +22,9 @@ #include "GLMHelpers.h" /**jsdoc - * The Vec3 API provides facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a + * The Vec3 API provides facilities for generating and manipulating 3-dimensional vectors. Vircadia uses a * right-handed Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. - * High Fidelity coordinate system + * Vircadia coordinate system * * @namespace Vec3 * @variation 0 diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index eb3e286843..57904be586 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -13,6 +13,13 @@ if (ANDROID) target_link_libraries(${TARGET_NAME} android) endif() +if (APPLE) + find_library(FRAMEWORK_IOKIT IOKit) + find_library(CORE_FOUNDATION CoreFoundation) + find_library(OpenGL OpenGL) + target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION} ${OpenGL}) +endif() + target_zlib() target_nsight() target_json() diff --git a/libraries/shared/src/BlendshapeConstants.cpp b/libraries/shared/src/BlendshapeConstants.cpp index 172df461fd..83c8271340 100644 --- a/libraries/shared/src/BlendshapeConstants.cpp +++ b/libraries/shared/src/BlendshapeConstants.cpp @@ -76,3 +76,11 @@ const char* FACESHIFT_BLENDSHAPES[] = { "UserBlendshape9", "" }; + +const QMap BLENDSHAPE_LOOKUP_MAP = [] { + QMap toReturn; + for (int i = 0; i < (int)Blendshapes::BlendshapeCount; i++) { + toReturn[FACESHIFT_BLENDSHAPES[i]] = i; + } + return toReturn; +}(); diff --git a/libraries/shared/src/BlendshapeConstants.h b/libraries/shared/src/BlendshapeConstants.h index e74146eb56..33d02de57d 100644 --- a/libraries/shared/src/BlendshapeConstants.h +++ b/libraries/shared/src/BlendshapeConstants.h @@ -12,8 +12,14 @@ #ifndef hifi_BlendshapeConstants_h #define hifi_BlendshapeConstants_h +#include +#include + +#include + /// The names of the blendshapes expected by Faceshift, terminated with an empty string. extern const char* FACESHIFT_BLENDSHAPES[]; +extern const QMap BLENDSHAPE_LOOKUP_MAP; enum class Blendshapes : int { EyeBlink_L = 0, @@ -115,5 +121,16 @@ enum class LegacyBlendshpaes : int { // * LipsUpperOpen (not in ARKit) // * LipsLowerOpen (not in ARKit) +struct BlendshapeOffsetPacked { + glm::uvec4 packedPosNorTan; +}; + +struct BlendshapeOffsetUnpacked { + glm::vec3 positionOffset; + glm::vec3 normalOffset; + glm::vec3 tangentOffset; +}; + +using BlendshapeOffset = BlendshapeOffsetPacked; #endif // hifi_BlendshapeConstants_h diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 5787295da6..cfb4bb6398 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -392,14 +392,4 @@ inline glm::vec4 extractFov( const glm::mat4& m) { return result; } -inline bool operator<(const glm::vec3& lhs, const glm::vec3& rhs) { - return (lhs.x < rhs.x) || ( - (lhs.x == rhs.x) && ( - (lhs.y < rhs.y) || ( - (lhs.y == rhs.y) && (lhs.z < rhs.z) - ) - ) - ); -} - #endif // hifi_GLMHelpers_h diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 4ab6a79e22..142cd7bfbc 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -58,7 +58,7 @@ public: static QString generateTemporaryDir(); static bool deleteMyTemporaryDir(QString dirName); - static int removeTemporaryApplicationDirs(QString appName = QString::null); + static int removeTemporaryApplicationDirs(QString appName = QString()); static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 7faaebbae9..d47cc0769a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -36,6 +36,7 @@ int qVectorVec3MetaTypeId = qRegisterMetaType>(); int qVectorQuatMetaTypeId = qRegisterMetaType>(); int qVectorBoolMetaTypeId = qRegisterMetaType>(); int qVectorGLMUint32MetaTypeId = qRegisterMetaType>("QVector"); +int qVectorQUuidMetaTypeId = qRegisterMetaType>(); int quatMetaTypeId = qRegisterMetaType(); int pickRayMetaTypeId = qRegisterMetaType(); int collisionMetaTypeId = qRegisterMetaType(); @@ -58,6 +59,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue); qScriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue); qScriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue); + qScriptRegisterMetaType(engine, qVectorQUuidToScriptValue, qVectorQUuidFromScriptValue); qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); @@ -874,6 +876,22 @@ QVector qVectorFloatFromScriptValue(const QScriptValue& array) { return newVector; } +QScriptValue qVectorQUuidToScriptValue(QScriptEngine* engine, const QVector& vector) { + QScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, quuidToScriptValue(engine, vector.at(i))); + } + return array; +} + +void qVectorQUuidFromScriptValue(const QScriptValue& array, QVector& vector) { + int length = array.property("length").toInteger(); + + for (int i = 0; i < length; i++) { + vector << array.property(i).toVariant().toUuid(); + } +} + QVector qVectorQUuidFromScriptValue(const QScriptValue& array) { if (!array.isArray()) { return QVector(); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index a7b7c4edf4..86e06d7f93 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -247,6 +247,8 @@ QVector qVectorFloatFromScriptValue(const QScriptValue& array); QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vector); +QScriptValue qVectorQUuidToScriptValue(QScriptEngine* engine, const QVector& vector); +void qVectorQUuidFromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorQUuidFromScriptValue(const QScriptValue& array); QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index cb9ad41fd0..c60d1c2574 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -189,7 +189,7 @@ uint32_t ShapeInfo::getNumSubShapes() const { return 0; case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_COMPOUND: - return (uint32_t)_pointCollection.size(); + return _pointCollection.size(); case SHAPE_TYPE_MULTISPHERE: case SHAPE_TYPE_SIMPLE_HULL: case SHAPE_TYPE_STATIC_MESH: @@ -200,10 +200,10 @@ uint32_t ShapeInfo::getNumSubShapes() const { } } -uint32_t ShapeInfo::getLargestSubshapePointCount() const { - uint32_t numPoints = 0; - for (uint32_t i = 0; i < (uint32_t)_pointCollection.size(); ++i) { - uint32_t n = (uint32_t)_pointCollection[i].size(); +int ShapeInfo::getLargestSubshapePointCount() const { + int numPoints = 0; + for (int i = 0; i < _pointCollection.size(); ++i) { + int n = _pointCollection[i].size(); if (n > numPoints) { numPoints = n; } diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 676f38d087..6b0f981b24 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -12,7 +12,7 @@ #ifndef hifi_ShapeInfo_h #define hifi_ShapeInfo_h -#include +#include #include #include #include @@ -53,11 +53,11 @@ class ShapeInfo { public: - using PointList = std::vector; - using PointCollection = std::vector; - using TriangleIndices = std::vector; + using PointList = QVector; + using PointCollection = QVector; + using TriangleIndices = QVector; using SphereData = glm::vec4; - using SphereCollection = std::vector; + using SphereCollection = QVector; static QString getNameForShapeType(ShapeType type); static ShapeType getShapeTypeForName(QString string); @@ -85,7 +85,7 @@ public: TriangleIndices& getTriangleIndices() { return _triangleIndices; } const TriangleIndices& getTriangleIndices() const { return _triangleIndices; } - uint32_t getLargestSubshapePointCount() const; + int getLargestSubshapePointCount() const; float computeVolume() const; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 8387270905..c1c1fd38d9 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1144,7 +1144,7 @@ AACube SpatiallyNestable::calculateInitialQueryAACube(bool& success) { } } -bool SpatiallyNestable::updateQueryAACube() { +bool SpatiallyNestable::updateQueryAACube(bool updateParent) { if (!queryAACubeNeedsUpdate()) { return false; } @@ -1171,9 +1171,39 @@ bool SpatiallyNestable::updateQueryAACube() { _queryAACubeSet = true; - auto parent = getParentPointer(success); - if (success && parent) { - parent->updateQueryAACube(); + if (updateParent) { + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + } + + return true; +} + +bool SpatiallyNestable::updateQueryAACubeWithDescendantAACube(const AACube& descendantAACube, bool updateParent) { + if (!queryAACubeNeedsUpdateWithDescendantAACube(descendantAACube)) { + return false; + } + + bool success; + AACube initialQueryAACube = calculateInitialQueryAACube(success); + if (!success) { + return false; + } + _queryAACube = initialQueryAACube; + _queryAACubeIsPuffed = shouldPuffQueryAACube(); + + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + + _queryAACubeSet = true; + + if (updateParent) { + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } } return true; @@ -1216,6 +1246,24 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { return childNeedsUpdate; } +bool SpatiallyNestable::queryAACubeNeedsUpdateWithDescendantAACube(const AACube& descendantAACube) const { + if (!_queryAACubeSet) { + return true; + } + + bool success; + AACube maxAACube = getMaximumAACube(success); + if (success && !_queryAACube.contains(maxAACube)) { + return true; + } + + if (shouldPuffQueryAACube() != _queryAACubeIsPuffed) { + return true; + } + + return !_queryAACube.contains(descendantAACube); +} + AACube SpatiallyNestable::getQueryAACube(bool& success) const { if (_queryAACubeSet) { success = true; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index f52dc4bf8b..01e3b045ad 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -117,8 +117,10 @@ public: virtual void setQueryAACube(const AACube& queryAACube); virtual bool queryAACubeNeedsUpdate() const; + virtual bool queryAACubeNeedsUpdateWithDescendantAACube(const AACube& descendantAACube) const; virtual bool shouldPuffQueryAACube() const { return false; } - bool updateQueryAACube(); + bool updateQueryAACube(bool updateParent = true); + bool updateQueryAACubeWithDescendantAACube(const AACube& descendentAACube, bool updateParent = true); void forceQueryAACubeUpdate() { _queryAACubeSet = false; } virtual AACube getQueryAACube(bool& success) const; virtual AACube getQueryAACube() const; diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp index 2ccc247af3..9fc72bd000 100644 --- a/libraries/shared/src/shared/FileLogger.cpp +++ b/libraries/shared/src/shared/FileLogger.cpp @@ -38,12 +38,12 @@ private: QMutex _fileMutex; }; -static const QString FILENAME_FORMAT = "hifi-log_%1%2.txt"; +static const QString FILENAME_FORMAT = "vircadia-log_%1%2.txt"; static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; static const QString LOGS_DIRECTORY = "Logs"; static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[01][0-9]-[0-3][0-9]_[0-2][0-9]\\.[0-6][0-9]\\.[0-6][0-9]"; static const QString SESSION_WILDCARD = "[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}"; -static QRegExp LOG_FILENAME_REGEX { "hifi-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\\.txt" }; +static QRegExp LOG_FILENAME_REGEX { "vircadia-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\\.txt" }; static QUuid SESSION_ID; // Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively @@ -68,7 +68,7 @@ QString getLogRollerFilename() { } const QString& getLogFilename() { - static QString fileName = FileUtils::standardPath(LOGS_DIRECTORY) + "hifi-log.txt"; + static QString fileName = FileUtils::standardPath(LOGS_DIRECTORY) + "vircadia-log.txt"; return fileName; } diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 6a2cecf8b9..f64f2758c3 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index f8e4189d12..5468ee18c3 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -103,9 +103,9 @@ public: * @function Tablet.getTablet * @param {string} name - A unique name that identifies the tablet. * @returns {TabletProxy} The tablet instance. - * @example + * @example * var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - * tablet.gotoWebScreen("https://projectathena.io/"); + * tablet.gotoWebScreen("https://vircadia.com/"); */ Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId); diff --git a/pkg-scripts/Dockerfile.templ b/pkg-scripts/Dockerfile.templ index 76d27e8c78..e340024bce 100644 --- a/pkg-scripts/Dockerfile.templ +++ b/pkg-scripts/Dockerfile.templ @@ -19,25 +19,25 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ apt-get install -y tzdata supervisor ${DEPENDS} && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ - mkdir -p /var/lib/athena -RUN groupadd -r athena ; \ - useradd -Nr athena -d /var/lib/athena ; \ - usermod -aG athena athena ; \ - chown athena.athena /var/lib/athena ; \ + mkdir -p /var/lib/vircadia +RUN groupadd -r vircadia ; \ + useradd -Nr vircadia -d /var/lib/vircadia ; \ + usermod -aG vircadia vircadia ; \ + chown vircadia.vircadia /var/lib/vircadia ; \ exit 0 -VOLUME /var/lib/athena +VOLUME /var/lib/vircadia RUN mkdir -p /var/run ; chmod 777 /var/run -COPY athena.conf /etc/supervisor/conf.d/athena.conf +COPY vircadia.conf /etc/supervisor/conf.d/vircadia.conf COPY entrypoint.sh / -COPY opt /opt/athena -COPY lib /opt/athena/lib +COPY opt /opt/vircadia +COPY lib /opt/vircadia/lib ENTRYPOINT ["/entrypoint.sh"] -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/athena.conf"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/vircadia.conf"] LABEL \ - net.projectathena.gitsrc="${GITSRC}" \ - net.projectathena.gitdate="${GITDATE}" \ - net.projectathena.gitcommit="${GITCOMMIT}" + net.vircadia.gitsrc="${GITSRC}" \ + net.vircadia.gitdate="${GITDATE}" \ + net.vircadia.gitcommit="${GITCOMMIT}" diff --git a/pkg-scripts/README b/pkg-scripts/README index f4ef24eed4..92a9bf00d3 100644 --- a/pkg-scripts/README +++ b/pkg-scripts/README @@ -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/daleglass/athena-builder, specifically that +use of the build script at https://github.com/kasenvr/vircadia-builder, specifically that the following directory structure exists base folder/ @@ -9,7 +9,7 @@ base folder/ These scripts assume that the current directory is the pkg-scripts folder inside of the source directory and that the base folder can be reached by going to "../..". This may not work if pkg-scripts is a symlink; -adding an ATHENA=~/Athena to the beginning of the commandline will override where it looks for the base folder +adding an VIRCADIA=~/Vircadia to the beginning of the commandline will override where it looks for the base folder Ubuntu: DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server @@ -30,24 +30,24 @@ Docker: Results: The following directory structure is created for binaries: - /opt/athena - executables - /opt/athena/lib - private shared libraries required for executables - /opt/athena/resources - files required by domain-server administrative website - /opt/athena/plugins - files required by assignment-client, mainly for audio codecs + /opt/vircadia - executables + /opt/vircadia/lib - private shared libraries required for executables + /opt/vircadia/resources - files required by domain-server administrative website + /opt/vircadia/plugins - files required by assignment-client, mainly for audio codecs The following systemd services are installed in /usr/lib/systemd/system: - athena-assignment-client.service - athena-domain-server.service - athena-server.target - used to launch/shutdown the two prior services - athena-assignment-client@.service - athena-domain-server@.service - athena-server@.target - used to launch/shutdown the two prior services + vircadia-assignment-client.service + vircadia-domain-server.service + vircadia-server.target - used to launch/shutdown the two prior services + vircadia-assignment-client@.service + vircadia-domain-server@.service + vircadia-server@.target - used to launch/shutdown the two prior services - The top three services in this list are the "normal" services that launch Athena + The top three services in this list are the "normal" services that launch Vircadia in the typical fashion. The bottom three services are "template" services designed to permit multiple services to be installed and running on a single machine. - The script "/opt/athena/new-server serverName basePort" will do the necessary + The script "/opt/vircadia/new-server serverName basePort" will do the necessary setup for a new domain with the specified server name and port. Upon installation the package will create and launch a domain named "default" at base port 40100. The domain name here has nothing to do with the name people will use to find your @@ -55,6 +55,6 @@ Results: used to configure and run the domain on your server. The server stores its files in the following locations: - /var/lib/athena/.local - "unnamed" services (the default location for Athena servers) - /var/lib/athena/serverName - "named" (template) domains - /etc/opt/athena - environment variables when launching named domains + /var/lib/vircadia/.local - "unnamed" services (the default location for Vircadia servers) + /var/lib/vircadia/serverName - "named" (template) domains + /etc/opt/vircadia - environment variables when launching named domains diff --git a/pkg-scripts/athena-assignment-client.service b/pkg-scripts/athena-assignment-client.service deleted file mode 100644 index a6407294dc..0000000000 --- a/pkg-scripts/athena-assignment-client.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Assignment client service for Athena server -After=network.target -PartOf=athena-server.target - -[Service] -Restart=always - -WorkingDirectory=/opt/athena -Environment="LD_LIBRARY_PATH=/opt/athena/lib" -User=athena -Group=athena -#LimitCORE=infinity -#ExecStart=/opt/athena/assignment-client -n 6 -ExecStart=/opt/athena/assignment-client --min 6 --max 20 - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-assignment-client@.service b/pkg-scripts/athena-assignment-client@.service deleted file mode 100644 index 7f60ed4d28..0000000000 --- a/pkg-scripts/athena-assignment-client@.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=Assignment client service for Athena server -After=network.target -PartOf=athena-server@%i.target - -[Service] -Restart=always - -WorkingDirectory=/opt/athena -EnvironmentFile=/etc/opt/athena/%i.conf -Environment="LD_LIBRARY_PATH=/opt/athena/lib" "HOME=/var/lib/athena/%i" -PrivateTmp=true -User=athena -Group=athena -#LimitCORE=infinity -#ExecStart=/opt/athena/assignment-client -n 6 -ExecStart=/opt/athena/assignment-client --min 6 --max 20 --server-port $HIFI_DOMAIN_SERVER_PORT - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-domain-server.service b/pkg-scripts/athena-domain-server.service deleted file mode 100644 index a5db9dc269..0000000000 --- a/pkg-scripts/athena-domain-server.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Domain Server service for Athena -After=network.target -PartOf=athena-server.target - -[Service] -Restart=on-failure - -WorkingDirectory=/opt/athena -Environment="LD_LIBRARY_PATH=/opt/athena/lib" -User=athena -Group=athena -#LimitCORE=infinity -#ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l domain-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/domain-server/domain-server; fi' -ExecStart=/opt/athena/domain-server - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-domain-server@.service b/pkg-scripts/athena-domain-server@.service deleted file mode 100644 index 330d8ac81d..0000000000 --- a/pkg-scripts/athena-domain-server@.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=Domain Server service for Athena -After=network.target -PartOf=athena-server@%i.target - -[Service] -Restart=on-failure - -WorkingDirectory=/opt/athena -EnvironmentFile=/etc/opt/athena/%i.conf -Environment="LD_LIBRARY_PATH=/opt/athena/lib" "HOME=/var/lib/athena/%i" -PrivateTmp=true -User=athena -Group=athena -#LimitCORE=infinity -#ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l domain-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/domain-server/domain-server; fi' -ExecStart=/opt/athena/domain-server - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-server.spec b/pkg-scripts/athena-server.spec deleted file mode 100644 index 7910c8114b..0000000000 --- a/pkg-scripts/athena-server.spec +++ /dev/null @@ -1,128 +0,0 @@ -#ATHENA=~/Athena rpmbuild --target x86_64 -bb athena-server.spec -%define version %{lua:print(os.getenv("VERSION"))} -%define depends %{lua:print(os.getenv("DEPENDS"))} - -Name: athena-server -Version: %{version} -Release: 1%{?dist} -Summary: Project Athena metaverse platform, based on the High Fidelity Engine. - -License: ASL 2.0 -URL: https://projectathena.io -Source0: https://github.com/daleglass/athena-builder/blob/master/athena_builder - -#BuildRequires: systemd-rpm-macros -BuildRequires: chrpath -Requires: %{depends} -BuildArch: x86_64 -AutoReq: no -AutoProv: no - -%description -Project Athena allows creation and sharing of VR experiences. - The Project Athena metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. - - -%prep - - -%build - - -%install -rm -rf $RPM_BUILD_ROOT -install -d $RPM_BUILD_ROOT/opt/athena -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/assignment-client/assignment-client -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/domain-server/domain-server -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/tools/oven/oven -#install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/build/ice-server/ice-server -strip --strip-all $RPM_BUILD_ROOT/opt/athena/* -chrpath -d $RPM_BUILD_ROOT/opt/athena/* -install -m 0755 -t $RPM_BUILD_ROOT/opt/athena $ATHENA/source/pkg-scripts/new-server -install -d $RPM_BUILD_ROOT/opt/athena/lib -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/build/libraries/*/*.so -strip --strip-all $RPM_BUILD_ROOT/opt/athena/lib/* -chrpath -d $RPM_BUILD_ROOT/opt/athena/lib/* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* -install -m 0644 -t $RPM_BUILD_ROOT/opt/athena/lib $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* -install -d $RPM_BUILD_ROOT/usr/lib/systemd/system -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-assignment-client.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-assignment-client@.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-domain-server.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-domain-server@.service -#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-ice-server.service -#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-ice-server@.service -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-server.target -install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $ATHENA/source/pkg-scripts/athena-server@.target -cp -a $ATHENA/source/domain-server/resources $RPM_BUILD_ROOT/opt/athena -cp -a $ATHENA/build/assignment-client/plugins $RPM_BUILD_ROOT/opt/athena -chrpath -d $RPM_BUILD_ROOT/opt/athena/plugins/*.so -chrpath -d $RPM_BUILD_ROOT/opt/athena/plugins/*/*.so -strip --strip-all $RPM_BUILD_ROOT/opt/athena/plugins/*.so -strip --strip-all $RPM_BUILD_ROOT/opt/athena/plugins/*/*.so -find $RPM_BUILD_ROOT/opt/athena/resources -name ".gitignore" -delete - - -%files -%license $ATHENA/source/LICENSE -/opt/athena -/usr/lib/systemd/system - - -%changelog - - -%post -# create users -getent passwd athena >/dev/numm 2>&1 || useradd -r -c "Project Athena" -d /var/lib/athena -U -M athena -#getent group athena >/dev/null 2>&1 || groupadd -r athena - -# create data folder -mkdir -p /etc/opt/athena -mkdir -p /var/lib/athena && chown athena:athena /var/lib/athena && chmod 775 /var/lib/athena - -ldconfig -n /opt/athena/lib -if [ ! -d "/var/lib/athena/default" ]; then - /opt/athena/new-server default 40100 - systemctl enable athena-server@default.target - systemctl start athena-server@default.target -fi - -%systemd_post athena-assignment-client.service -%systemd_post athena-assignment-client@.service -%systemd_post athena-domain-server.service -%systemd_post athena-domain-server@.service -#%systemd_post athena-ice-server.service -#%systemd_post athena-ice-server@.service -%systemd_post athena-server.target -%systemd_post athena-server@.target - - -%preun -%systemd_preun athena-server.target -%systemd_preun athena-server@.target -%systemd_preun athena-assignment-client.service -%systemd_preun athena-assignment-client@.service -%systemd_preun athena-domain-server.service -%systemd_preun athena-domain-server@.service -#%systemd_preun athena-ice-server.service -#%systemd_preun athena-ice-server@.service - - -%postun -%systemd_postun_with_restart athena-server.target -%systemd_postun_with_restart athena-server@.target -%systemd_postun_with_restart athena-assignment-client.service -%systemd_postun_with_restart athena-assignment-client@.service -%systemd_postun_with_restart athena-domain-server.service -%systemd_postun_with_restart athena-domain-server@.service -#%systemd_postun_with_restart athena-ice-server.service -#%systemd_postun_with_restart athena-ice-server@.service diff --git a/pkg-scripts/athena-server.target b/pkg-scripts/athena-server.target deleted file mode 100644 index e3929b401b..0000000000 --- a/pkg-scripts/athena-server.target +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Athena virtual domain -Wants=athena-assignment-client.service -Wants=athena-domain-server.service -#Wants=athena-ice-server.service -After=athena-assignment-client.service -After=athena-domain-server.service -#After=athena-ice-server.service - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/athena-server@.target b/pkg-scripts/athena-server@.target deleted file mode 100644 index 8ac451ca80..0000000000 --- a/pkg-scripts/athena-server@.target +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Athena virtual domain -Wants=athena-assignment-client@%i.service -Wants=athena-domain-server@%i.service -#Wants=athena-ice-server@%i.service -After=athena-assignment-client@%i.service -After=athena-domain-server@%i.service -#After=athena-ice-server@%i.service - -[Install] -WantedBy=multi-user.target diff --git a/pkg-scripts/docker-entrypoint.sh b/pkg-scripts/docker-entrypoint.sh index 4a4a7506dc..b519647edb 100755 --- a/pkg-scripts/docker-entrypoint.sh +++ b/pkg-scripts/docker-entrypoint.sh @@ -6,23 +6,23 @@ set -x # allowing the container to be run directly as Jenkins. In Dev, or on unknown # environments, run the container as root to automatically correct docker # group in container to match the docker.sock GID mounted from the host. -if [ -f /var/lib/athena/.local -a "$(id -u)" = "0" ]; then +if [ -f /var/lib/vircadia/.local -a "$(id -u)" = "0" ]; then # realign gid - THIS_ATHENA_GID=`ls -ngd /var/lib/athena/.local | cut -f3 -d' '` - CUR_ATHENA_GID=`getent group athena | cut -f3 -d: || true` - if [ ! -z "$THIS_ATHENA_GID" -a "$THIS_ATHENA_GID" != "$CUR_ATHENA_GID" ]; then - groupmod -g ${THIS_ATHENA_GID} -o athena + THIS_VIRCADIA_GID=`ls -ngd /var/lib/vircadia/.local | cut -f3 -d' '` + CUR_VIRCADIA_GID=`getent group vircadia | cut -f3 -d: || true` + if [ ! -z "$THIS_VIRCADIA_GID" -a "$THIS_VIRCADIA_GID" != "$CUR_VIRCADIA_GID" ]; then + groupmod -g ${THIS_VIRCADIA_GID} -o vircadia fi # realign pid - THIS_ATHENA_PID=`ls -nd /var/lib/athena/.local | cut -f3 -d' '` - CUR_ATHENA_PID=`getent passwd athena | cut -f3 -d: || true` - if [ ! -z "$THIS_ATHENA_PID" -a "$THIS_ATHENA_PID" != "$CUR_ATHENA_PID" ]; then - usermod -u ${THIS_ATHENA_PID} -o athena + THIS_VIRCADIA_PID=`ls -nd /var/lib/vircadia/.local | cut -f3 -d' '` + CUR_VIRCADIA_PID=`getent passwd vircadia | cut -f3 -d: || true` + if [ ! -z "$THIS_VIRCADIA_PID" -a "$THIS_VIRCADIA_PID" != "$CUR_VIRCADIA_PID" ]; then + usermod -u ${THIS_VIRCADIA_PID} -o vircadia fi - if ! groups athena | grep -q athena; then - usermod -aG athena athena + if ! groups vircadia | grep -q vircadia; then + usermod -aG vircadia vircadia fi fi diff --git a/pkg-scripts/docker-athena-supervisor.conf b/pkg-scripts/docker-vircadia-supervisor.conf similarity index 60% rename from pkg-scripts/docker-athena-supervisor.conf rename to pkg-scripts/docker-vircadia-supervisor.conf index d6e53996b6..3ee1961c80 100644 --- a/pkg-scripts/docker-athena-supervisor.conf +++ b/pkg-scripts/docker-vircadia-supervisor.conf @@ -1,74 +1,74 @@ [supervisord] -user=athena +user=vircadia nodaemon=true -environment=HOME="/var/lib/athena",USER="athena",LD_LIBRARY_PATH="/opt/athena/lib" +environment=HOME="/var/lib/vircadia",USER="vircadia",LD_LIBRARY_PATH="/opt/vircadia/lib" logfile=/dev/stdout logfile_maxbytes=0 pidfile=/var/run/supervisord.pid [program:domain-server] -command=/opt/athena/domain-server +command=/opt/vircadia/domain-server autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia [program:audio-mixer] -command=/opt/athena/assignment-client -t 0 -a localhost -p 48000 +command=/opt/vircadia/assignment-client -t 0 -a localhost -p 48000 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:avatar-mixer] -command=/opt/athena/assignment-client -t 1 -a localhost -p 48001 +command=/opt/vircadia/assignment-client -t 1 -a localhost -p 48001 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:entities-server] -command=/opt/athena/assignment-client -t 6 -a localhost -p 48006 +command=/opt/vircadia/assignment-client -t 6 -a localhost -p 48006 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:asset-server] -command=/opt/athena/assignment-client -t 3 -a localhost -p 48003 +command=/opt/vircadia/assignment-client -t 3 -a localhost -p 48003 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:entity-script-server] -command=/opt/athena/assignment-client -t 5 -a localhost -p 48005 +command=/opt/vircadia/assignment-client -t 5 -a localhost -p 48005 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:messages-mixer] -command=/opt/athena/assignment-client -t 4 -a localhost -p 48004 +command=/opt/vircadia/assignment-client -t 4 -a localhost -p 48004 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [program:scripted-agent] -command=/opt/athena/assignment-client -t 2 -a localhost --max 100 +command=/opt/vircadia/assignment-client -t 2 -a localhost --max 100 autorestart=unexpected -directory=/opt/athena +directory=/opt/vircadia stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr diff --git a/pkg-scripts/make-deb-server b/pkg-scripts/make-deb-server index 72304ae414..6a7bff26c3 100755 --- a/pkg-scripts/make-deb-server +++ b/pkg-scripts/make-deb-server @@ -1,107 +1,99 @@ #!/bin/sh -if [ "$ATHENA" = "" ]; then - ATHENA=`realpath ../..` +if [ "$VIRCADIA" = "" ]; then + VIRCADIA=`realpath ../..` fi -GITDATE=`git -C $ATHENA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` -GITCOMMIT=`git -C $ATHENA/source rev-parse HEAD | cut -c 1-7` -VERSION=0.86.0-k2-$GITDATE-$GITCOMMIT +GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` +GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` +VERSION=2020.2.0-asteria-$GITDATE-$GITCOMMIT sudo apt-get install chrpath binutils dh-make -DEB_BUILD_ROOT=temp-make-deb/athena-server-$VERSION-0ubuntu1 +DEB_BUILD_ROOT=temp-make-deb/vircadia-server-$VERSION-0ubuntu1 rm -r temp-make-deb mkdir -p $DEB_BUILD_ROOT # copy the files over -cp $ATHENA/build/assignment-client/assignment-client $DEB_BUILD_ROOT -cp $ATHENA/build/domain-server/domain-server $DEB_BUILD_ROOT -cp $ATHENA/build/tools/oven/oven $DEB_BUILD_ROOT -cp $ATHENA/build/libraries/*/*.so $DEB_BUILD_ROOT -#cp $ATHENA/build/ice-server/ice-server $DEB_BUILD_ROOT +cp $VIRCADIA/build/assignment-client/assignment-client $DEB_BUILD_ROOT +cp $VIRCADIA/build/domain-server/domain-server $DEB_BUILD_ROOT +cp $VIRCADIA/build/tools/oven/oven $DEB_BUILD_ROOT +cp $VIRCADIA/build/libraries/*/*.so $DEB_BUILD_ROOT +#cp $VIRCADIA/build/ice-server/ice-server $DEB_BUILD_ROOT chrpath -d $DEB_BUILD_ROOT/* -cp $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DEB_BUILD_ROOT -cp $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* $DEB_BUILD_ROOT +cp $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DEB_BUILD_ROOT chmod +x $DEB_BUILD_ROOT/*.so.*.*.* strip --strip-all $DEB_BUILD_ROOT/* -cp $ATHENA/source/pkg-scripts/new-server $DEB_BUILD_ROOT -cp -a $ATHENA/source/domain-server/resources $DEB_BUILD_ROOT +cp $VIRCADIA/source/pkg-scripts/new-server $DEB_BUILD_ROOT +cp -a $VIRCADIA/source/domain-server/resources $DEB_BUILD_ROOT find $DEB_BUILD_ROOT/resources -name ".gitignore" -delete find $DEB_BUILD_ROOT/resources -type f -executable -exec sh -c 'chmod -x {}' \; -cp $ATHENA/source/README.md $DEB_BUILD_ROOT -cp $ATHENA/source/README_hifi.md $DEB_BUILD_ROOT -cp -a $ATHENA/build/assignment-client/plugins $DEB_BUILD_ROOT +cp $VIRCADIA/source/README.md $DEB_BUILD_ROOT +cp $VIRCADIA/source/README_hifi.md $DEB_BUILD_ROOT +cp -a $VIRCADIA/build/assignment-client/plugins $DEB_BUILD_ROOT strip --strip-all $DEB_BUILD_ROOT/plugins/*.so strip --strip-all $DEB_BUILD_ROOT/plugins/*/*.so #begin the debian package construction cd $DEB_BUILD_ROOT -dh_make -p athena-server_$VERSION-0ubuntu1 -c apache -s --createorig -y +dh_make -p vircadia-server_$VERSION-0ubuntu1 -c apache -s --createorig -y -cp $ATHENA/source/pkg-scripts/athena-assignment-client.service debian -cp $ATHENA/source/pkg-scripts/athena-assignment-client@.service debian -cp $ATHENA/source/pkg-scripts/athena-domain-server.service debian -cp $ATHENA/source/pkg-scripts/athena-domain-server@.service debian -#cp $ATHENA/source/pkg-scripts/athena-ice-server.service debian -#cp $ATHENA/source/pkg-scripts/athena-ice-server@.service debian -cp $ATHENA/source/pkg-scripts/athena-server.target debian -cp $ATHENA/source/pkg-scripts/athena-server@.target debian +cp $VIRCADIA/source/pkg-scripts/vircadia-assignment-client.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-assignment-client@.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-domain-server.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-domain-server@.service debian +#cp $VIRCADIA/source/pkg-scripts/vircadia-ice-server.service debian +#cp $VIRCADIA/source/pkg-scripts/vircadia-ice-server@.service debian +cp $VIRCADIA/source/pkg-scripts/vircadia-server.target debian +cp $VIRCADIA/source/pkg-scripts/vircadia-server@.target debian -cp $ATHENA/source/pkg-scripts/server-control debian/control -cp $ATHENA/source/pkg-scripts/server-prerm debian/prerm -cp $ATHENA/source/pkg-scripts/server-postinst debian/postinst -cp $ATHENA/source/LICENSE debian/copyright +cp $VIRCADIA/source/pkg-scripts/server-control debian/control +cp $VIRCADIA/source/pkg-scripts/server-prerm debian/prerm +cp $VIRCADIA/source/pkg-scripts/server-postinst debian/postinst +cp $VIRCADIA/source/pkg-scripts/server-postrm debian/postrm +cp $VIRCADIA/source/LICENSE debian/copyright -echo /etc/opt/athena > debian/dirs -echo /var/lib/athena >> debian/dirs +echo /etc/opt/vircadia > debian/dirs +echo /var/lib/vircadia >> debian/dirs echo README.md > debian/docs echo README_hifi.md >> debian/docs -echo assignment-client opt/athena > debian/install -echo domain-server opt/athena >> debian/install -echo oven opt/athena >> debian/install -#echo ice-server opt/athena >> debian/install -echo new-server opt/athena >> debian/install +echo assignment-client opt/vircadia > debian/install +echo domain-server opt/vircadia >> debian/install +echo oven opt/vircadia >> debian/install +#echo ice-server opt/vircadia >> debian/install +echo new-server opt/vircadia >> debian/install for so in *.so.*.*.*; do - echo $so opt/athena/lib >> debian/install + echo $so opt/vircadia/lib >> debian/install done for so in *.so; do - echo $so opt/athena/lib >> debian/install + echo $so opt/vircadia/lib >> debian/install done #for service in *.service; do -# echo $service opt/athena/systemd >> debian/install +# echo $service opt/vircadia/systemd >> debian/install #done #for target in *.target; do -# echo $target opt/athena/systemd >> debian/install +# echo $target opt/vircadia/systemd >> debian/install #done -find resources -type f -exec sh -c 'echo {} opt/athena/$(dirname "{}") >> debian/install' \; -find plugins -type f -exec sh -c 'echo {} opt/athena/$(dirname "{}") >> debian/install' \; +find resources -type f -exec sh -c 'echo {} opt/vircadia/$(dirname "{}") >> debian/install' \; +find plugins -type f -exec sh -c 'echo {} opt/vircadia/$(dirname "{}") >> debian/install' \; -#echo usr/lib/systemd/system/athena-assignment-client.service opt/athena/systemd/athena-assignment-client.service > debian/athena-server.links -#echo usr/lib/systemd/system/athena-assignment-client@.service opt/athena/systemd/athena-assignment-client@.service >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-domain-server.service opt/athena/systemd/athena-domain-server.service >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-domain-server@.service opt/athena/systemd/athena-domain-server@.service >> debian/athena-server.links -##echo usr/lib/systemd/system/athena-ice-server.service opt/athena/systemd/athena-ice-server.service >> debian/athena-server.links -##echo usr/lib/systemd/system/athena-ice-server@.service opt/athena/systemd/athena-ice-server@.service >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-server.target opt/athena/systemd/athena-server.target >> debian/athena-server.links -#echo usr/lib/systemd/system/athena-server@.target opt/athena/systemd/athena-server@.target >> debian/athena-server.links +SOFILES=`ls *.so *.so.*.*.* | grep -Po '^(.+\.so(\.\d+)?)' | sed 's/\./\\\./g' | paste -d'|' -s` -SOFILES=`ls *.so *.so.*.*.* | sed 's/\./\\\./g' | paste -d'|' -s` +DEPENDS=`find * -path debian -prune -o -type f -executable -exec sh -c 'objdump -p {} | grep NEEDED' \; \ + | awk '{print $2}' | sort | uniq | egrep -v "^($SOFILES)$" \ + | xargs -n 1 -I {} sh -c 'dpkg -S {} | head -n 1' | cut -d ':' -f 1 | sort | uniq | paste -d',' -s` -DEPENDS=`find * -type f -executable -exec sh -c 'objdump -p {} | grep NEEDED' \; | awk '{print $2}' | sort | uniq | egrep -v "^($SOFILES)$" | xargs -n 1 -I {} sh -c 'dpkg -S {} | head -n 1' | cut -d ':' -f 1 | sort | uniq | paste -d',' -s` - -cp $ATHENA/source/pkg-scripts/server-rules debian/rules -sed "s/{DEPENDS}/$DEPENDS/" $ATHENA/source/pkg-scripts/server-control > debian/control +cp $VIRCADIA/source/pkg-scripts/server-rules debian/rules +sed "s/{DEPENDS}/$DEPENDS/" $VIRCADIA/source/pkg-scripts/server-control > debian/control dpkg-buildpackage -us -uc diff --git a/pkg-scripts/make-docker-server b/pkg-scripts/make-docker-server index 3ad37bb0e2..68b8ca7cd6 100755 --- a/pkg-scripts/make-docker-server +++ b/pkg-scripts/make-docker-server @@ -1,53 +1,51 @@ #!/bin/sh -if [ "$ATHENA" = "" ]; then - ATHENA=`realpath ../..` +if [ "$VIRCADIA" = "" ]; then + VIRCADIA=`realpath ../..` fi -GITSRC=`git -C $ATHENA/source config --get remote.origin.url | cut -d':' -f 2` -GITDATE=`git -C $ATHENA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` -GITCOMMIT=`git -C $ATHENA/source rev-parse HEAD | cut -c 1-7` +GITSRC=`git -C $VIRCADIA/source config --get remote.origin.url | cut -d':' -f 2` +GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` +GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` sudo apt-get install chrpath binutils DOCK_BUILD_ROOT=temp-make-dock rm -r temp-make-dock mkdir -p $DOCK_BUILD_ROOT -cp $ATHENA/source/pkg-scripts/Dockerfile.templ $DOCK_BUILD_ROOT/Dockerfile -cp $ATHENA/source/pkg-scripts/docker-entrypoint.sh $DOCK_BUILD_ROOT/entrypoint.sh -cp $ATHENA/source/pkg-scripts/docker-athena-supervisor.conf $DOCK_BUILD_ROOT/athena.conf +cp $VIRCADIA/source/pkg-scripts/Dockerfile.templ $DOCK_BUILD_ROOT/Dockerfile +cp $VIRCADIA/source/pkg-scripts/docker-entrypoint.sh $DOCK_BUILD_ROOT/entrypoint.sh +cp $VIRCADIA/source/pkg-scripts/docker-vircadia-supervisor.conf $DOCK_BUILD_ROOT/vircadia.conf # copy the files over mkdir -p $DOCK_BUILD_ROOT/opt -cp $ATHENA/build/assignment-client/assignment-client $DOCK_BUILD_ROOT/opt -cp $ATHENA/build/domain-server/domain-server $DOCK_BUILD_ROOT/opt -cp $ATHENA/build/tools/oven/oven $DOCK_BUILD_ROOT/opt -#cp $ATHENA/build/ice-server/ice-server $DOCK_BUILD_ROOT/opt +cp $VIRCADIA/build/assignment-client/assignment-client $DOCK_BUILD_ROOT/opt +cp $VIRCADIA/build/domain-server/domain-server $DOCK_BUILD_ROOT/opt +cp $VIRCADIA/build/tools/oven/oven $DOCK_BUILD_ROOT/opt +#cp $VIRCADIA/build/ice-server/ice-server $DOCK_BUILD_ROOT/opt strip --strip-all $DOCK_BUILD_ROOT/opt/* chrpath -d $DOCK_BUILD_ROOT/opt/* -cp -a $ATHENA/build/assignment-client/plugins $DOCK_BUILD_ROOT/opt +cp -a $VIRCADIA/build/assignment-client/plugins $DOCK_BUILD_ROOT/opt strip --strip-all $DOCK_BUILD_ROOT/opt/plugins/*.so chrpath -d $DOCK_BUILD_ROOT/opt/plugins/*.so strip --strip-all $DOCK_BUILD_ROOT/opt/plugins/*/*.so chrpath -d $DOCK_BUILD_ROOT/opt/plugins/*/*.so -cp -a $ATHENA/source/domain-server/resources $DOCK_BUILD_ROOT/opt +cp -a $VIRCADIA/source/domain-server/resources $DOCK_BUILD_ROOT/opt find $DOCK_BUILD_ROOT/opt/resources -name ".gitignore" -delete find $DOCK_BUILD_ROOT/opt/resources -type f -executable -exec sh -c 'chmod -x {}' \; mkdir -p $DOCK_BUILD_ROOT/lib -cp $ATHENA/build/libraries/*/*.so $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DOCK_BUILD_ROOT/lib -cp $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/build/libraries/*/*.so $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* $DOCK_BUILD_ROOT/lib +cp $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* $DOCK_BUILD_ROOT/lib chmod +x $DOCK_BUILD_ROOT/lib/* strip --strip-all $DOCK_BUILD_ROOT/lib/* chrpath -d $DOCK_BUILD_ROOT/lib/* @@ -57,4 +55,4 @@ SOFILES=`ls $DOCK_BUILD_ROOT/lib | sed 's/\./\\\./g' | paste -d'|' -s` DEPENDS=`find $DOCK_BUILD_ROOT/opt $DOCK_BUILD_ROOT/lib -type f -executable -exec sh -c 'objdump -p {} | grep NEEDED' \; | awk '{print $2}' | sort | uniq | egrep -v "^($SOFILES)$" | xargs -n 1 -I {} sh -c 'dpkg -S {} | head -n 1' | cut -d ':' -f 1 | sort | uniq | paste -d' ' -s` cd $DOCK_BUILD_ROOT -docker build -t odysseus654/athena-server --build-arg "DEPENDS=$DEPENDS" --build-arg "GITSRC=$GITSRC" --build-arg "GITDATE=$GITDATE" --build-arg "GITCOMMIT=$GITCOMMIT" . +docker build -t odysseus654/vircadia-server --build-arg "DEPENDS=$DEPENDS" --build-arg "GITSRC=$GITSRC" --build-arg "GITDATE=$GITDATE" --build-arg "GITCOMMIT=$GITCOMMIT" . diff --git a/pkg-scripts/make-rpm-server b/pkg-scripts/make-rpm-server index 9863faa0ab..20ac76d4b0 100755 --- a/pkg-scripts/make-rpm-server +++ b/pkg-scripts/make-rpm-server @@ -1,44 +1,42 @@ #!/bin/sh -if [ "$ATHENA" = "" ]; then - ATHENA=`realpath ../..` +if [ "$VIRCADIA" = "" ]; then + VIRCADIA=`realpath ../..` fi -GITDATE=`git -C $ATHENA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` -GITCOMMIT=`git -C $ATHENA/source rev-parse HEAD | cut -c 1-7` -VERSION=0.86.0_K2_${GITDATE}_${GITCOMMIT} +GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` +GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` +VERSION=2020.2.0_ASTERIA_${GITDATE}_${GITCOMMIT} SOFILES=`ls \ - $ATHENA/build/libraries/*/*.so \ - $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ - $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* \ + $VIRCADIA/build/libraries/*/*.so \ + $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Quick.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ | sed 's/\./\\\./g' \ | paste -d'|' -s` DEPENDS=mesa-libGL,`ls \ - $ATHENA/build/assignment-client/assignment-client \ - $ATHENA/build/domain-server/domain-server \ - $ATHENA/build/tools/oven/oven \ - $ATHENA/build/libraries/*/*.so \ - $ATHENA/qt5-install/lib/libQt5Network.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Core.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Widgets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Gui.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Script.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Quick.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5Qml.so.*.*.* \ - $ATHENA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ - $ATHENA/build/ext/makefiles/quazip/project/lib/libquazip5.so.*.*.* \ - $ATHENA/build/assignment-client/plugins/*.so \ - $ATHENA/build/assignment-client/plugins/*/*.so \ + $VIRCADIA/build/assignment-client/assignment-client \ + $VIRCADIA/build/domain-server/domain-server \ + $VIRCADIA/build/tools/oven/oven \ + $VIRCADIA/build/libraries/*/*.so \ + $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5Quick.so.*.*.* \ + $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* \ + $VIRCADIA/build/assignment-client/plugins/*.so \ + $VIRCADIA/build/assignment-client/plugins/*/*.so \ | xargs -I {} sh -c 'objdump -p {} | grep NEEDED' \ | awk '{print $2}' \ | sort | uniq \ @@ -51,6 +49,6 @@ DEPENDS=mesa-libGL,`ls \ sudo yum install chrpath -export VERSION DEPENDS ATHENA -rpmbuild --target x86_64 -bb ./athena-server.spec +export VERSION DEPENDS VIRCADIA +rpmbuild --target x86_64 -bb ./vircadia-server.spec mv ~/rpmbuild/RPMS/x86_64/*.rpm . diff --git a/pkg-scripts/new-server b/pkg-scripts/new-server index fd9ba95761..a2ce9b330d 100755 --- a/pkg-scripts/new-server +++ b/pkg-scripts/new-server @@ -2,13 +2,13 @@ if [ -z "$1" ] || [ -z "$2" ]; then echo "new-server {name} {base-port}" echo - echo "Sets up a new athena server with the specified name and base port number" + echo "Sets up a new vircadia server with the specified name and base port number" echo " {name} - a simple name used to identify the server to scripts (not used in the server configuration)" echo " {base-port} - the base port number (default server is 40100). The metaverse port will be {base-port}+2" echo " Four contiguous port numbers are allocated, these must not overlap with other running services on this machine" echo echo "Launching a server created by this script is done with:" - echo " sudo systemctl start athena-server@{name}.target" + echo " sudo systemctl start vircadia-server@{name}.target" echo exit 1 fi @@ -20,30 +20,30 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi -if [ -d "/var/lib/athena/$1" ]; then - echo "Path /var/lib/athena/$1 already exists" +if [ -d "/var/lib/vircadia/$1" ]; then + echo "Path /var/lib/vircadia/$1 already exists" echo echo "Please remove this path first if you wish to recreate this server" exit 2 fi -mkdir -p /var/lib/athena/$1/.local/share -ln -s ../.. /var/lib/athena/$1/.local/share/Project\ Athena\ -\ dev -ln -s ../.. /var/lib/athena/$1/.local/share/Project\ Athena -mkdir -p /var/lib/athena/$1/domain-server -echo "{\"metaverse\": {\"local_port\": $(($2 + 2))},\"version\": 2.4}" > /var/lib/athena/$1/domain-server/config.json -chown -R athena.athena /var/lib/athena/$1 +mkdir -p /var/lib/vircadia/$1/.local/share +ln -s ../.. /var/lib/vircadia/$1/.local/share/Vircadia\ -\ dev +ln -s ../.. /var/lib/vircadia/$1/.local/share/Vircadia +mkdir -p /var/lib/vircadia/$1/domain-server +echo "{\"metaverse\": {\"local_port\": $(($2 + 2))},\"version\": 2.4}" > /var/lib/vircadia/$1/domain-server/config.json +chown -R vircadia.vircadia /var/lib/vircadia/$1 -echo HIFI_DOMAIN_SERVER_HTTP_PORT=$2 > /etc/opt/athena/$1.conf -echo HIFI_DOMAIN_SERVER_HTTPS_PORT=$(($2 + 1)) >> /etc/opt/athena/$1.conf -echo HIFI_DOMAIN_SERVER_PORT=$(($2 + 2)) >> /etc/opt/athena/$1.conf -echo HIFI_DOMAIN_SERVER_DTLS_PORT=$(($2 + 3)) >> /etc/opt/athena/$1.conf +echo HIFI_DOMAIN_SERVER_HTTP_PORT=$2 > /etc/opt/vircadia/$1.conf +echo HIFI_DOMAIN_SERVER_HTTPS_PORT=$(($2 + 1)) >> /etc/opt/vircadia/$1.conf +echo HIFI_DOMAIN_SERVER_PORT=$(($2 + 2)) >> /etc/opt/vircadia/$1.conf +echo HIFI_DOMAIN_SERVER_DTLS_PORT=$(($2 + 3)) >> /etc/opt/vircadia/$1.conf -echo "A new athena server has been created with the name of '$1'" +echo "A new vircadia server has been created with the name of '$1'" echo echo "To launch it:" -echo " sudo systemctl start athena-server@$1.target" +echo " sudo systemctl start vircadia-server@$1.target" echo "To have it launch at system start:" -echo " sudo systemctl enable athena-server@$1.target" +echo " sudo systemctl enable vircadia-server@$1.target" echo "The server configuration console is available at:" echo " http://localhost:$2" diff --git a/pkg-scripts/server-control b/pkg-scripts/server-control index 70383891bd..c80b8da724 100644 --- a/pkg-scripts/server-control +++ b/pkg-scripts/server-control @@ -1,15 +1,15 @@ -Source: athena-server +Source: vircadia-server Section: comm Priority: optional Maintainer: Heather Anderson Build-Depends: debhelper (>= 10) Standards-Version: 4.1.2 -Homepage: https://www.projectathena.dev +Homepage: https://vircadia.com Vcs-Git: https://github.com/kasenvr/project-athena.git Vcs-Browser: https://github.com/kasenvr/project-athena -Package: athena-server +Package: vircadia-server Architecture: any Depends: adduser, {DEPENDS} -Description: Project Athena allows creation and sharing of VR experiences. - The Project Athena metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. +Description: Vircadia allows creation and sharing of VR experiences. + The Vircadia metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. diff --git a/pkg-scripts/server-postinst b/pkg-scripts/server-postinst index fa14e8175f..cb06adbae0 100755 --- a/pkg-scripts/server-postinst +++ b/pkg-scripts/server-postinst @@ -1,5 +1,5 @@ #!/bin/sh -# postinst script for athena-server +# postinst script for vircadia-server # # see: dh_installdeb(1) @@ -17,23 +17,55 @@ set -e # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package - case "$1" in configure) - ldconfig -n /opt/athena/lib - adduser --system --quiet --gecos "Project Athena" --home /var/lib/athena --group --no-create-home athena - mkdir -p /var/lib/athena - chown athena:athena /var/lib/athena - chmod 775 /var/lib/athena - if [ ! -d "/var/lib/athena/default" ]; then - /opt/athena/new-server default 40100 - systemctl enable athena-server@default.target - systemctl start athena-server@default.target + ldconfig -n /opt/vircadia/lib + adduser --system --quiet --gecos "Vircadia" --home /var/lib/vircadia --group --no-create-home vircadia + mkdir -p /var/lib/vircadia + chown vircadia:vircadia /var/lib/vircadia + chmod 775 /var/lib/vircadia + if [ ! -d "/var/lib/vircadia/default" ]; then + if [ -d "/var/lib/athena" ]; then + ATHENA_ACTIVE=`systemctl list-units \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | paste -s -d'|' \ + | head -c -1` + ATHENA_ENABLED=`systemctl list-units --state=loaded \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | xargs -I {} sh -c 'if systemctl is-enabled {} >/dev/null ; then echo {} ; fi' \ + | paste -s -d'|' \ + | head -c -1` + + # shutdown athena servers + echo -n $ATHENA_ACTIVE | xargs -d'|' systemctl stop + + # copy the server files over + cp /etc/opt/athena/* /etc/opt/vircadia + cp -R /var/lib/athena/* /var/lib/vircadia + chown -R vircadia:vircadia /var/lib/vircadia/* + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. "Vircadia - dev"' ';' + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. Vircadia' ';' + + VIRCADIA_ACTIVE=`echo -n $ATHENA_ACTIVE | sed 's/athena/vircadia/g'` + VIRCADIA_ENABLED=`echo -n $ATHENA_ENABLED | sed 's/athena/vircadia/g'` + + echo -n $ATHENA_ENABLED | xargs -d'|' systemctl disable + echo -n $VIRCADIA_ENABLED | xargs -d'|' systemctl enable + echo -n $VIRCADIA_ACTIVE | xargs -d'|' systemctl start + else + /opt/vircadia/new-server default 40100 + systemctl enable vircadia-server@default.target + systemctl start vircadia-server@default.target + fi + else + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl restart fi ;; abort-remove|abort-deconfigure) - ldconfig -n /opt/athena/lib + ldconfig -n /opt/vircadia/lib ;; abort-upgrade) diff --git a/pkg-scripts/server-postrm b/pkg-scripts/server-postrm new file mode 100755 index 0000000000..944d5e60f4 --- /dev/null +++ b/pkg-scripts/server-postrm @@ -0,0 +1,41 @@ +#!/bin/sh +# postrm script for vircadia-server +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `disappear' +# * `abort-install; +# * `abort-install' +# * `abort-upgrade' +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + rm -r /etc/opt/vircadia + rm -r /var/lib/vircadia + ;; + + remove|upgrade|failed-upgrade|disappear|abort-install|abort-upgrade) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/pkg-scripts/server-prerm b/pkg-scripts/server-prerm index cdeddd308e..de15128778 100755 --- a/pkg-scripts/server-prerm +++ b/pkg-scripts/server-prerm @@ -1,5 +1,5 @@ #!/bin/sh -# prerm script for athena-server +# prerm script for vircadia-server # # see: dh_installdeb(1) @@ -19,7 +19,11 @@ set -e case "$1" in remove) - find -P /opt/athena/lib -type l -delete + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl stop + + find -P /opt/vircadia/lib -type l -delete ;; upgrade|deconfigure) diff --git a/pkg-scripts/server-rules b/pkg-scripts/server-rules index 4585591b15..24f557159b 100755 --- a/pkg-scripts/server-rules +++ b/pkg-scripts/server-rules @@ -18,26 +18,26 @@ dh $@ --with=systemd override_dh_systemd_enable: - dh_systemd_enable --no-enable --name athena-assignment-client athena-assignment-client.service - dh_systemd_enable --no-enable --name athena-assignment-client@ athena-assignment-client@.service - dh_systemd_enable --no-enable --name athena-domain-server athena-domain-server.service - dh_systemd_enable --no-enable --name athena-domain-server@ athena-domain-server@.service - #dh_systemd_enable --no-enable --name athena-ice-server athena-ice-server.service - #dh_systemd_enable --no-enable --name athena-ice-server@ athena-ice-server@.service - dh_systemd_enable --no-enable --name athena-server athena-server.target - dh_systemd_enable --no-enable --name athena-server@ athena-server@.target - #dh_systemd_enable --name athena-server@default athena-server@default.target + dh_systemd_enable --no-enable --name vircadia-assignment-client vircadia-assignment-client.service + dh_systemd_enable --no-enable --name vircadia-assignment-client@ vircadia-assignment-client@.service + dh_systemd_enable --no-enable --name vircadia-domain-server vircadia-domain-server.service + dh_systemd_enable --no-enable --name vircadia-domain-server@ vircadia-domain-server@.service + #dh_systemd_enable --no-enable --name vircadia-ice-server vircadia-ice-server.service + #dh_systemd_enable --no-enable --name vircadia-ice-server@ vircadia-ice-server@.service + dh_systemd_enable --no-enable --name vircadia-server vircadia-server.target + dh_systemd_enable --no-enable --name vircadia-server@ vircadia-server@.target + #dh_systemd_enable --name vircadia-server@default vircadia-server@default.target override_dh_systemd_start: - dh_systemd_start --restart-after-upgrade --no-start athena-assignment-client.service - dh_systemd_start --restart-after-upgrade --no-start athena-assignment-client@.service - dh_systemd_start --restart-after-upgrade --no-start athena-domain-server.service - dh_systemd_start --restart-after-upgrade --no-start athena-domain-server@.service - #dh_systemd_start --restart-after-upgrade --no-start athena-ice-server.service - #dh_systemd_start --restart-after-upgrade --no-start athena-ice-server@.service - dh_systemd_start --restart-after-upgrade --no-start athena-server.target - dh_systemd_start --restart-after-upgrade --no-start athena-server@.target - #dh_systemd_start --restart-after-upgrade athena-server@default.target + dh_systemd_start --restart-after-upgrade --no-start vircadia-assignment-client.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-assignment-client@.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-domain-server.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-domain-server@.service + #dh_systemd_start --restart-after-upgrade --no-start vircadia-ice-server.service + #dh_systemd_start --restart-after-upgrade --no-start vircadia-ice-server@.service + dh_systemd_start --restart-after-upgrade --no-start vircadia-server.target + dh_systemd_start --restart-after-upgrade --no-start vircadia-server@.target + #dh_systemd_start --restart-after-upgrade vircadia-server@default.target override_dh_installinit: dh_installinit --noscripts diff --git a/pkg-scripts/vircadia-assignment-client.service b/pkg-scripts/vircadia-assignment-client.service new file mode 100644 index 0000000000..9c86e4e874 --- /dev/null +++ b/pkg-scripts/vircadia-assignment-client.service @@ -0,0 +1,18 @@ +[Unit] +Description=Assignment client service for Vircadia server +After=network.target +PartOf=vircadia-server.target + +[Service] +Restart=always + +WorkingDirectory=/opt/vircadia +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" +User=vircadia +Group=vircadia +#LimitCORE=infinity +#ExecStart=/opt/vircadia/assignment-client -n 6 +ExecStart=/opt/vircadia/assignment-client --min 6 --max 20 + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-assignment-client@.service b/pkg-scripts/vircadia-assignment-client@.service new file mode 100644 index 0000000000..4684947426 --- /dev/null +++ b/pkg-scripts/vircadia-assignment-client@.service @@ -0,0 +1,20 @@ +[Unit] +Description=Assignment client service for Vircadia server +After=network.target +PartOf=vircadia-server@%i.target + +[Service] +Restart=always + +WorkingDirectory=/opt/vircadia +EnvironmentFile=/etc/opt/vircadia/%i.conf +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" "HOME=/var/lib/vircadia/%i" +PrivateTmp=true +User=vircadia +Group=vircadia +#LimitCORE=infinity +#ExecStart=/opt/vircadia/assignment-client -n 6 +ExecStart=/opt/vircadia/assignment-client --min 6 --max 20 --server-port $HIFI_DOMAIN_SERVER_PORT + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-domain-server.service b/pkg-scripts/vircadia-domain-server.service new file mode 100644 index 0000000000..8c261bc72f --- /dev/null +++ b/pkg-scripts/vircadia-domain-server.service @@ -0,0 +1,17 @@ +[Unit] +Description=Domain Server service for Vircadia +After=network.target +PartOf=vircadia-server.target + +[Service] +Restart=on-failure + +WorkingDirectory=/opt/vircadia +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" +User=vircadia +Group=vircadia +#LimitCORE=infinity +ExecStart=/opt/vircadia/domain-server + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-domain-server@.service b/pkg-scripts/vircadia-domain-server@.service new file mode 100644 index 0000000000..7ae84dc6f3 --- /dev/null +++ b/pkg-scripts/vircadia-domain-server@.service @@ -0,0 +1,19 @@ +[Unit] +Description=Domain Server service for Vircadia +After=network.target +PartOf=vircadia-server@%i.target + +[Service] +Restart=on-failure + +WorkingDirectory=/opt/vircadia +EnvironmentFile=/etc/opt/vircadia/%i.conf +Environment="LD_LIBRARY_PATH=/opt/vircadia/lib" "HOME=/var/lib/vircadia/%i" +PrivateTmp=true +User=vircadia +Group=vircadia +#LimitCORE=infinity +ExecStart=/opt/vircadia/domain-server + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/athena-ice-server.service b/pkg-scripts/vircadia-ice-server.service similarity index 63% rename from pkg-scripts/athena-ice-server.service rename to pkg-scripts/vircadia-ice-server.service index 57db8581b3..d966ffe0c0 100644 --- a/pkg-scripts/athena-ice-server.service +++ b/pkg-scripts/vircadia-ice-server.service @@ -1,18 +1,18 @@ [Unit] -Description=Ice Server service for Athena +Description=Ice Server service for Vircadia After=network.target -PartOf=athena-server.target +PartOf=vircadia-server.target [Service] Restart=on-failure -Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/athena/lib" -WorkingDirectory=/opt/athena -User=athena -Group=athena +Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/vircadia/lib" +WorkingDirectory=/opt/vircadia +User=vircadia +Group=vircadia #ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l ice-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/ice-server/ice-server; fi' #LimitCORE=infinity -ExecStart=/opt/athena/ice-server +ExecStart=/opt/vircadia/ice-server [Install] WantedBy=multi-user.target diff --git a/pkg-scripts/athena-ice-server@.service b/pkg-scripts/vircadia-ice-server@.service similarity index 56% rename from pkg-scripts/athena-ice-server@.service rename to pkg-scripts/vircadia-ice-server@.service index e4993f5436..fc5ab4615d 100644 --- a/pkg-scripts/athena-ice-server@.service +++ b/pkg-scripts/vircadia-ice-server@.service @@ -1,20 +1,20 @@ [Unit] -Description=Ice Server service for Athena +Description=Ice Server service for Vircadia After=network.target -PartOf=athena-server@%i.target +PartOf=vircadia-server@%i.target [Service] Restart=on-failure -EnvironmentFile=/etc/opt/athena/%i.conf -Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/athena/lib" "HOME=/var/lib/athena/%i" +EnvironmentFile=/etc/opt/vircadia/%i.conf +Environment="HIFI_ENVIRONMENT=production" "LD_LIBRARY_PATH=/opt/vircadia/lib" "HOME=/var/lib/vircadia/%i" PrivateTmp=true -WorkingDirectory=/opt/athena -User=athena -Group=athena +WorkingDirectory=/opt/vircadia +User=vircadia +Group=vircadia #ExecStartPre=/bin/bash -c 'if /usr/bin/pgrep -l ice-server; then /usr/bin/pkill -SIGKILL -f /usr/share/hifi/ice-server/ice-server; fi' #LimitCORE=infinity -ExecStart=/opt/athena/ice-server +ExecStart=/opt/vircadia/ice-server [Install] WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-server.spec b/pkg-scripts/vircadia-server.spec new file mode 100644 index 0000000000..3e0eed896e --- /dev/null +++ b/pkg-scripts/vircadia-server.spec @@ -0,0 +1,168 @@ +#VIRCADIA=~/Vircadia rpmbuild --target x86_64 -bb vircadia-server.spec +%define version %{lua:print(os.getenv("VERSION"))} +%define depends %{lua:print(os.getenv("DEPENDS"))} + +Name: vircadia-server +Version: %{version} +Release: 1%{?dist} +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 + +#BuildRequires: systemd-rpm-macros +BuildRequires: chrpath +Requires: %{depends} +BuildArch: x86_64 +AutoReq: no +AutoProv: no + +%description +Vircadia allows creation and sharing of VR experiences. + The Vircadia metaverse provides built-in social features, including avatar interactions, spatialized audio and interactive physics. Additionally, you have the ability to import any 3D object into your virtual environment. + + +%prep + + +%build + + +%install +rm -rf $RPM_BUILD_ROOT +install -d $RPM_BUILD_ROOT/opt/vircadia +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/assignment-client/assignment-client +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/domain-server/domain-server +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/tools/oven/oven +#install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/build/ice-server/ice-server +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/* +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/* +install -m 0755 -t $RPM_BUILD_ROOT/opt/vircadia $VIRCADIA/source/pkg-scripts/new-server +install -d $RPM_BUILD_ROOT/opt/vircadia/lib +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/build/libraries/*/*.so +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/lib/* +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/lib/* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Network.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Core.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Widgets.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Gui.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Script.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5WebSockets.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Qml.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5Quick.so.*.*.* +install -m 0644 -t $RPM_BUILD_ROOT/opt/vircadia/lib $VIRCADIA/qt5-install/lib/libQt5ScriptTools.so.*.*.* +install -d $RPM_BUILD_ROOT/usr/lib/systemd/system +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-assignment-client.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-assignment-client@.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-domain-server.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-domain-server@.service +#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-ice-server.service +#install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-ice-server@.service +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-server.target +install -m 0644 -t $RPM_BUILD_ROOT/usr/lib/systemd/system $VIRCADIA/source/pkg-scripts/vircadia-server@.target +cp -a $VIRCADIA/source/domain-server/resources $RPM_BUILD_ROOT/opt/vircadia +cp -a $VIRCADIA/build/assignment-client/plugins $RPM_BUILD_ROOT/opt/vircadia +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/plugins/*.so +chrpath -d $RPM_BUILD_ROOT/opt/vircadia/plugins/*/*.so +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/plugins/*.so +strip --strip-all $RPM_BUILD_ROOT/opt/vircadia/plugins/*/*.so +find $RPM_BUILD_ROOT/opt/vircadia/resources -name ".gitignore" -delete + + +%files +%license $VIRCADIA/source/LICENSE +/opt/vircadia +/usr/lib/systemd/system + + +%changelog + + +%post +# create users +getent passwd vircadia >/dev/null 2>&1 || useradd -r -c "Vircadia" -d /var/lib/vircadia -U -M vircadia +#getent group vircadia >/dev/null 2>&1 || groupadd -r vircadia + +# create data folder +mkdir -p /etc/opt/vircadia +mkdir -p /var/lib/vircadia && chown vircadia:vircadia /var/lib/vircadia && chmod 775 /var/lib/vircadia + +ldconfig -n /opt/vircadia/lib + +%systemd_post vircadia-assignment-client.service +%systemd_post vircadia-assignment-client@.service +%systemd_post vircadia-domain-server.service +%systemd_post vircadia-domain-server@.service +#%systemd_post vircadia-ice-server.service +#%systemd_post vircadia-ice-server@.service +%systemd_post vircadia-server.target +%systemd_post vircadia-server@.target + +if [ ! -d "/var/lib/vircadia/default" ]; then + if [ -d "/var/lib/athena" ]; then + ATHENA_ACTIVE=`systemctl list-units \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | paste -s -d'|' \ + | head -c -1` + ATHENA_ENABLED=`systemctl list-units --state=loaded \ + | grep -P -o "(athena-assignment-client|athena-domain-server|athena-server)\S+" \ + | xargs -I {} sh -c 'if systemctl is-enabled {} >/dev/null ; then echo {} ; fi' \ + | paste -s -d'|' \ + | head -c -1` + + # shutdown athena servers + echo -n $ATHENA_ACTIVE | xargs -d'|' systemctl stop + + # copy the server files over + cp /etc/opt/athena/* /etc/opt/vircadia + cp -R /var/lib/athena/* /var/lib/vircadia + chown -R vircadia:vircadia /var/lib/vircadia/* + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. "Vircadia - dev"' ';' + find /var/lib/vircadia -maxdepth 3 -path "*\.local/share" -execdir sh -c 'cd share; ln -s ../.. Vircadia' ';' + + VIRCADIA_ACTIVE=`echo -n $ATHENA_ACTIVE | sed 's/athena/vircadia/g'` + VIRCADIA_ENABLED=`echo -n $ATHENA_ENABLED | sed 's/athena/vircadia/g'` + + echo -n $ATHENA_ENABLED | xargs -d'|' systemctl disable + echo -n $VIRCADIA_ENABLED | xargs -d'|' systemctl enable + echo -n $VIRCADIA_ACTIVE | xargs -d'|' systemctl start + else + /opt/vircadia/new-server default 40100 + systemctl enable vircadia-server@default.target + systemctl start vircadia-server@default.target + fi +else + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl restart +fi + + +%preun + +if [ "$1" -eq 0 ]; then + systemctl list-units \ + | grep -P -o "(vircadia-assignment-client|vircadia-domain-server|vircadia-server)\S+" \ + | xargs systemctl stop +fi + +%systemd_preun vircadia-server.target +%systemd_preun vircadia-server@.target +%systemd_preun vircadia-assignment-client.service +%systemd_preun vircadia-assignment-client@.service +%systemd_preun vircadia-domain-server.service +%systemd_preun vircadia-domain-server@.service +#%systemd_preun vircadia-ice-server.service +#%systemd_preun vircadia-ice-server@.service + + +%postun +%systemd_postun_with_restart vircadia-server.target +%systemd_postun_with_restart vircadia-server@.target +%systemd_postun_with_restart vircadia-assignment-client.service +%systemd_postun_with_restart vircadia-assignment-client@.service +%systemd_postun_with_restart vircadia-domain-server.service +%systemd_postun_with_restart vircadia-domain-server@.service +#%systemd_postun_with_restart vircadia-ice-server.service +#%systemd_postun_with_restart vircadia-ice-server@.service diff --git a/pkg-scripts/vircadia-server.target b/pkg-scripts/vircadia-server.target new file mode 100644 index 0000000000..27225aade3 --- /dev/null +++ b/pkg-scripts/vircadia-server.target @@ -0,0 +1,11 @@ +[Unit] +Description=Vircadia virtual domain +Wants=vircadia-assignment-client.service +Wants=vircadia-domain-server.service +#Wants=vircadia-ice-server.service +After=vircadia-assignment-client.service +After=vircadia-domain-server.service +#After=vircadia-ice-server.service + +[Install] +WantedBy=multi-user.target diff --git a/pkg-scripts/vircadia-server@.target b/pkg-scripts/vircadia-server@.target new file mode 100644 index 0000000000..158a48bedc --- /dev/null +++ b/pkg-scripts/vircadia-server@.target @@ -0,0 +1,11 @@ +[Unit] +Description=Vircadia virtual domain +Wants=vircadia-assignment-client@%i.service +Wants=vircadia-domain-server@%i.service +#Wants=vircadia-ice-server@%i.service +After=vircadia-assignment-client@%i.service +After=vircadia-domain-server@%i.service +#After=vircadia-ice-server@%i.service + +[Install] +WantedBy=multi-user.target diff --git a/plugins/KasenAPIExample/src/KasenAPIExample.cpp b/plugins/KasenAPIExample/src/KasenAPIExample.cpp index d566d11376..9f81793037 100644 --- a/plugins/KasenAPIExample/src/KasenAPIExample.cpp +++ b/plugins/KasenAPIExample/src/KasenAPIExample.cpp @@ -113,7 +113,7 @@ private: } struct _HeadHelper : public HeadData { QMap getBlendshapeMap() const { - return _blendshapeLookupMap; + return BLENDSHAPE_LOOKUP_MAP; } struct States { QVector base, summed, transient; }; States getBlendshapeStates() const { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 78b462369b..7691d2ab70 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -738,26 +738,28 @@ int OpenVrDisplayPlugin::getRequiredThreadCount() const { } QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const { - QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String); + QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_RecordingDeviceOverride_String); if (!device.isEmpty()) { static const WCHAR INIT = 0; size_t size = device.size() + 1; std::vector deviceW; deviceW.assign(size, INIT); device.toWCharArray(deviceW.data()); + // FIXME: This may not be necessary if vr::k_pch_audio_RecordingDeviceOverride_StringName is used above. device = AudioClient::getWinDeviceName(deviceW.data()); } return device; } QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const { - QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String); + QString device = getVrSettingString(vr::k_pch_audio_Section, vr::k_pch_audio_PlaybackDeviceOverride_String); if (!device.isEmpty()) { static const WCHAR INIT = 0; size_t size = device.size() + 1; std::vector deviceW; deviceW.assign(size, INIT); device.toWCharArray(deviceW.data()); + // FIXME: This may not be necessary if vr::k_pch_audio_PlaybackDeviceOverride_StringName is used above. device = AudioClient::getWinDeviceName(deviceW.data()); } return device; diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index ce60719d67..e55e7feaf4 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -100,7 +100,7 @@ QString getVrSettingString(const char* section, const char* setting) { vr::IVRSettings * vrSettings = vr::VRSettings(); if (vrSettings) { vr::EVRSettingsError error = vr::VRSettingsError_None; - vrSettings->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, BUFFER, BUFFER_SIZE, &error); + vrSettings->GetString(vr::k_pch_audio_Section, setting, BUFFER, BUFFER_SIZE, &error); if (error == vr::VRSettingsError_None) { result = BUFFER; } @@ -413,7 +413,6 @@ void showMinSpecWarning() { vrSystem->ResetSeatedZeroPose(); QString imagePath = PathUtils::resourcesPath() + "/images/steam-min-spec-failed.png"; vrOverlay->SetOverlayFromFile(minSpecFailedOverlay, imagePath.toLocal8Bit().toStdString().c_str()); - vrOverlay->SetHighQualityOverlay(minSpecFailedOverlay); vrOverlay->SetOverlayWidthInMeters(minSpecFailedOverlay, 1.4f); vrOverlay->SetOverlayInputMethod(minSpecFailedOverlay, vr::VROverlayInputMethod_Mouse); vrOverlay->ShowOverlay(minSpecFailedOverlay); diff --git a/prebuild.py b/prebuild.py index 03677f21d7..cc315a49a4 100644 --- a/prebuild.py +++ b/prebuild.py @@ -94,6 +94,8 @@ def parse_args(): parser.add_argument('--force-build', action='store_true') parser.add_argument('--release-type', type=str, default="DEV", help="DEV, PR, or PRODUCTION") parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution') + parser.add_argument('--vcpkg-build-type', type=str, help='Could be `release` or `debug`. By default it doesn`t set the build-type') + parser.add_argument('--vcpkg-skip-clean', action='store_true', help='Skip the cleanup of vcpkg downloads and packages folders after vcpkg build completition.') parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build') parser.add_argument('--ports-path', type=str, default=defaultPortsPath) parser.add_argument('--ci-build', action='store_true', default=os.getenv('CI_BUILD') is not None) @@ -113,6 +115,7 @@ def main(): del os.environ[var] args = parse_args() + assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS') if args.ci_build: logging.basicConfig(datefmt='%H:%M:%S', format='%(asctime)s %(guid)s %(message)s', level=logging.INFO) @@ -125,7 +128,7 @@ def main(): if 'Windows' == system and 'CI_BUILD' in os.environ and os.environ["CI_BUILD"] == "Github": logger.info("Downloading NSIS") with timer('NSIS'): - hifi_utils.downloadAndExtract('https://athena-public.s3.amazonaws.com/dependencies/NSIS-hifi-plugins-1.0.tgz', "C:/Program Files (x86)") + hifi_utils.downloadAndExtract(assets_url + '/dependencies/NSIS-hifi-plugins-1.0.tgz', "C:/Program Files (x86)") qtInstallPath = '' # If not android, install our Qt build @@ -138,6 +141,11 @@ def main(): qt.writeConfig() pm = hifi_vcpkg.VcpkgRepo(args) + if qtInstallPath != '': + pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath) + + # Only allow one instance of the program to run at a time + if qtInstallPath != '': pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath) @@ -160,7 +168,7 @@ def main(): pm.setupDependencies(qt=qtInstallPath) # wipe out the build directories (after writing the tag, since failure - # here shouldn't invalidte the vcpkg install) + # here shouldn't invalidate the vcpkg install) with timer('Cleaning builds'): pm.cleanBuilds() @@ -175,6 +183,13 @@ def main(): qtPath = os.path.join(pm.androidPackagePath, 'qt') hifi_android.QtPackager(appPath, qtPath).bundle() + # Fixup the vcpkg cmake to not reset VCPKG_TARGET_TRIPLET + pm.fixupCmakeScript() + + if not args.vcpkg_skip_clean: + # Cleanup downloads and packages folders in vcpkg to make it smaller for CI + pm.cleanupDevelopmentFiles() + # Write the vcpkg config to the build directory last with timer('Writing configuration'): pm.writeConfig() diff --git a/script-archive/afk.js b/script-archive/afk.js index 5977c6384a..46441b8886 100644 --- a/script-archive/afk.js +++ b/script-archive/afk.js @@ -6,7 +6,7 @@ // Copyright 2015 High Fidelity, Inc. // kevintown.net // -// JavaScript for the High Fidelity interface that creates an away from keyboard functionality by providing a UI and keyPressEvent which will mute toggle the connected microphone, face tracking dde and set the avatar to a hand raise pose. +// JavaScript for the Vircadia interface that creates an away from keyboard functionality by providing a UI and keyPressEvent which will mute toggle the connected microphone, face tracking dde and set the avatar to a hand raise pose. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/script-archive/tests/performance/renderableMatrix.js b/script-archive/tests/performance/renderableMatrix.js index 04946328cc..2108e35519 100644 --- a/script-archive/tests/performance/renderableMatrix.js +++ b/script-archive/tests/performance/renderableMatrix.js @@ -114,7 +114,7 @@ Script.setInterval(function () { if (isModel) { properties.modelURL = type; } else if (type === 'Web') { - properties.sourceUrl = 'https://projectathena.io'; + properties.sourceUrl = 'https://vircadia.com'; } else { properties.color = { red: x / ROWS_X * 255, green: y / ROWS_Y * 255, blue: z / ROWS_Z * 255 }; if (type === 'ParticleEffect') { diff --git a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js index 1232bfc843..f518701eb1 100644 --- a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js +++ b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonEntityScript.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 1 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 1 entity script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js index 31d5e00e00..921c0508ee 100644 --- a/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js +++ b/script-archive/tutorials/fireworks/chapter1/fireworksLaunchButtonSpawner.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 1 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 1 interface script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js index 66d2e96858..ba16e8af5b 100644 --- a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js +++ b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonEntityScript.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 2 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 2 entity script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js index 19fd67f6c4..e55e7f9d43 100644 --- a/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js +++ b/script-archive/tutorials/fireworks/chapter2/fireworksLaunchButtonSpawner.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 2 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 2 interface script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js index f811d95315..faccec5b5c 100644 --- a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js +++ b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonEntityScript.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 3 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 3 entity script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js index 6b77b2609a..0cde1cbd9c 100644 --- a/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js +++ b/script-archive/tutorials/fireworks/chapter3/fireworksLaunchButtonSpawner.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 3/7/2016 // Copyright 2016 High Fidelity, Inc. // -// This is the chapter 3 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial) +// This is the chapter 3 interface script of the fireworks tutorial (https://docs.vircadia.dev/docs/fireworks-scripting-tutorial) // // Distributed under the Apache License, Version 2.0. diff --git a/scripts/communityModules/chat/FloofChat.js b/scripts/communityModules/chat/FloofChat.js index 905dbeaafa..92382c3199 100644 --- a/scripts/communityModules/chat/FloofChat.js +++ b/scripts/communityModules/chat/FloofChat.js @@ -53,7 +53,7 @@ var settingsRoot = "FloofChat"; var athenaGotoUrl = "https://metaverse.projectathena.io/interim/d-goto/app/goto.json"; var gotoJSONUrl = Settings.getValue(settingsRoot + "/gotoJSONUrl", athenaGotoUrl); -var muted = Settings.getValue(settingsRoot + "/muted", {"Local": false, "Domain": false, "Grid": false}); +var muted = Settings.getValue(settingsRoot + "/muted", {"Local": false, "Domain": false, "Grid": true}); var ws; var wsReady = false; diff --git a/scripts/communityScripts/explore/explore.html b/scripts/communityScripts/explore/explore.html index affaf0887f..6914b062c9 100644 --- a/scripts/communityScripts/explore/explore.html +++ b/scripts/communityScripts/explore/explore.html @@ -73,6 +73,9 @@

Explore

+ + + @@ -85,15 +88,36 @@ + diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 8c048cc0cc..08c1f14e6d 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -9,7 +9,7 @@ // // Created by Dante Ruiz on 8 February 2017 // Copyright 2016 High Fidelity, Inc. -// Copyright 2020 Project Athena contributors. +// 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 diff --git a/server-console/package.json b/server-console/package.json index ed01a9c89e..1ceed08d4d 100644 --- a/server-console/package.json +++ b/server-console/package.json @@ -1,7 +1,7 @@ { "name": "VircadiaConsole", "description": "Vircadia Console", - "author": "High Fidelity", + "author": "Vircadia", "license": "Apache-2.0", "version": "1.0.0", "keywords": [ diff --git a/server-console/resources/console-beta.icns b/server-console/resources/console-beta.icns index 1fe162f88e..9044dbfbf7 100644 Binary files a/server-console/resources/console-beta.icns and b/server-console/resources/console-beta.icns differ diff --git a/server-console/resources/console-beta.ico b/server-console/resources/console-beta.ico index 63a3eff482..22cd370d72 100644 Binary files a/server-console/resources/console-beta.ico and b/server-console/resources/console-beta.ico differ diff --git a/server-console/resources/console-beta.png b/server-console/resources/console-beta.png index 59fe03c8ee..b7c6ad4889 100644 Binary files a/server-console/resources/console-beta.png and b/server-console/resources/console-beta.png differ diff --git a/server-console/resources/console-notification.png b/server-console/resources/console-notification.png index e8991cd773..80fc9dd3a9 100644 Binary files a/server-console/resources/console-notification.png and b/server-console/resources/console-notification.png differ diff --git a/server-console/resources/console-tray-osx-stopped.png b/server-console/resources/console-tray-osx-stopped.png index 82fcf76d5c..e8df123f20 100644 Binary files a/server-console/resources/console-tray-osx-stopped.png and b/server-console/resources/console-tray-osx-stopped.png differ diff --git a/server-console/resources/console-tray-osx-stopped@2x.png b/server-console/resources/console-tray-osx-stopped@2x.png index 535e6b8135..3671365aa1 100644 Binary files a/server-console/resources/console-tray-osx-stopped@2x.png and b/server-console/resources/console-tray-osx-stopped@2x.png differ diff --git a/server-console/resources/console-tray-osx-stopping.png b/server-console/resources/console-tray-osx-stopping.png index c99313d00b..fe8a1932bd 100644 Binary files a/server-console/resources/console-tray-osx-stopping.png and b/server-console/resources/console-tray-osx-stopping.png differ diff --git a/server-console/resources/console-tray-osx-stopping@2x.png b/server-console/resources/console-tray-osx-stopping@2x.png index de09b722d3..1982102e5d 100644 Binary files a/server-console/resources/console-tray-osx-stopping@2x.png and b/server-console/resources/console-tray-osx-stopping@2x.png differ diff --git a/server-console/resources/console-tray-osx.png b/server-console/resources/console-tray-osx.png index 36756dd16f..13dd30430c 100644 Binary files a/server-console/resources/console-tray-osx.png and b/server-console/resources/console-tray-osx.png differ diff --git a/server-console/resources/console-tray-osx@2x.png b/server-console/resources/console-tray-osx@2x.png index 86d9eacea2..f8636fdd29 100644 Binary files a/server-console/resources/console-tray-osx@2x.png and b/server-console/resources/console-tray-osx@2x.png differ diff --git a/server-console/resources/console-tray-stopped.ico b/server-console/resources/console-tray-stopped.ico index 2f0251a185..d1f48a8862 100644 Binary files a/server-console/resources/console-tray-stopped.ico and b/server-console/resources/console-tray-stopped.ico differ diff --git a/server-console/resources/console-tray-win-stopped.png b/server-console/resources/console-tray-win-stopped.png index cedc6e02e1..07db771652 100644 Binary files a/server-console/resources/console-tray-win-stopped.png and b/server-console/resources/console-tray-win-stopped.png differ diff --git a/server-console/resources/console-tray-win-stopped@2x.png b/server-console/resources/console-tray-win-stopped@2x.png index b176209758..81f4fd4f10 100644 Binary files a/server-console/resources/console-tray-win-stopped@2x.png and b/server-console/resources/console-tray-win-stopped@2x.png differ diff --git a/server-console/resources/console-tray-win-stopping.png b/server-console/resources/console-tray-win-stopping.png index ddf60efc8e..30ab381158 100644 Binary files a/server-console/resources/console-tray-win-stopping.png and b/server-console/resources/console-tray-win-stopping.png differ diff --git a/server-console/resources/console-tray-win-stopping@2x.png b/server-console/resources/console-tray-win-stopping@2x.png index 50f9df457f..a72cdc1209 100644 Binary files a/server-console/resources/console-tray-win-stopping@2x.png and b/server-console/resources/console-tray-win-stopping@2x.png differ diff --git a/server-console/resources/console-tray-win.png b/server-console/resources/console-tray-win.png index cd8fa1d691..1ed710c880 100644 Binary files a/server-console/resources/console-tray-win.png and b/server-console/resources/console-tray-win.png differ diff --git a/server-console/resources/console-tray-win@2x.png b/server-console/resources/console-tray-win@2x.png index 142498f6a1..abe7bb0b93 100644 Binary files a/server-console/resources/console-tray-win@2x.png and b/server-console/resources/console-tray-win@2x.png differ diff --git a/server-console/resources/console-tray.ico b/server-console/resources/console-tray.ico index 6f06b43f73..cf25b69083 100644 Binary files a/server-console/resources/console-tray.ico and b/server-console/resources/console-tray.ico differ diff --git a/server-console/resources/console.icns b/server-console/resources/console.icns index 1fe162f88e..4e4df39d69 100644 Binary files a/server-console/resources/console.icns and b/server-console/resources/console.icns differ diff --git a/server-console/resources/console.ico b/server-console/resources/console.ico index 63a3eff482..34b48a029d 100644 Binary files a/server-console/resources/console.ico and b/server-console/resources/console.ico differ diff --git a/server-console/resources/console.png b/server-console/resources/console.png index e6e13081fd..677ce293c4 100644 Binary files a/server-console/resources/console.png and b/server-console/resources/console.png differ diff --git a/tools/ci-scripts/upload_to_publish_server.py b/tools/ci-scripts/upload_to_publish_server.py new file mode 100644 index 0000000000..5e1a9b24f2 --- /dev/null +++ b/tools/ci-scripts/upload_to_publish_server.py @@ -0,0 +1,77 @@ +import os +import json +from hashlib import sha256 +import http.client +from http import HTTPStatus +import time +import struct +import random +import glob + +FILE_READ_BUFFER = 4096 + +path = os.path.join(os.getcwd(), os.environ['ARTIFACT_PATTERN']) +files = glob.glob(path, recursive=False) +uploading_files = [] +for archive_file in files: + file = open(archive_file, 'rb') + sha256_hash = sha256() + file.seek(0, 0) + for byte_block in iter(lambda: file.read(FILE_READ_BUFFER), b""): + sha256_hash.update(byte_block) + + checksum = sha256_hash.hexdigest() + + uploading_files.append({ + "filename": os.path.basename(archive_file), + "sha256_checksum": checksum, + "file_length": file.tell() + }) + file.close() + +print("BuildFileHashes: " + json.dumps(uploading_files)) + +file_contents = [] +file_sizes = [] + +for archiveFile in files: + file = open(archiveFile, 'rb') + file_data = file.read() + file_sizes.append(len(file_data)) + file_contents.append(file_data) + file.close() + +conn = http.client.HTTPSConnection("build-uploader.vircadia.com") + +context = json.loads(os.environ['GITHUB_CONTEXT']) + +owner_and_repository = context["repository"].split("/") +owner = owner_and_repository[0] +repository = owner_and_repository[1] + +headers = { + "owner": owner, + "repo": repository, + "commit_hash": context["event"]["pull_request"]["head"]["sha"], + "pull_number": context["event"]["number"], + "job_name": os.environ["JOB_NAME"], + "run_id": context["run_id"], + "file_sizes": ','.join(str(e) for e in file_sizes) +} + +concat_file_body = b''.join(file_contents) + +print("Total files size: " + str(len(concat_file_body))) + +conn.request("PUT", "/", body=concat_file_body, headers=headers) +response = conn.getresponse() + +EXIT_CODE_OK = 0 +EXIT_CODE_ERROR = 1 + +if (response.status == HTTPStatus.OK): + print("response: ", json.loads(response.read())) + exit(EXIT_CODE_OK) +else: + print(response.status, response.reason, response.read()) + exit(EXIT_CODE_ERROR) diff --git a/tools/oven/src/BakerCLI.h b/tools/oven/src/BakerCLI.h index bf33b625dd..d9b29cf6bd 100644 --- a/tools/oven/src/BakerCLI.h +++ b/tools/oven/src/BakerCLI.h @@ -34,7 +34,7 @@ public: BakerCLI(OvenCLIApplication* parent); public slots: - void bakeFile(QUrl inputUrl, const QString& outputPath, const QString& type = QString::null); + void bakeFile(QUrl inputUrl, const QString& outputPath, const QString& type = QString()); private slots: void handleFinishedBaker(); diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp index 5b0de4d6fa..6d3a8912ab 100644 --- a/tools/oven/src/OvenCLIApplication.cpp +++ b/tools/oven/src/OvenCLIApplication.cpp @@ -85,7 +85,7 @@ void OvenCLIApplication::parseCommandLine(int argc, char* argv[]) { _inputUrlParameter = QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER)); _outputUrlParameter = QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER)); - _typeParameter = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null; + _typeParameter = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString(); if (parser.isSet(CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER)) { qDebug() << "Disabling texture compression"; diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index da20339123..a5ad5bc891 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -149,11 +149,12 @@ void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOff result.vertices << p3; // add the new point to the result mesh HFMMeshPart newMeshPart; + setMeshPartDefaults(newMeshPart, "unknown"); newMeshPart.triangleIndices << index0 << index1 << index2; newMeshPart.triangleIndices << index0 << index3 << index1; newMeshPart.triangleIndices << index1 << index3 << index2; newMeshPart.triangleIndices << index2 << index3 << index0; - result.parts.push_back(newMeshPart); + result.parts.append(newMeshPart); } } @@ -258,8 +259,8 @@ void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& res VHACD::IVHACD::ConvexHull hull; convexifier->GetConvexHull(j, hull); - resultMesh.parts.push_back(HFMMeshPart()); - HFMMeshPart& resultMeshPart = resultMesh.parts.back(); + resultMesh.parts.append(HFMMeshPart()); + HFMMeshPart& resultMeshPart = resultMesh.parts.last(); int hullIndexStart = resultMesh.vertices.size(); resultMesh.vertices.reserve(hullIndexStart + hull.m_nPoints); @@ -299,8 +300,8 @@ bool vhacd::VHACDUtil::computeVHACD(HFMModel& hfmModel, } // count the mesh-parts - size_t numParts = 0; - for (const HFMMesh& mesh : hfmModel.meshes) { + int numParts = 0; + foreach (const HFMMesh& mesh, hfmModel.meshes) { numParts += mesh.parts.size(); } if (_verbose) { @@ -310,8 +311,8 @@ bool vhacd::VHACDUtil::computeVHACD(HFMModel& hfmModel, VHACD::IVHACD * convexifier = VHACD::CreateVHACD(); result.meshExtents.reset(); - result.meshes.push_back(HFMMesh()); - HFMMesh &resultMesh = result.meshes.back(); + result.meshes.append(HFMMesh()); + HFMMesh &resultMesh = result.meshes.last(); const uint32_t POINT_STRIDE = 3; const uint32_t TRIANGLE_STRIDE = 3; @@ -347,7 +348,7 @@ bool vhacd::VHACDUtil::computeVHACD(HFMModel& hfmModel, if (_verbose) { qDebug() << "mesh" << meshIndex << ": " - << " parts =" << mesh.parts.size() + << " parts =" << mesh.parts.size() << " clusters =" << mesh.clusters.size() << " vertices =" << numVertices; } ++meshIndex; diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index 61a6b38181..3d675f8baf 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -387,7 +387,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } if (verbose) { - auto totalHulls = result.meshes[0].parts.size(); + int totalHulls = result.meshes[0].parts.size(); qDebug() << "output file =" << outputFilename; qDebug() << "vertices =" << totalVertices; qDebug() << "triangles =" << totalTriangles; @@ -402,7 +402,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : HFMMesh result; // count the mesh-parts - size_t meshCount = 0; + unsigned int meshCount = 0; foreach (const HFMMesh& mesh, fbx.meshes) { meshCount += mesh.parts.size(); } @@ -412,7 +412,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : vUtil.fattenMesh(mesh, fbx.offset, result); } - newFbx.meshes.push_back(result); + newFbx.meshes.append(result); writeOBJ(outputFilename, newFbx, outputCentimeters); } }
ValueNameDescription
Invite Steam friends to join you in High Fidelity.Invite Steam friends to join you in Vircadia.Display the High Fidelity home page on the system tablet.Display the Vircadia home page on the system tablet.