diff --git a/.eslintrc.js b/.eslintrc.js index 67921be395..9900825b23 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,6 +36,7 @@ module.exports = { "GlobalServices": false, "GooglePoly": false, "Graphics": false, + "HifiAbout": false, "HMD": false, "LaserPointers": false, "location": true, diff --git a/BUILD.md b/BUILD.md index df3f18cf51..00b17743e9 100644 --- a/BUILD.md +++ b/BUILD.md @@ -9,14 +9,12 @@ - [cmake](https://cmake.org/download/): 3.9 - [Qt](https://www.qt.io/download-open-source): 5.10.1 -- [OpenSSL](https://www.openssl.org/): Use the latest available 1.0 version (**NOT** 1.1) of OpenSSL to avoid security vulnerabilities. -- [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) +- [Python](https://www.python.org/downloads/): 3.6 or higher ### CMake External Project Dependencies These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required. - [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83 -- [GLEW](http://glew.sourceforge.net/): 1.13 - [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) @@ -24,16 +22,15 @@ These dependencies need not be installed manually. They are automatically downlo - [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3 - [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3 - [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3 -- [Sixense](http://sixense.com/): 071615 +- [vcpkg](https://github.com/highfidelity/vcpkg): +- [VHACD](https://github.com/virneo/v-hacd) - [zlib](http://www.zlib.net/): 1.28 (Win32 only) -- nVidia Texture Tools: 2.1 +- [nvtt](https://github.com/highfidelity/nvidia-texture-tools): 2.1.1 (customized) The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project. These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder. -If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE\_LOCAL\_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project. - #### CMake Hifi uses CMake to generate build files and project files for your platform. @@ -81,9 +78,22 @@ In the examples below the variable $NAME would be replaced by the name of the de * $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' - ### Optional Components +#### Build Options + +The following build options can be used when running CMake + +* BUILD_CLIENT +* BUILD_SERVER +* BUILD_TESTS +* BUILD_TOOLS + +#### Developer Build Options + +* USE_GLES +* DISABLE_UI + #### Devices You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 1ee3d2b7c8..15c5915c51 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -40,6 +40,11 @@ Install build tools: sudo apt-get install cmake ``` +Install Python 3: +```bash +sudo apt-get install python3.6 +``` + ### Get code and checkout the tag you need diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 62102b3e18..488c38e909 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -6,6 +6,10 @@ Please read the [general build guide](BUILD.md) for information on dependencies brew install cmake openssl qt +### Python 3 + +Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/). Execute the `Update Shell Profile.command` script that is provided with the installer. + ### OpenSSL Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. @@ -28,7 +32,9 @@ Note that this uses the version from the homebrew formula at the time of this wr If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. - cmake .. -GXcode + cmake .. -G Xcode + +If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally instaled CMake After running cmake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 90d2995e7d..073b048911 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -5,11 +5,17 @@ Note: We are now using Visual Studio 2017 and Qt 5.10.1. If you are upgrading fr Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory. -### Step 1. Visual Studio 2017 +### Step 1. Visual Studio 2017 & Python If you don’t have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/). -When selecting components, check "Desktop development with C++." Also on the right on the Summary toolbar, check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)". +When selecting components, check "Desktop development with C++". Also on the right on the Summary toolbar, check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)". If you do not already have a python development environment installed, also check "Python Development" in this screen. + +If you already have Visual Studio installed and need to add python, open the "Add or remove programs" control panel and find the "Microsoft Visual Studio Installer". Select it and click "Modify". In the installer, select "Modify" again, then check "Python Development" and allow the installer to apply the changes. + +### Step 1a. Alternate Python + +If you do not wish to use the Python installation bundled with Visual Studio, you can download the installer from [here](https://www.python.org/downloads/). Ensure you get version 3.6.6 or higher. ### Step 2. Installing CMake diff --git a/CMakeLists.txt b/CMakeLists.txt index a887a35fcc..4e5dbe935a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,17 +7,36 @@ else() cmake_minimum_required(VERSION 3.2) endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros/TargetPython.cmake") +target_python() + +if (HIFI_ANDROID ) + execute_process( + COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --android --build-root ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +else() + execute_process( + COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --build-root ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + # squelch the Policy CMP0074 warning without requiring an update to cmake 3.12. + if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) + cmake_policy(SET CMP0074 NEW) + endif() +endif() + +if(NOT EXISTS "${CMAKE_BINARY_DIR}/vcpkg.cmake") + message(FATAL_ERROR "vcpkg configuration missing.") +endif() + +include("${CMAKE_BINARY_DIR}/vcpkg.cmake") project(hifi) - include("cmake/init.cmake") - include("cmake/compiler.cmake") -if (BUILD_SCRIBE_ONLY) - add_subdirectory(tools/scribe) - add_subdirectory(tools/shader_reflect) - return() -endif() +add_paths_to_fixup_libs(${VCPKG_INSTALL_ROOT}/bin) +add_paths_to_fixup_libs(${VCPKG_INSTALL_ROOT}/debug/bin) if (NOT DEFINED CLIENT_ONLY) set(CLIENT_ONLY 0) @@ -35,7 +54,8 @@ endif() set(BUILD_CLIENT_OPTION ON) set(BUILD_SERVER_OPTION ON) -set(BUILD_TESTS_OPTION ON) +set(BUILD_TESTS_OPTION OFF) +set(BUILD_MANUAL_TESTS_OPTION ${BUILD_TESTS_OPTION}) set(BUILD_TOOLS_OPTION ON) set(BUILD_INSTALLER_OPTION ON) set(GLES_OPTION OFF) @@ -61,7 +81,7 @@ if (ANDROID) set(GLES_OPTION ON) set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) else () - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) + set(PLATFORM_QT_COMPONENTS WebEngine) endif () if (USE_GLES AND (NOT ANDROID)) @@ -72,6 +92,7 @@ endif() option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION}) option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION}) option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION}) +option(BUILD_MANUAL_TESTS "Build manual tests" ${BUILD_MANUAL_TESTS_OPTION}) option(BUILD_TOOLS "Build tools" ${BUILD_TOOLS_OPTION}) option(BUILD_INSTALLER "Build installer" ${BUILD_INSTALLER_OPTION}) option(USE_GLES "Use OpenGL ES" ${GLES_OPTION}) @@ -139,6 +160,8 @@ list(APPEND CMAKE_PREFIX_PATH "${QT_CMAKE_PREFIX_PATH}") find_package( Threads ) add_definitions(-DGLM_FORCE_RADIANS) +add_definitions(-DGLM_ENABLE_EXPERIMENTAL) +add_definitions(-DGLM_FORCE_CTOR_INIT) set(HIFI_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries") set(EXTERNAL_PROJECT_PREFIX "project") @@ -186,7 +209,9 @@ if (BUILD_TESTS) include(CTest) enable_testing() add_subdirectory(tests) - add_subdirectory(tests-manual) + if (BUILD_MANUAL_TESTS) + add_subdirectory(tests-manual) + endif() endif() if (BUILD_INSTALLER) diff --git a/LICENSE b/LICENSE index 60e86a1cc7..deb80afc19 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2016, High Fidelity, Inc. +Copyright (c) 2013-2018, High Fidelity, Inc. All rights reserved. licensing@highfidelity.io diff --git a/VCPKG.md b/VCPKG.md new file mode 100644 index 0000000000..c426dc618f --- /dev/null +++ b/VCPKG.md @@ -0,0 +1,75 @@ +[VCPKG](https://github.com/Microsoft/vcpkg) is an open source package management system created by Microsoft, intially just for Windows based system, but eventually extended to cover Linux and OSX as well, and in theory extensible enough to cover additional operating systems. + +VCPKG is now our primary mechanism for managing the external libraries and tools on which we rely to build our applications. + +Conventional usage of VCPKG involves cloning the repository, running the bootstrapping script to build the vcpkg binary, and then calling the binary to install a set of libraries. The libraries themselves are specified by a set of port files inside the [repository](https://github.com/Microsoft/vcpkg/tree/master/ports) + +Because the main VCPKG repository does not contain all the ports we want, and because we want to be able to manage the precise versions of our dependencies, rather than allow it to be outside of our control, instead of using the main vcpkg repository, we use a combination of a [fork](https://github.com/highfidelity/vcpkg) of the repository (which allows us to customize the vcpkg binary, currently necessary to deal with some out of date tools on our build hosts) and a set of [custom port files](./cmake/ports) stored in our own repository. + +## Adding new packages to vcpkg + +Note... Android vcpkg usage is still experimental. Contact Austin for more detailed information if you need to add a new package for use by Android. + +### Setup development environment + +In order to add new packages, you will need to set up an environment for testing. This assumes you already have the tools for normal Hifi development (git, cmake, a working C++ compiler, etc) + +* Clone our vcpkg [fork](https://github.com/highfidelity/vcpkg) +* Remove the ports directory from the checkout and symlink to our own [custom port files](./cmake/ports) +* Bootstrap the vcpkg binary with the `bootstrap-vcpkg.sh` or `bootstrap-vcpkg.bat` script + +### Add a new port skeleton + +Your new package will require, at minimum, a `CONTROL` file and a `portfile.cmake` file, located in a subdirectory of the ports folder. Assuming you're creating a new dependency named `foo` it should be located in `ports/foo` under the vcpkg directory. The `CONTROL` file will contain a small number of fields, such as the name, version, description and any other vcpkg ports on which you depend. The `portfile.cmake` is a CMake script that will instruct vcpkg how to build the packages. We'll cover that in more depth in a moment. For now, just create one and leave it blank. + +### Add a reference to your package to one or more of the hifi meta-packages + +We have three meta-packages used to do our building. When you modify one of these packages, make sure to bump the version number in the `CONTROL` file for the package + +#### hifi-deps + +This metapackage contains anything required for building the server or shared components. For instance, the `glm`, `tbb` and `zlib` packages are declared here because they're used everywhere, not just in our client application code. + +#### hifi-client-deps + +This metapackage contains anything required for building the client. For example, `sdl2` is listed here because it's required for our joystick input, but not for the server or shared components. Note that `hifi-client-deps` depends on `hifi-deps`, so you don't have to declare something twice if it's used in both he server and client. Just declare it in `hifi-deps` and it will still be includeded transitively. + +#### hifi-host-tools + +This metapackage contains anything we use to create executables that will then be used in the build process. The `hifi-deps` and `hifi-client-deps` packages are built for the target architecture, which may be different than the host architecture (for instance, when building for Android). The `hifi-host-tools` packages are always build for the host architecture, because they're tools that are intended to be run as part of the build process. Scribe for example is used at build time to generate shaders. Building an arm64 version of Scribe is useless because we need to run it on the host machine. + +Note that packages can appear in both the `hifi-host-tools` and one of the other metapackages, indicating that the package both contains a library which we will use at runtime, and a tool which we will use at build time. The `spirv-tools` package is an example. + +### Implement the portfile.cmake + +How the portfile is written depends on what kind of package you're working with. It's basically still a CMake script, but there are a number of [functions](https://vcpkg.readthedocs.io/en/latest/maintainers/portfile-functions/) available to make fetching and building packages easier. + +Typically there are three areas you need to deal with + +* Getting the source +* Building the source +* Installing the artifacts + +#### Getting sources + +Getting sources from github, gitlab or bitbucket is easy. There are special functions specifcially for those. See the [etc2comp portfile](./cmake/ports/etc2comp/portfile.cmake) for an example of fetching source via github. + +If the project isn't available that way, you can still use the [vcpkg_download_distfile](https://vcpkg.readthedocs.io/en/latest/maintainers/vcpkg_download_distfile/) function to explicitly download an archive and then use [vcpkg_extract_source_archive](https://vcpkg.readthedocs.io/en/latest/maintainers/vcpkg_extract_source_archive/) to unpack it. See the [zlib portfile](./cmake/ports/zlib/portfile.cmake) for an example there. + +#### Building + +If your package uses CMake, you'll be able to use the [vcpkg_configure_cmake](https://vcpkg.readthedocs.io/en/latest/maintainers/vcpkg_configure_cmake/) and [vcpkg_build_cmake](https://vcpkg.readthedocs.io/en/latest/maintainers/vcpkg_build_cmake/) commands to configure and build the package. If you're going to be relying on the CMake installation functionality, you can just call [vcpkg_install_cmake](https://vcpkg.readthedocs.io/en/latest/maintainers/vcpkg_install_cmake/), since it will implicitly run the build before the install. + +If your package is not binary, but doesn't use CMake, you're just going to have to figure it out. + +If your package is binary, then you can just skip this step + +#### Installing + +Once you've built the package, you need to install the artifacts in the target directory. Ideally, your package's CMake INSTALL commands will do the right thing. However, there are usually some things you have to do manually. Since VCPKG will build both the release and debug versions for all packages, you need to make sure if your package installed headers that you remove the _debug_ versions of these headers. This is typically done with the `file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)`. Additionally, if your package creates any standalone executables, you need to make sure they're installed in the destination `tools` directory, not the `bin` or `lib` directories, which are specifically for shared library binaries (like .so or .dll files) and link library files (like .a or .lib files) respectively. + +If you're dealing with a binary package, then you'll need to explicitly perform all the required copies from the location where you extracted the archive to the installation directory. An example of this is available in the [openssl-android portfile](./cmake/ports/openssl-android/portfile.cmake) + +### Commit and test + +Once you've tested building your new package locally, you'll need to commit and push the changes and additions to the portfiles you've made and then monitor the build hosts to verify that the new package successfully built on all the target environments. diff --git a/android/app/build.gradle b/android/app/build.gradle index 76f5acfaea..c04bf37438 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -23,8 +23,6 @@ android { '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_shared', '-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake', - '-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe' + EXEC_SUFFIX, - '-DNATIVE_SHREFLECT=' + HIFI_ANDROID_PRECOMPILED + '/shreflect' + EXEC_SUFFIX, '-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED, '-DRELEASE_NUMBER=' + RELEASE_NUMBER, '-DRELEASE_TYPE=' + RELEASE_TYPE, diff --git a/android/build.gradle b/android/build.gradle index 14f9e4803f..e22c2d877f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -106,11 +106,6 @@ def packages = [ versionId: 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY', checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', ], - glm: [ - file: 'glm-0.9.8.5-patched.tgz', - versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ', - checksum: '067b5fe16b220b5b1a1039ba51b062ae', - ], gvr: [ file: 'gvrsdk_v1.101.0.tgz', versionId: 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r', @@ -161,33 +156,6 @@ def packages = [ ] ] - -def scribeLocalFile='scribe' + EXEC_SUFFIX -def scribeFile='scribe_linux_x86_64' -def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6' -def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G' - -def shreflectLocalFile='shreflect' + EXEC_SUFFIX -def shreflectFile='shreflect_linux_x86_64' -def shreflectChecksum='d6094a8580066c0b6f4e80b5adfb1d98' -def shreflectVersion='jnrpudh6fptIg6T2.Z6fgKP2ultAdKmE' - -if (Os.isFamily(Os.FAMILY_MAC)) { - scribeFile = 'scribe_osx_x86_64' - scribeChecksum='72db9d32d4e1e50add755570ac5eb749' - scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC' - shreflectFile='shreflect_osx_x86_64' - shreflectChecksum='d613ef0703c21371fee93fd2e54b964f' - shreflectVersion='.rYNzjSFq6WtWDnE5KIKRIAGyJtr__ad' -} else if (Os.isFamily(Os.FAMILY_WINDOWS)) { - scribeFile = 'scribe_win32_x86_64.exe' - scribeChecksum='678e43d290c90fda670c6fefe038a06d' - scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7' - shreflectFile='shreflect_win32_x86_64.exe' - shreflectChecksum='6f4a77b8cceb3f1bbc655132c3665060' - shreflectVersion='iIyCyza1nelkbI7ihybF59bBlwrfAC3D' -} - def options = [ files: new TreeSet(), features: new HashSet(), @@ -446,44 +414,6 @@ task copyDependencies(dependsOn: [ extractDependencies ]) { } } -task downloadScribe(type: Download) { - src baseUrl + scribeFile + '?versionId=' + scribeVersion - dest new File(baseFolder, scribeLocalFile) - onlyIfNewer true -} - -task verifyScribe (type: Verify, dependsOn: downloadScribe) { - src new File(baseFolder, scribeLocalFile); - checksum scribeChecksum -} - -task fixScribePermissions(type: Exec, dependsOn: verifyScribe) { - commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + scribeLocalFile -} - -task downloadShreflect(type: Download) { - src baseUrl + shreflectFile + '?versionId=' + shreflectVersion - dest new File(baseFolder, shreflectLocalFile) - onlyIfNewer true -} - -task verifyShreflect(type: Verify, dependsOn: downloadShreflect) { - src new File(baseFolder, shreflectLocalFile); - checksum shreflectChecksum -} - -task fixShreflectPermissions(type: Exec, dependsOn: verifyShreflect) { - commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + shreflectLocalFile -} - -task setupScribe(dependsOn: [verifyScribe, verifyShreflect]) { } - -// On Windows, we don't need to set the executable bit, but on OSX and Unix we do -if (!Os.isFamily(Os.FAMILY_WINDOWS)) { - setupScribe.dependsOn fixScribePermissions - setupScribe.dependsOn fixShreflectPermissions -} - task extractGvrBinaries(dependsOn: extractDependencies) { doLast { def gvrLibFolder = new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries'); @@ -570,7 +500,7 @@ task qtBundle { } } -task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBinaries, qtBundle]) { } +task setupDependencies(dependsOn: [copyDependencies, extractGvrBinaries, qtBundle]) { } task cleanDependencies(type: Delete) { delete HIFI_ANDROID_PRECOMPILED diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000000..ac639c5ae7 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xms2g -Xmx4g diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 639e9f924b..d9a399c162 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -216,13 +216,14 @@ void Agent::requestScript() { } // make sure this is not a script request for the file scheme - if (scriptURL.scheme() == URL_SCHEME_FILE) { + if (scriptURL.scheme() == HIFI_URL_SCHEME_FILE) { qWarning() << "Cannot load script for Agent from local filesystem."; scriptRequestFinished(); return; } - auto request = DependencyManager::get()->createResourceRequest(this, scriptURL); + auto request = DependencyManager::get()->createResourceRequest( + this, scriptURL, true, -1, "Agent::requestScript"); if (!request) { qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString(); @@ -368,11 +369,7 @@ void Agent::executeScript() { // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); - scriptedAvatar->setID(getSessionUUID()); - - connect(_scriptEngine.data(), SIGNAL(update(float)), - scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(true); // call model URL setters with empty URLs so our avatar, if user, will have the default models @@ -504,8 +501,6 @@ void Agent::executeScript() { DependencyManager::set(_entityViewer.getTree()); - _avatarAudioTimer.start(); - // Agents should run at 45hz static const int AVATAR_DATA_HZ = 45; static const int AVATAR_DATA_IN_MSECS = MSECS_PER_SECOND / AVATAR_DATA_HZ; @@ -530,7 +525,8 @@ void Agent::executeScript() { } avatarDataTimer->stop(); - _avatarAudioTimer.stop(); + + setIsAvatar(false); // will stop timers for sending identity packets } setFinished(true); @@ -582,28 +578,33 @@ void Agent::setIsAvatar(bool isAvatar) { } _isAvatar = isAvatar; - if (_isAvatar && !_avatarIdentityTimer) { - // set up the avatar timers - _avatarIdentityTimer = new QTimer(this); - _avatarQueryTimer = new QTimer(this); + auto scriptableAvatar = DependencyManager::get(); + if (_isAvatar) { + if (!_avatarIdentityTimer) { + // set up the avatar timers + _avatarIdentityTimer = new QTimer(this); + _avatarQueryTimer = new QTimer(this); - // connect our slot - connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket); - connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars); + // connect our slot + connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket); + connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars); - static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; - static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000; + static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; + static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000; - // start the timers - _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets - _avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS); + // start the timers + _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets + _avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS); - // tell the avatarAudioTimer to start ticking - QMetaObject::invokeMethod(&_avatarAudioTimer, "start"); - } + connect(_scriptEngine.data(), &ScriptEngine::update, + scriptableAvatar.data(), &ScriptableAvatar::update, Qt::QueuedConnection); - if (!_isAvatar) { + // tell the avatarAudioTimer to start ticking + QMetaObject::invokeMethod(&_avatarAudioTimer, "start"); + } + _entityEditSender.setMyAvatar(scriptableAvatar.data()); + } else { if (_avatarIdentityTimer) { _avatarIdentityTimer->stop(); delete _avatarIdentityTimer; @@ -630,14 +631,14 @@ void Agent::setIsAvatar(bool isAvatar) { packet->writePrimitive(KillAvatarReason::NoReason); nodeList->sendPacket(std::move(packet), *node); }); + + disconnect(_scriptEngine.data(), &ScriptEngine::update, + scriptableAvatar.data(), &ScriptableAvatar::update); + + QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); } - QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); - _entityEditSender.setMyAvatar(nullptr); - } else { - auto scriptableAvatar = DependencyManager::get(); - _entityEditSender.setMyAvatar(scriptableAvatar.data()); } } @@ -788,7 +789,7 @@ void Agent::processAgentAvatarAudio() { // seek past the sequence number, will be packed when destination node is known audioPacket->seek(sizeof(quint16)); - if (silentFrame) { + if (silentFrame && !_flushEncoder) { if (!_isListeningToAudioStream) { // if we have a silent frame and we're not listening then just send nothing and break out of here @@ -810,7 +811,7 @@ void Agent::processAgentAvatarAudio() { // no matter what, the loudness should be set to 0 computeLoudness(nullptr, scriptedAvatar); - } else if (nextSoundOutput) { + } else if (nextSoundOutput || _flushEncoder) { // write the codec audioPacket->writeString(_selectedCodecName); @@ -864,8 +865,6 @@ void Agent::processAgentAvatarAudio() { } void Agent::aboutToFinish() { - setIsAvatar(false);// will stop timers for sending identity packets - // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(nullptr); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 2b5ff51b49..7d47c8e713 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 426f3ce6fc..76ff5ab2ed 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -35,6 +35,7 @@ #include "AssignmentClientLogging.h" #include "AssignmentFactory.h" +#include "ResourceRequestObserver.h" const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; @@ -49,6 +50,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); auto addressManager = DependencyManager::set(); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index afd4047c68..d6f893c42e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -89,7 +89,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : PacketType::NodeIgnoreRequest, PacketType::RadiusIgnoreRequest, PacketType::RequestsDomainListData, - PacketType::PerAvatarGainSet }, + PacketType::PerAvatarGainSet, + PacketType::AudioSoloRequest }, this, "queueAudioPacket"); // packets whose consequences are global should be processed on the main thread diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 4545f48c41..9a78ba31a2 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -13,7 +13,7 @@ #include -#include +#include #include #include @@ -98,6 +98,9 @@ int AudioMixerClientData::processPackets(ConcurrentAddedStreams& addedStreams) { case PacketType::RadiusIgnoreRequest: parseRadiusIgnoreRequest(packet, node); break; + case PacketType::AudioSoloRequest: + parseSoloRequest(packet, node); + break; default: Q_UNREACHABLE(); } @@ -202,7 +205,7 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const } } -void AudioMixerClientData::setGainForAvatar(QUuid nodeID, uint8_t gain) { +void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) { auto it = std::find_if(_streams.active.cbegin(), _streams.active.cend(), [nodeID](const MixableStream& mixableStream){ return mixableStream.nodeStreamID.nodeID == nodeID && mixableStream.nodeStreamID.streamID.isNull(); }); @@ -295,6 +298,25 @@ void AudioMixerClientData::parseRadiusIgnoreRequest(QSharedPointer message, const SharedNodePointer& node) { + + uint8_t addToSolo; + message->readPrimitive(&addToSolo); + + while (message->getBytesLeftToRead()) { + // parse out the UUID being soloed from the packet + QUuid soloedUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (addToSolo) { + _soloedNodes.push_back(soloedUUID); + } else { + auto it = std::remove(std::begin(_soloedNodes), std::end(_soloedNodes), soloedUUID); + _soloedNodes.erase(it, std::end(_soloedNodes)); + } + } +} + AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() { auto it = std::find_if(_audioStreams.begin(), _audioStreams.end(), [](const SharedStreamPointer& stream){ return stream->getStreamIdentifier().isNull(); @@ -497,7 +519,7 @@ void AudioMixerClientData::processStreamPacket(ReceivedMessage& message, Concurr if (newStream) { // whenever a stream is added, push it to the concurrent vector of streams added this frame - addedStreams.emplace_back(getNodeID(), getNodeLocalID(), matchingStream->getStreamIdentifier(), matchingStream.get()); + addedStreams.push_back(AddedStream(getNodeID(), getNodeLocalID(), matchingStream->getStreamIdentifier(), matchingStream.get())); } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 610b258789..653749f619 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -65,6 +65,7 @@ public: void parsePerAvatarGainSet(ReceivedMessage& message, const SharedNodePointer& node); void parseNodeIgnoreRequest(QSharedPointer message, const SharedNodePointer& node); void parseRadiusIgnoreRequest(QSharedPointer message, const SharedNodePointer& node); + void parseSoloRequest(QSharedPointer message, const SharedNodePointer& node); // attempt to pop a frame from each audio stream, and return the number of streams from this client int checkBuffersBeforeFrameSend(); @@ -150,6 +151,9 @@ public: const Node::IgnoredNodeIDs& getIgnoringNodeIDs() const { return _ignoringNodeIDs; } + + const std::vector& getSoloedNodes() const { return _soloedNodes; } + bool getHasReceivedFirstMix() const { return _hasReceivedFirstMix; } void setHasReceivedFirstMix(bool hasReceivedFirstMix) { _hasReceivedFirstMix = hasReceivedFirstMix; } @@ -172,7 +176,7 @@ private: void optionallyReplicatePacket(ReceivedMessage& packet, const Node& node); - void setGainForAvatar(QUuid nodeID, uint8_t gain); + void setGainForAvatar(QUuid nodeID, float gain); bool containsValidPosition(ReceivedMessage& message) const; @@ -209,6 +213,8 @@ private: std::atomic_bool _isIgnoreRadiusEnabled { false }; + std::vector _soloedNodes; + bool _hasReceivedFirstMix { false }; }; diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 57bada47f1..7a6ab9c3e2 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -272,6 +272,10 @@ bool shouldBeSkipped(MixableStream& stream, const Node& listener, return true; } + if (!listenerData.getSoloedNodes().empty()) { + return !contains(listenerData.getSoloedNodes(), stream.nodeStreamID.nodeID); + } + bool shouldCheckIgnoreBox = (listenerAudioStream.isIgnoreBoxEnabled() || stream.positionalStream->isIgnoreBoxEnabled()); if (shouldCheckIgnoreBox && @@ -310,6 +314,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { memset(_mixSamples, 0, sizeof(_mixSamples)); bool isThrottling = _numToRetain != -1; + bool isSoloing = !listenerData->getSoloedNodes().empty(); auto& streams = listenerData->getStreams(); @@ -376,13 +381,14 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { stream.approximateVolume = approximateVolume(stream, listenerAudioStream); } else { if (shouldBeSkipped(stream, *listener, *listenerAudioStream, *listenerData)) { - addStream(stream, *listenerAudioStream, 0.0f); + addStream(stream, *listenerAudioStream, 0.0f, isSoloing); streams.skipped.push_back(move(stream)); ++stats.activeToSkipped; return true; } - addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain()); + addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), + isSoloing); if (shouldBeInactive(stream)) { // To reduce artifacts we still call render to flush the HRTF for every silent @@ -417,7 +423,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { return true; } - addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain()); + addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), + isSoloing); if (shouldBeInactive(stream)) { // To reduce artifacts we still call render to flush the HRTF for every silent @@ -484,7 +491,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStream, AvatarAudioStream& listeningNodeStream, - float masterListenerGain) { + float masterListenerGain, bool isSoloing) { ++stats.totalMixes; auto streamToAdd = mixableStream.positionalStream; @@ -495,9 +502,13 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream.getPosition(); float distance = glm::max(glm::length(relativePosition), EPSILON); - float gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho); float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition); + float gain = 1.0f; + if (!isSoloing) { + gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho); + } + const int HRTF_DATASET_INDEX = 1; if (!streamToAdd->lastPopSucceeded()) { diff --git a/assignment-client/src/audio/AudioMixerSlave.h b/assignment-client/src/audio/AudioMixerSlave.h index 6566c839b8..3d979da1fc 100644 --- a/assignment-client/src/audio/AudioMixerSlave.h +++ b/assignment-client/src/audio/AudioMixerSlave.h @@ -57,7 +57,7 @@ private: bool prepareMix(const SharedNodePointer& listener); void addStream(AudioMixerClientData::MixableStream& mixableStream, AvatarAudioStream& listeningNodeStream, - float masterListenerGain); + float masterListenerGain, bool isSoloing); void updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream, AvatarAudioStream& listeningNodeStream, float masterListenerGain); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 00cdabe70e..53fc13e5cf 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -541,7 +541,7 @@ void AvatarMixer::handleRequestsDomainListDataPacket(QSharedPointersetLastBroadcastTime(node->getUUID(), 0); + nodeData->setLastBroadcastTime(node->getLocalID(), 0); nodeData->resetSentTraitData(node->getLocalID()); } ); @@ -565,7 +565,8 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate bool identityChanged = false; bool displayNameChanged = false; - avatar.processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + QDataStream avatarIdentityStream(message->getMessage()); + avatar.processAvatarIdentity(avatarIdentityStream, identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); @@ -637,7 +638,7 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer // Reset the lastBroadcastTime for the ignored avatar to 0 // so the AvatarMixer knows it'll have to send identity data about the ignored avatar // to the ignorer if the ignorer unignores. - nodeData->setLastBroadcastTime(ignoredUUID, 0); + nodeData->setLastBroadcastTime(ignoredNode->getLocalID(), 0); nodeData->resetSentTraitData(ignoredNode->getLocalID()); } @@ -647,7 +648,7 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer // to the ignored if the ignorer unignores. AvatarMixerClientData* ignoredNodeData = reinterpret_cast(ignoredNode->getLinkedData()); if (ignoredNodeData) { - ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0); + ignoredNodeData->setLastBroadcastTime(senderNode->getLocalID(), 0); ignoredNodeData->resetSentTraitData(senderNode->getLocalID()); } } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 09bdfbc564..76cdf13986 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -26,20 +26,20 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID _avatar->setID(nodeID); } -uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar) const { - std::unordered_map::const_iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar); +uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(NLPacket::LocalID otherAvatar) const { + const auto itr = _lastOtherAvatarEncodeTime.find(otherAvatar); if (itr != _lastOtherAvatarEncodeTime.end()) { return itr->second; } return 0; } -void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time) { - std::unordered_map::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar); +void AvatarMixerClientData::setLastOtherAvatarEncodeTime(NLPacket::LocalID otherAvatar, uint64_t time) { + auto itr = _lastOtherAvatarEncodeTime.find(otherAvatar); if (itr != _lastOtherAvatarEncodeTime.end()) { itr->second = time; } else { - _lastOtherAvatarEncodeTime.emplace(std::pair(otherAvatar, time)); + _lastOtherAvatarEncodeTime.emplace(std::pair(otherAvatar, time)); } } @@ -220,7 +220,7 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedDa } } -uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { +uint64_t AvatarMixerClientData::getLastBroadcastTime(NLPacket::LocalID nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastTimes.find(nodeUUID); if (nodeMatch != _lastBroadcastTimes.end()) { @@ -229,9 +229,9 @@ uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) cons return 0; } -uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { +uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(NLPacket::LocalID nodeID) const { // return the matching PacketSequenceNumber, or the default if we don't have it - auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); + auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeID); if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { return nodeMatch->second; } @@ -252,7 +252,7 @@ void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) { } else { killPacket->writePrimitive(KillAvatarReason::YourAvatarEnteredTheirBubble); } - setLastBroadcastTime(other->getUUID(), 0); + setLastBroadcastTime(other->getLocalID(), 0); resetSentTraitData(other->getLocalID()); @@ -331,9 +331,9 @@ AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherA } } -void AvatarMixerClientData::cleanupKilledNode(const QUuid& nodeUUID, Node::LocalID nodeLocalID) { - removeLastBroadcastSequenceNumber(nodeUUID); - removeLastBroadcastTime(nodeUUID); +void AvatarMixerClientData::cleanupKilledNode(const QUuid&, Node::LocalID nodeLocalID) { + removeLastBroadcastSequenceNumber(nodeLocalID); + removeLastBroadcastTime(nodeLocalID); _lastSentTraitsTimestamps.erase(nodeLocalID); _sentTraitVersions.erase(nodeLocalID); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 09d11359c3..afbc68378a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -49,17 +49,16 @@ public: const AvatarData* getConstAvatarData() const { return _avatar.get(); } AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; } + uint16_t getLastBroadcastSequenceNumber(NLPacket::LocalID nodeID) const; + void setLastBroadcastSequenceNumber(NLPacket::LocalID nodeID, uint16_t sequenceNumber) + { _lastBroadcastSequenceNumbers[nodeID] = sequenceNumber; } + Q_INVOKABLE void removeLastBroadcastSequenceNumber(NLPacket::LocalID nodeID) { _lastBroadcastSequenceNumbers.erase(nodeID); } bool isIgnoreRadiusEnabled() const { return _isIgnoreRadiusEnabled; } void setIsIgnoreRadiusEnabled(bool enabled) { _isIgnoreRadiusEnabled = enabled; } - uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; - void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber) - { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } - Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } - - uint64_t getLastBroadcastTime(const QUuid& nodeUUID) const; - void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { _lastBroadcastTimes[nodeUUID] = broadcastTime; } - Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } + uint64_t getLastBroadcastTime(NLPacket::LocalID nodeUUID) const; + void setLastBroadcastTime(NLPacket::LocalID nodeUUID, uint64_t broadcastTime) { _lastBroadcastTimes[nodeUUID] = broadcastTime; } + Q_INVOKABLE void removeLastBroadcastTime(NLPacket::LocalID nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } Q_INVOKABLE void cleanupKilledNode(const QUuid& nodeUUID, Node::LocalID nodeLocalID); @@ -93,7 +92,7 @@ public: void loadJSONStats(QJsonObject& jsonObject) const; - glm::vec3 getPosition() const { return _avatar ? _avatar->getWorldPosition() : glm::vec3(0); } + glm::vec3 getPosition() const { return _avatar ? _avatar->getClientGlobalPosition() : glm::vec3(0); } bool isRadiusIgnoring(const QUuid& other) const; void addToRadiusIgnoringSet(const QUuid& other); void removeFromRadiusIgnoringSet(const QUuid& other); @@ -114,10 +113,10 @@ public: const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; } - uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const; - void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time); + uint64_t getLastOtherAvatarEncodeTime(NLPacket::LocalID otherAvatar) const; + void setLastOtherAvatarEncodeTime(NLPacket::LocalID otherAvatar, uint64_t time); - QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } + QVector& getLastOtherAvatarSentJoints(NLPacket::LocalID otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } void queuePacket(QSharedPointer message, SharedNodePointer node); int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed @@ -150,13 +149,13 @@ private: AvatarSharedPointer _avatar { new AvatarData() }; uint16_t _lastReceivedSequenceNumber { 0 }; - std::unordered_map _lastBroadcastSequenceNumbers; - std::unordered_map _lastBroadcastTimes; + std::unordered_map _lastBroadcastSequenceNumbers; + std::unordered_map _lastBroadcastTimes; // this is a map of the last time we encoded an "other" avatar for // sending to "this" node - std::unordered_map _lastOtherAvatarEncodeTime; - std::unordered_map> _lastOtherAvatarSentJoints; + std::unordered_map _lastOtherAvatarEncodeTime; + std::unordered_map> _lastOtherAvatarSentJoints; uint64_t _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ true }; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index b6ec006c39..7e0b6a00ad 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -68,13 +68,11 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { _stats.processIncomingPacketsElapsedTime += (end - start); } -int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { - if (destinationNode->getType() == NodeType::Agent && !destinationNode->isUpstream()) { +int AvatarMixerSlave::sendIdentityPacket(NLPacketList& packetList, const AvatarMixerClientData* nodeData, const Node& destinationNode) { + if (destinationNode.getType() == NodeType::Agent && !destinationNode.isUpstream()) { QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious - auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); - identityPackets->write(individualData); - DependencyManager::get()->sendPacketList(std::move(identityPackets), *destinationNode); + packetList.write(individualData); _stats.numIdentityPackets++; return individualData.size(); } else { @@ -247,12 +245,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // reset the internal state for correct random number distribution distribution.reset(); + // Estimate number to sort on number sent last frame (with min. of 20). + const int numToSendEst = std::max(int(nodeData->getNumAvatarsSentLastFrame() * 2.5f), 20); + // reset the number of sent avatars nodeData->resetNumAvatarsSentLastFrame(); - // keep a counter of the number of considered avatars - int numOtherAvatars = 0; - // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; int identityBytesSent = 0; @@ -261,7 +259,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // max number of avatarBytes per frame int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND); - // keep track of the number of other avatars held back in this frame int numAvatarsHeldBack = 0; @@ -279,10 +276,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int minimumBytesPerAvatar = PALIsOpen ? AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID + sizeof(AvatarDataPacket::AvatarGlobalPosition) + sizeof(AvatarDataPacket::AudioLoudness) : 0; - // setup a PacketList for the avatarPackets - auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; - // compute node bounding box const float MY_AVATAR_BUBBLE_EXPANSION_FACTOR = 4.0f; // magic number determined emperically AABox nodeBox = computeBubbleBox(avatar, MY_AVATAR_BUBBLE_EXPANSION_FACTOR); @@ -350,8 +343,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored if (nodeData->isIgnoreRadiusEnabled() || (avatarClientNodeData->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { // Perform the collision check between the two bounding boxes - const float OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR = 2.4f; // magic number determined empirically - AABox otherNodeBox = computeBubbleBox(avatarClientNodeData->getAvatar(), OTHER_AVATAR_BUBBLE_EXPANSION_FACTOR); + AABox otherNodeBox = avatarClientNodeData->getAvatar().getDefaultBubbleBox(); if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(destinationNode, avatarNode); shouldIgnore = !getsAnyIgnored; @@ -364,7 +356,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) } if (!shouldIgnore) { - AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); + AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getLocalID()); AvatarDataSequenceNumber lastSeqFromSender = avatarClientNodeData->getLastReceivedSequenceNumber(); // FIXME - This code does appear to be working. But it seems brittle. @@ -380,6 +372,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { ++numAvatarsHeldBack; shouldIgnore = true; + } else if (lastSeqFromSender == 0) { + // We have have not yet recieved any data about this avatar. Ignore it for now + // This is important for Agent scripts that are not avatar + // so that they don't appear to be an avatar at the origin + shouldIgnore = true; } else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; @@ -391,7 +388,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) if (!shouldIgnore) { // sort this one for later const AvatarData* avatarNodeData = avatarClientNodeData->getConstAvatarData(); - auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNodeData->getSessionUUID()); + auto lastEncodeTime = nodeData->getLastOtherAvatarEncodeTime(avatarNode->getLocalID()); sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime)); } @@ -401,8 +398,13 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int remainingAvatars = (int)sortedAvatars.size(); auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true); + auto avatarPacket = NLPacket::create(PacketType::BulkAvatarData); + const int avatarPacketCapacity = avatarPacket->getPayloadCapacity(); + int avatarSpaceAvailable = avatarPacketCapacity; + int numPacketsSent = 0; + auto identityPacketList = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true); - const auto& sortedAvatarVector = sortedAvatars.getSortedVector(); + const auto& sortedAvatarVector = sortedAvatars.getSortedVector(numToSendEst); for (const auto& sortedAvatar : sortedAvatarVector) { const Node* otherNode = sortedAvatar.getNode(); auto lastEncodeForOther = sortedAvatar.getTimestamp(); @@ -427,21 +429,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) auto startAvatarDataPacking = chrono::high_resolution_clock::now(); - ++numOtherAvatars; - const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); - // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO - // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A. - if (otherAvatar->hasProcessedFirstIdentity() - && nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) { - identityBytesSent += sendIdentityPacket(otherNodeData, node); - - // remember the last time we sent identity details about this other node to the receiver - nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); - } - // Typically all out-of-view avatars but such avatars' priorities will rise with time: bool isLowerPriority = sortedAvatar.getPriority() <= OUT_OF_VIEW_THRESHOLD; @@ -451,71 +441,56 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) } else if (!overBudget) { detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO ? AvatarData::SendAllData : AvatarData::CullSmallData; nodeData->incrementAvatarInView(); - } - bool includeThisAvatar = true; - QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); + // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO + // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A. + if (otherAvatar->hasProcessedFirstIdentity() + && nodeData->getLastBroadcastTime(otherNode->getLocalID()) <= otherNodeData->getIdentityChangeTimestamp()) { + identityBytesSent += sendIdentityPacket(*identityPacketList, otherNodeData, *destinationNode); - lastSentJointsForOther.resize(otherAvatar->getJointCount()); - - bool distanceAdjust = true; - glm::vec3 viewerPosition = myPosition; - AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray - bool dropFaceTracking = false; - - auto startSerialize = chrono::high_resolution_clock::now(); - QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, - &lastSentJointsForOther); - auto endSerialize = chrono::high_resolution_clock::now(); - _stats.toByteArrayElapsedTime += - (quint64) chrono::duration_cast(endSerialize - startSerialize).count(); - - if (bytes.size() > maxAvatarDataBytes) { - qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() - << "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data"; - - dropFaceTracking = true; // first try dropping the facial data - bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - - if (bytes.size() > maxAvatarDataBytes) { - qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() - << "without facial data resulted in very large buffer of" << bytes.size() - << "bytes - reducing to MinimumData"; - bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - - if (bytes.size() > maxAvatarDataBytes) { - qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() - << "MinimumData resulted in very large buffer of" << bytes.size() - << "bytes - refusing to send avatar"; - includeThisAvatar = false; - } + // remember the last time we sent identity details about this other node to the receiver + nodeData->setLastBroadcastTime(otherNode->getLocalID(), usecTimestampNow()); } } - if (includeThisAvatar) { - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); - numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList->write(bytes); - avatarPacketList->endSegment(); + QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getLocalID()); - if (detail != AvatarData::NoData) { - _stats.numOthersIncluded++; + const bool distanceAdjust = true; + const bool dropFaceTracking = false; + AvatarDataPacket::SendStatus sendStatus; + sendStatus.sendUUID = true; - // increment the number of avatars sent to this reciever - nodeData->incrementNumAvatarsSentLastFrame(); + do { + auto startSerialize = chrono::high_resolution_clock::now(); + QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, + sendStatus, dropFaceTracking, distanceAdjust, myPosition, + &lastSentJointsForOther, avatarSpaceAvailable); + auto endSerialize = chrono::high_resolution_clock::now(); + _stats.toByteArrayElapsedTime += + (quint64)chrono::duration_cast(endSerialize - startSerialize).count(); - // set the last sent sequence number for this sender on the receiver - nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), - otherNodeData->getLastReceivedSequenceNumber()); - nodeData->setLastOtherAvatarEncodeTime(otherNode->getUUID(), usecTimestampNow()); + avatarPacket->write(bytes); + avatarSpaceAvailable -= bytes.size(); + numAvatarDataBytes += bytes.size(); + if (!sendStatus || avatarSpaceAvailable < (int)AvatarDataPacket::MIN_BULK_PACKET_SIZE) { + // Weren't able to fit everything. + nodeList->sendPacket(std::move(avatarPacket), *destinationNode); + ++numPacketsSent; + avatarPacket = NLPacket::create(PacketType::BulkAvatarData); + avatarSpaceAvailable = avatarPacketCapacity; } - } else { - // TODO? this avatar is not included now, and will probably not be included next frame. - // It would be nice if we could tweak its future sort priority to put it at the back of the list. + } while (!sendStatus); + + if (detail != AvatarData::NoData) { + _stats.numOthersIncluded++; + + // increment the number of avatars sent to this receiver + nodeData->incrementNumAvatarsSentLastFrame(); + + // set the last sent sequence number for this sender on the receiver + nodeData->setLastBroadcastSequenceNumber(otherNode->getLocalID(), + otherNodeData->getLastReceivedSequenceNumber()); + nodeData->setLastOtherAvatarEncodeTime(otherNode->getLocalID(), usecTimestampNow()); } auto endAvatarDataPacking = chrono::high_resolution_clock::now(); @@ -527,17 +502,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) remainingAvatars--; } + if (nodeData->getNumAvatarsSentLastFrame() > numToSendEst) { + qCWarning(avatars) << "More avatars sent than upper estimate" << nodeData->getNumAvatarsSentLastFrame() + << " / " << numToSendEst; + } + quint64 startPacketSending = usecTimestampNow(); - // close the current packet so that we're always sending something - avatarPacketList->closeCurrentPacket(true); + if (avatarPacket->getPayloadSize() != 0) { + nodeList->sendPacket(std::move(avatarPacket), *destinationNode); + ++numPacketsSent; + } - _stats.numPacketsSent += (int)avatarPacketList->getNumPackets(); + _stats.numPacketsSent += numPacketsSent; _stats.numBytesSent += numAvatarDataBytes; - // send the avatar data PacketList - nodeList->sendPacketList(std::move(avatarPacketList), *destinationNode); - // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes); @@ -549,6 +528,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeList->sendPacketList(std::move(traitsPacketList), *destinationNode); } + // Send any AvatarIdentity packets: + identityPacketList->closeCurrentPacket(); + if (identityBytesSent > 0) { + nodeList->sendPacketList(std::move(identityPacketList), *destinationNode); + } + // record the number of avatars held back this frame nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); @@ -594,20 +579,20 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin // so we always send a full update for this avatar quint64 start = usecTimestampNow(); - AvatarDataPacket::HasFlags flagsOut; + AvatarDataPacket::SendStatus sendStatus; QVector emptyLastJointSendData { otherAvatar->getJointCount() }; QByteArray avatarByteArray = otherAvatar->toByteArray(AvatarData::SendAllData, 0, emptyLastJointSendData, - flagsOut, false, false, glm::vec3(0), nullptr); + sendStatus, false, false, glm::vec3(0), nullptr, 0); quint64 end = usecTimestampNow(); _stats.toByteArrayElapsedTime += (end - start); - auto lastBroadcastTime = nodeData->getLastBroadcastTime(agentNode->getUUID()); + auto lastBroadcastTime = nodeData->getLastBroadcastTime(agentNode->getLocalID()); if (lastBroadcastTime <= agentNodeData->getIdentityChangeTimestamp() || (start - lastBroadcastTime) >= REBROADCAST_IDENTITY_TO_DOWNSTREAM_EVERY_US) { sendReplicatedIdentityPacket(*agentNode, agentNodeData, *node); - nodeData->setLastBroadcastTime(agentNode->getUUID(), start); + nodeData->setLastBroadcastTime(agentNode->getLocalID(), start); } // figure out how large our avatar byte array can be to fit in the packet list @@ -625,14 +610,14 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin << "-" << avatarByteArray.size() << "bytes"; avatarByteArray = otherAvatar->toByteArray(AvatarData::SendAllData, 0, emptyLastJointSendData, - flagsOut, true, false, glm::vec3(0), nullptr); + sendStatus, true, false, glm::vec3(0), nullptr, 0); if (avatarByteArray.size() > maxAvatarByteArraySize) { qCWarning(avatars) << "Replicated avatar data without facial data still too large for" << otherAvatar->getSessionUUID() << "-" << avatarByteArray.size() << "bytes"; avatarByteArray = otherAvatar->toByteArray(AvatarData::MinimumData, 0, emptyLastJointSendData, - flagsOut, true, false, glm::vec3(0), nullptr); + sendStatus, true, false, glm::vec3(0), nullptr, 0); } } @@ -641,7 +626,7 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin nodeData->incrementNumAvatarsSentLastFrame(); // set the last sent sequence number for this sender on the receiver - nodeData->setLastBroadcastSequenceNumber(agentNode->getUUID(), + nodeData->setLastBroadcastSequenceNumber(agentNode->getLocalID(), agentNodeData->getLastReceivedSequenceNumber()); // increment the number of avatars sent to this reciever diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index bcb70f8743..2ef90af38e 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -101,7 +101,7 @@ public: void harvestStats(AvatarMixerSlaveStats& stats); private: - int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); + int sendIdentityPacket(NLPacketList& packet, const AvatarMixerClientData* nodeData, const Node& destinationNode); int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode); qint64 addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 7d2b267a05..51038a782f 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -69,10 +69,10 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); } -static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { +static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) { glm::mat4 translationMat = glm::translate(translation); - glm::mat4 rotationMat = glm::mat4_cast(fbxJoint.preRotation * rotation * fbxJoint.postRotation); - glm::mat4 finalMat = translationMat * fbxJoint.preTransform * rotationMat * fbxJoint.postTransform; + glm::mat4 rotationMat = glm::mat4_cast(joint.preRotation * rotation * joint.postRotation); + glm::mat4 finalMat = translationMat * joint.preTransform * rotationMat * joint.postTransform; return AnimPose(finalMat); } @@ -84,7 +84,7 @@ void ScriptableAvatar::update(float deltatime) { // Run animation if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { - _animSkeleton = std::make_shared(_bind->getGeometry()); + _animSkeleton = std::make_shared(_bind->getHFMModel()); } float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { @@ -93,7 +93,7 @@ void ScriptableAvatar::update(float deltatime) { } _animationDetails.currentFrame = currentFrame; - const QVector& modelJoints = _bind->getGeometry().joints; + const QVector& modelJoints = _bind->getHFMModel().joints; QStringList animationJointNames = _animation->getJointNames(); const int nJoints = modelJoints.size(); @@ -102,8 +102,8 @@ void ScriptableAvatar::update(float deltatime) { } const int frameCount = _animation->getFrames().size(); - const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); - const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); + const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); + const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); std::vector poses = _animSkeleton->getRelativeDefaultPoses(); @@ -113,7 +113,7 @@ void ScriptableAvatar::update(float deltatime) { const QString& name = animationJointNames[i]; // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than // trusting the .fst (which is sometimes not updated to match changes to .fbx). - int mapping = _bind->getGeometry().getJointIndex(name); + int mapping = _bind->getHFMModel().getJointIndex(name); if (mapping != -1 && !_maskedJoints.contains(name)) { AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE); diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 52beba72a1..578bd84a8f 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -164,7 +164,7 @@ public: void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement); bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } -private slots: +public slots: void update(float deltatime); private: diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index 6ff6fce1d8..bc35d2f2f8 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -6,6 +6,10 @@ if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8") message( FATAL_ERROR "Only 64 bit builds supported." ) endif() +if (USE_CCACHE OR "$ENV{USE_CCACHE}") + configure_ccache() +endif() + if (WIN32) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) @@ -88,7 +92,7 @@ if (APPLE) set(OSX_SDK "${OSX_VERSION}" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH") # set our OS X deployment target - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9) # find the SDK path for the desired SDK find_path( diff --git a/cmake/externals/boostconfig/CMakeLists.txt b/cmake/externals/boostconfig/CMakeLists.txt deleted file mode 100644 index e33167b0ba..0000000000 --- a/cmake/externals/boostconfig/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(EXTERNAL_NAME boostconfig) -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - #URL https://github.com/boostorg/config/archive/boost-1.58.0.zip - URL https://public.highfidelity.com/dependencies/config-boost-1.58.0.zip - URL_MD5 42fa673bae2b7645a22736445e80eb8d - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) - -ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) - -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt deleted file mode 100644 index ffa1c67ce3..0000000000 --- a/cmake/externals/bullet/CMakeLists.txt +++ /dev/null @@ -1,85 +0,0 @@ -set(EXTERNAL_NAME bullet) - -if (WIN32) - set(PLATFORM_CMAKE_ARGS "-DUSE_MSVC_RUNTIME_LIBRARY_DLL=1") -else () - set(PLATFORM_CMAKE_ARGS "-DBUILD_SHARED_LIBS=1") - - if (ANDROID) - list(APPEND PLATFORM_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") - elseif (APPLE) - list(APPEND PLATFORM_CMAKE_ARGS "-DCMAKE_INSTALL_NAME_DIR=/lib") - endif() -endif () - -include(ExternalProject) - -if (WIN32) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz - URL_MD5 0a6876607ebe83e227427215f15946fd - CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 -DUSE_DX11=0 - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - ) -else () - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/bullet-2.88.tgz - URL_MD5 0a6876607ebe83e227427215f15946fd - CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_BULLET3=0 -DBUILD_OPENGL3_DEMOS=0 -DBUILD_BULLET2_DEMOS=0 -DBUILD_UNIT_TESTS=0 -DUSE_GLUT=0 - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - ) -endif () - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -set(BULLET_LIB_DIR "${INSTALL_DIR}/lib") - -if (APPLE OR UNIX OR ANDROID) - if (APPLE) - set(BULLET_LIB_EXT "dylib") - else () - set(BULLET_LIB_EXT "so") - endif () - - set(LIB_PREFIX "lib") -elseif (WIN32) - set(BULLET_LIB_EXT "lib") -endif () - -if (DEFINED BULLET_LIB_EXT) - set(_BULLET_LIB_PAIRS "DYNAMICS_LIBRARY\;BulletDynamics" "COLLISION_LIBRARY\;BulletCollision" "MATH_LIBRARY\;LinearMath" "SOFTBODY_LIBRARY\;BulletSoftBody") - - foreach(_LIB_PAIR ${_BULLET_LIB_PAIRS}) - list(GET _LIB_PAIR 0 _LIB_VAR_NAME) - list(GET _LIB_PAIR 1 _LIB_NAME) - - - - if (WIN32) - # on windows, we might end up with a library that ends with RelWithDebInfo if Visual Studio is building for that configuration - set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_RELEASE "${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}$<$:_RelWithDebugInfo>$<$:_MinsizeRel>.${BULLET_LIB_EXT}" CACHE FILEPATH "${_LIB_NAME} release library location") - - set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_DEBUG ${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}_Debug.${BULLET_LIB_EXT} CACHE FILEPATH "${_LIB_NAME} debug library location") - else () - set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_RELEASE ${BULLET_LIB_DIR}/${LIB_PREFIX}${_LIB_NAME}.${BULLET_LIB_EXT} CACHE FILEPATH "${_LIB_NAME} release library location") - set(${EXTERNAL_NAME_UPPER}_${_LIB_VAR_NAME}_DEBUG "" CACHE FILEPATH "${_LIB_NAME} debug library location") - endif () - endforeach() -endif () - -if (DEFINED ${EXTERNAL_NAME_UPPER}_DYNAMICS_LIBRARY_RELEASE) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/bullet CACHE PATH "Path to bullet include directory") -endif () diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt deleted file mode 100644 index 28a2177cbb..0000000000 --- a/cmake/externals/draco/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -set(EXTERNAL_NAME draco) - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () - -if (APPLE) - set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) -endif () - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/draco-1.1.0.zip - URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb - CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=-$ ${EXTRA_CMAKE_FLAGS} - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) -set(SUFFIXED_INSTALL_DIR "${INSTALL_DIR}-$") - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SUFFIXED_INSTALL_DIR}/include CACHE PATH "List of Draco include directories") - -if (UNIX) - set(LIB_PREFIX "lib") - set(LIB_EXT "a") -elseif (WIN32) - set(LIB_EXT "lib") -endif () - -set(${EXTERNAL_NAME_UPPER}_LIBRARY ${SUFFIXED_INSTALL_DIR}/lib/${LIB_PREFIX}draco.${LIB_EXT} CACHE FILEPATH "Path to Draco release library") -set(${EXTERNAL_NAME_UPPER}_ENCODER_LIBRARY ${SUFFIXED_INSTALL_DIR}/lib/${LIB_PREFIX}dracoenc.${LIB_EXT} CACHE FILEPATH "Path to Draco encoder release library") -set(${EXTERNAL_NAME_UPPER}_DECODER_LIBRARY ${SUFFIXED_INSTALL_DIR}/lib/${LIB_PREFIX}dracodec.${LIB_EXT} CACHE FILEPATH "Path to Draco decoder release library") diff --git a/cmake/externals/etc2comp/CMakeLists.txt b/cmake/externals/etc2comp/CMakeLists.txt deleted file mode 100644 index 88ed988873..0000000000 --- a/cmake/externals/etc2comp/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -set(EXTERNAL_NAME etc2comp) - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () - -if (APPLE) - set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) -endif () - -include(ExternalProject) -# We use a patched version of etc2comp that properly generates all the necessary mips -# See https://github.com/google/etc2comp/pull/29 -# We also use part of https://github.com/google/etc2comp/pull/1, which fixes a bug -# that would override CMAKE_CXX_FLAGS -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/etc2comp-patched.zip - URL_MD5 4c96153eb179acbe619e3d99d3330595 - CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS} - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -if (WIN32 OR APPLE) - if (WIN32) - set(_LIB_FILE "EtcLib.lib") - else () - set(_LIB_FILE "libEtcLib.a") - endif () - - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/${_LIB_FILE} CACHE FILEPATH "Path to Etc2Comp debug library") - - # use generator expression to ensure the correct library is found when building different configurations in VS - set(_LIB_FOLDER "$<$:build/EtcLib/RelWithDebInfo>") - set(_LIB_FOLDER "${_LIB_FOLDER}$<$:build/EtcLib/MinSizeRel>") - set(_LIB_FOLDER "${_LIB_FOLDER}$<$,$>:build/EtcLib/Release>") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/${_LIB_FILE} CACHE FILEPATH "Path to Etc2Comp release library") -else () - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to EtcLib debug library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library") - -endif () - -set(ETC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/Etc CACHE FILEPATH "Path to Etc2Comp/Etc include directory") -set(ETCCODEC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/EtcCodec CACHE FILEPATH "Path to Etc2Comp/EtcCodec include directory") -# ETC2COMP_INCLUDE_DIRS will be set later by FindEtc2Comp diff --git a/cmake/externals/gli/CMakeLists.txt b/cmake/externals/gli/CMakeLists.txt deleted file mode 100644 index bde31cbede..0000000000 --- a/cmake/externals/gli/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(EXTERNAL_NAME gli) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/gli-0.8.1.0.zip - URL_MD5 00c990f59c12bbf367956ef399d6f798 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "List of gli include directories") \ No newline at end of file diff --git a/cmake/externals/glm/CMakeLists.txt b/cmake/externals/glm/CMakeLists.txt deleted file mode 100644 index a52ddde9f5..0000000000 --- a/cmake/externals/glm/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(EXTERNAL_NAME glm) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/glm-0.9.8.5-patched.zip - URL_MD5 7d39ecc1cea275427534c3cfd6dd63f0 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= ${EXTERNAL_ARGS} - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glm include directories") diff --git a/cmake/externals/json/CMakeLists.txt b/cmake/externals/json/CMakeLists.txt deleted file mode 100644 index 91bdb09fc8..0000000000 --- a/cmake/externals/json/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -set(EXTERNAL_NAME json) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/json_3.1.2.zip - URL_MD5 94dbf6ea25a7569ddc0ab6e20862cf16 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= ${EXTERNAL_ARGS} - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "List of json include directories") diff --git a/cmake/externals/nvtt/CMakeLists.txt b/cmake/externals/nvtt/CMakeLists.txt deleted file mode 100644 index 2db8335cd7..0000000000 --- a/cmake/externals/nvtt/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -include(ExternalProject) -include(SelectLibraryConfigurations) - -set(EXTERNAL_NAME nvtt) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -if (WIN32) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/nvtt-win-2.1.0.hifi.zip - URL_MD5 10da01cf601f88f6dc12a6bc13c89136 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) - - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "Location of NVTT include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Release/x64/nvtt.lib CACHE FILEPATH "Path to NVTT release library") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/Release>/x64" CACHE PATH "Location of NVTT release DLL") -else () - - if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") - endif () - - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/nvidia-texture-tools-2.1.0.hifi-83462e4.zip - URL_MD5 602776e08515b54bfa1b8dc455003f0f - CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - ) - - ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "Location of NVTT include directory") - - if (APPLE) - set(_LIB_EXT "dylib") - else () - set(_LIB_EXT "so") - endif () - - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libnvtt.${_LIB_EXT} CACHE FILEPATH "Path to NVTT library") - - if (APPLE) - # on OS X we have to use install_name_tool to fix the paths found in the NVTT shared libraries - # so that they can be found and linked during the linking phase - set(_NVTT_LIB_DIR "${INSTALL_DIR}/lib") - - # first fix the install names of all present libraries - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - change-install-name - COMMENT "Calling install_name_tool on NVTT libraries to fix install name for dylib linking" - COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_NVTT_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake - DEPENDEES install - WORKING_DIRECTORY - LOG 1 - ) - - # then, for the main library (libnvtt) fix the paths to the dependency libraries (core, image, math) - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - change-dependency-paths - COMMENT "Calling install_name_tool on NVTT libraries to fix paths for dependency libraries" - COMMAND install_name_tool -change libnvimage.dylib ${INSTALL_DIR}/lib/libnvimage.dylib libnvtt.dylib - COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvtt.dylib - COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvtt.dylib - COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvimage.dylib - COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvimage.dylib - COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvmath.dylib - DEPENDEES install - WORKING_DIRECTORY /lib - LOG 1 - ) - endif () -endif () - -# Hide this external target (for IDE users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt deleted file mode 100644 index 05dfe70ed7..0000000000 --- a/cmake/externals/openvr/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -include(ExternalProject) -include(SelectLibraryConfigurations) - -set(EXTERNAL_NAME OpenVR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/openvr-1.0.6.zip - URL_MD5 f6892cd3a3078f505d03b4297f5a1951 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNAL) - -if (WIN32) - - # FIXME need to account for different architectures - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win64/openvr_api.lib CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win64) - else() - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) - endif() - -elseif(APPLE) - - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx32/libopenvr_api.dylib CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32) - -elseif(NOT ANDROID) - - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux64/libopenvr_api.so CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux64) - -endif() - diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 6960f7682a..f99d995a0c 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -1,14 +1,19 @@ set(EXTERNAL_NAME quazip) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -cmake_policy(SET CMP0046 OLD) include(ExternalProject) -set(QUAZIP_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON) +set(QUAZIP_CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH= + -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} + -DCMAKE_INSTALL_NAME_DIR:PATH=/lib + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + -DZLIB_ROOT=${VCPKG_INSTALL_ROOT} + -DCMAKE_POSITION_INDEPENDENT_CODE=ON) -if (APPLE) -else () - set(QUAZIP_CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} -DCMAKE_CXX_STANDARD=11) +if (NOT APPLE) + set(QUAZIP_CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} -DCMAKE_CXX_STANDARD=11) endif () ExternalProject_Add( @@ -22,10 +27,8 @@ ExternalProject_Add( LOG_BUILD 1 ) -add_dependencies(quazip zlib) - # Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals" INSTALL_NAME_DIR ${INSTALL_DIR}/lib BUILD_WITH_INSTALL_RPATH True) @@ -54,4 +57,4 @@ select_library_configurations(${EXTERNAL_NAME_UPPER}) # Force selected libraries into the cache set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of QuaZip libraries") -set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") +set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt deleted file mode 100644 index 1e8e690743..0000000000 --- a/cmake/externals/sdl2/CMakeLists.txt +++ /dev/null @@ -1,90 +0,0 @@ -set(EXTERNAL_NAME sdl2) - -include(ExternalProject) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -if (WIN32) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/SDL2-devel-2.0.3-VC.zip - URL_MD5 30a333bcbe94bc5016e8799c73e86233 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) -elseif (APPLE) - - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.zip - URL_MD5 55f1eae5142d20db11c844d8d4d6deed - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DVIDEO_OPENGL=OFF - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - ) - - ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY "${INSTALL_DIR}/lib/libSDL2-2.0.dylib" CACHE STRING "Path to SDL2 library") - - set(_SDL2_LIB_DIR "${INSTALL_DIR}/lib") - - ExternalProject_Add_Step( - ${EXTERNAL_NAME} - change-install-name - COMMENT "Calling install_name_tool on SDL2 libraries to fix install name for dylib linking" - COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SDL2_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake - DEPENDEES install - WORKING_DIRECTORY - LOG 1 - ) - -else () - if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") - endif () - - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/SDL2-2.0.3.tar.gz - URL_MD5 fe6c61d2e9df9ef570e7e80c6e822537 - CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - ) -endif () - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -if (APPLE) - -# NOOP - -elseif (WIN32) - - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") - - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x64/SDL2.lib CACHE FILEPATH "Path to SDL2 library") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x64 CACHE PATH "Location of SDL2 DLL") - else() - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") - endif() - - add_paths_to_fixup_libs(${${EXTERNAL_NAME_UPPER}_DLL_PATH}) - -else () - - ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library") - -endif () diff --git a/cmake/externals/serverless-content/CMakeLists.txt b/cmake/externals/serverless-content/CMakeLists.txt index 12e2b7a4c6..8bb49f0973 100644 --- a/cmake/externals/serverless-content/CMakeLists.txt +++ b/cmake/externals/serverless-content/CMakeLists.txt @@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC73.zip - URL_MD5 0c5edfb63cafb042311d3cf25261fbf2 + URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC75.zip + URL_MD5 b4225d058952e17976ac228330ce8d51 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/externals/zlib/CMakeLists.txt b/cmake/externals/zlib/CMakeLists.txt deleted file mode 100644 index 85506ba0e1..0000000000 --- a/cmake/externals/zlib/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(EXTERNAL_NAME zlib) -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - -include(ExternalProject) - -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/zlib128.zip - URL_MD5 126f8676442ffbd97884eb4d6f32afb4 - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) -set(${EXTERNAL_NAME_UPPER}_ROOT ${INSTALL_DIR} CACHE PATH "Path for Zlib install root") -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of zlib include directories") -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of zlib include directories") -set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/bin CACHE FILEPATH "Location of ZLib DLL") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/zlib.lib CACHE FILEPATH "Location of zlib release library") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/zlibd.lib CACHE FILEPATH "Location of zlib debug library") - -include(SelectLibraryConfigurations) -select_library_configurations(${EXTERNAL_NAME_UPPER}) - -# Force selected libraries into the cache -set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of zlib libraries") -set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of zlib libraries") diff --git a/cmake/init.cmake b/cmake/init.cmake index 9d7b0fd94c..3f632b30f8 100644 --- a/cmake/init.cmake +++ b/cmake/init.cmake @@ -3,11 +3,15 @@ if (WIN32) endif (WIN32) if (POLICY CMP0043) - cmake_policy(SET CMP0043 OLD) + cmake_policy(SET CMP0043 NEW) endif () if (POLICY CMP0042) - cmake_policy(SET CMP0042 OLD) + cmake_policy(SET CMP0042 NEW) +endif () + +if (POLICY CMP0074) + cmake_policy(SET CMP0074 OLD) endif () set_property(GLOBAL PROPERTY USE_FOLDERS ON) @@ -34,7 +38,7 @@ file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake") foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS}) include(${CUSTOM_MACRO}) endforeach() -unset(HIFI_CUSTOM_MACROS) +unset(HIFI_CUSTOM_MACROS) if (ANDROID) set(BUILD_SHARED_LIBS ON) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index 9c5ad70c78..64fbcc4ea6 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -8,34 +8,95 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # +# FIXME use the built tools + +macro(AUTOSCRIBE_APPEND_QRC) + string(CONCAT SHADER_QRC "${SHADER_QRC}" "${ARGV1}\n") +endmacro() + +macro(AUTOSCRIBE_PLATFORM_SHADER) + set(AUTOSCRIBE_PLATFORM_PATH "${ARGV0}") + string(REGEX MATCH "([0-9]+(es)?)(/stereo)?" PLATFORM_PATH_REGEX ${AUTOSCRIBE_PLATFORM_PATH}) + set(AUTOSCRIBE_DIALECT "${CMAKE_MATCH_1}") + if (CMAKE_MATCH_3) + set(AUTOSCRIBE_VARIANT "stereo") + else() + set(AUTOSCRIBE_VARIANT "mono") + endif() + string(REGEX REPLACE "/" "\\\\" SOURCE_GROUP_PATH ${AUTOSCRIBE_PLATFORM_PATH}) + set(SOURCE_GROUP_PATH "${SHADER_LIB}\\${SOURCE_GROUP_PATH}") + set(AUTOSCRIBE_DIALECT_HEADER "${AUTOSCRIBE_HEADER_DIR}/${AUTOSCRIBE_DIALECT}/header.glsl") + set(AUTOSCRIBE_VARIANT_HEADER "${AUTOSCRIBE_HEADER_DIR}/${AUTOSCRIBE_VARIANT}.glsl") + + set(AUTOSCRIBE_OUTPUT_FILE "${SHADERS_DIR}/${SHADER_LIB}/${AUTOSCRIBE_PLATFORM_PATH}/${SHADER_NAME}.${SHADER_TYPE}") + AUTOSCRIBE_APPEND_QRC("${SHADER_COUNT}/${AUTOSCRIBE_PLATFORM_PATH}/scribe" "${AUTOSCRIBE_OUTPUT_FILE}") + source_group(${SOURCE_GROUP_PATH} FILES ${AUTOSCRIBE_OUTPUT_FILE}) + set_property(SOURCE ${AUTOSCRIBE_OUTPUT_FILE} PROPERTY SKIP_AUTOMOC ON) + list(APPEND SCRIBED_SHADERS ${AUTOSCRIBE_OUTPUT_FILE}) + + set(AUTOSCRIBE_SPIRV_FILE "${AUTOSCRIBE_OUTPUT_FILE}.spv") + # don't add unoptimized spirv to the QRC + #AUTOSCRIBE_APPEND_QRC("${SHADER_COUNT}/${AUTOSCRIBE_PLATFORM_PATH}/spirv_unopt" "${AUTOSCRIBE_SPIRV_FILE}") + source_group(${SOURCE_GROUP_PATH} FILES ${AUTOSCRIBE_SPIRV_FILE}) + set_property(SOURCE ${AUTOSCRIBE_SPIRV_FILE} PROPERTY SKIP_AUTOMOC ON) + list(APPEND SPIRV_SHADERS ${AUTOSCRIBE_SPIRV_FILE}) + + set(AUTOSCRIBE_SPIRV_OPT_FILE "${AUTOSCRIBE_OUTPUT_FILE}.opt.spv") + AUTOSCRIBE_APPEND_QRC("${SHADER_COUNT}/${AUTOSCRIBE_PLATFORM_PATH}/spirv" "${AUTOSCRIBE_SPIRV_OPT_FILE}") + source_group(${SOURCE_GROUP_PATH} FILES ${AUTOSCRIBE_SPIRV_OPT_FILE}) + set_property(SOURCE ${AUTOSCRIBE_SPIRV_OPT_FILE} PROPERTY SKIP_AUTOMOC ON) + list(APPEND SPIRV_SHADERS ${AUTOSCRIBE_SPIRV_OPT_FILE}) + + set(AUTOSCRIBE_SPIRV_GLSL_FILE "${AUTOSCRIBE_OUTPUT_FILE}.glsl") + AUTOSCRIBE_APPEND_QRC("${SHADER_COUNT}/${AUTOSCRIBE_PLATFORM_PATH}/glsl" "${AUTOSCRIBE_SPIRV_GLSL_FILE}") + source_group(${SOURCE_GROUP_PATH} FILES ${AUTOSCRIBE_SPIRV_GLSL_FILE}) + set_property(SOURCE ${AUTOSCRIBE_SPIRV_GLSL_FILE} PROPERTY SKIP_AUTOMOC ON) + list(APPEND SPIRV_SHADERS ${AUTOSCRIBE_SPIRV_GLSL_FILE}) + + set(AUTOSCRIBE_SPIRV_JSON_FILE "${AUTOSCRIBE_OUTPUT_FILE}.json") + AUTOSCRIBE_APPEND_QRC("${SHADER_COUNT}/${AUTOSCRIBE_PLATFORM_PATH}/json" "${AUTOSCRIBE_SPIRV_JSON_FILE}") + source_group(${SOURCE_GROUP_PATH} FILES ${AUTOSCRIBE_SPIRV_JSON_FILE}) + set_property(SOURCE ${AUTOSCRIBE_SPIRV_JSON_FILE} PROPERTY SKIP_AUTOMOC ON) + list(APPEND REFLECTED_SHADERS ${AUTOSCRIBE_SPIRV_JSON_FILE}) + + unset(SHADER_GEN_LINE) + list(APPEND SHADER_GEN_LINE ${AUTOSCRIBE_DIALECT}) + list(APPEND SHADER_GEN_LINE ${AUTOSCRIBE_VARIANT}) + file(RELATIVE_PATH TEMP_PATH ${CMAKE_SOURCE_DIR} ${SHADER_FILE}) + list(APPEND SHADER_GEN_LINE ${TEMP_PATH}) + file(RELATIVE_PATH TEMP_PATH ${CMAKE_SOURCE_DIR} ${AUTOSCRIBE_OUTPUT_FILE}) + list(APPEND SHADER_GEN_LINE ${TEMP_PATH}) + list(APPEND SHADER_GEN_LINE ${AUTOSCRIBE_SHADER_SEEN_LIBS}) + string(CONCAT AUTOSCRIBE_SHADERGEN_COMMANDS "${AUTOSCRIBE_SHADERGEN_COMMANDS}" "${SHADER_GEN_LINE}\n") +endmacro() + macro(AUTOSCRIBE_SHADER) + # + # Set the include paths + # + # FIXME base the include paths off of output from the scribe tool, + # instead of treating every previously seen shader as a possible header unset(SHADER_INCLUDE_FILES) - # Grab include files foreach(includeFile ${ARGN}) list(APPEND SHADER_INCLUDE_FILES ${includeFile}) endforeach() - foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES}) get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH) list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR}) endforeach() - - list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS) - #Extract the unique include shader paths set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE}) endforeach() - list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS) - #message(ready for includes ${SHADER_INCLUDES_PATHS}) - - # make the scribe include arguments - set(SCRIBE_INCLUDES) + unset(SCRIBE_INCLUDES) foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS}) set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/) endforeach() + # + # Figure out the various output names + # # Define the final name of the generated shader file get_filename_component(SHADER_NAME ${SHADER_FILE} NAME_WE) get_filename_component(SHADER_EXT ${SHADER_FILE} EXT) @@ -46,38 +107,36 @@ macro(AUTOSCRIBE_SHADER) elseif(${SHADER_EXT} STREQUAL .slg) set(SHADER_TYPE geom) endif() - file(MAKE_DIRECTORY "${SHADERS_DIR}/${SHADER_LIB}") - set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_LIB}/${SHADER_NAME}.${SHADER_TYPE}") - file(TO_CMAKE_PATH "${SHADER_TARGET}" COMPILED_SHADER) - set(REFLECTED_SHADER "${COMPILED_SHADER}.json") - set(SCRIBE_ARGS -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + set(SCRIBE_ARGS -D GLPROFILE ${GLPROFILE} -T ${SHADER_TYPE} ${SCRIBE_INCLUDES} ) - # Generate the frag/vert file - add_custom_command( - OUTPUT ${SHADER_TARGET} - COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS} - DEPENDS ${SHADER_FILE} ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES}) + # SHADER_SCRIBED -> the output of scribe + set(SHADER_SCRIBED "${SHADERS_DIR}/${SHADER_LIB}/${SHADER_NAME}.${SHADER_TYPE}") - # Generate the json reflection - # FIXME move to spirv-cross for this task after we have spirv compatible shaders - add_custom_command( - OUTPUT ${REFLECTED_SHADER} - COMMAND ${SHREFLECT_COMMAND} ${COMPILED_SHADER} - DEPENDS ${SHREFLECT_DEPENDENCY} ${COMPILED_SHADER}) + # SHADER_NAME_FILE -> a file containing the shader name and extension (useful for debugging and for + # determining the type of shader from the filename) + set(SHADER_NAME_FILE "${SHADER_SCRIBED}.name") + file(TO_CMAKE_PATH "${SHADER_SCRIBED}" SHADER_SCRIBED) + file(WRITE "${SHADER_SCRIBED}.name" "${SHADER_NAME}.${SHADER_TYPE}") + AUTOSCRIBE_APPEND_QRC("${SHADER_COUNT}/name" "${SHADER_NAME_FILE}") - #output the generated file name - source_group("Compiled/${SHADER_LIB}" FILES ${COMPILED_SHADER}) - set_property(SOURCE ${COMPILED_SHADER} PROPERTY SKIP_AUTOMOC ON) - list(APPEND COMPILED_SHADERS ${COMPILED_SHADER}) + if (USE_GLES) + set(SPIRV_CROSS_ARGS --version 310es) + AUTOSCRIBE_PLATFORM_SHADER("310es") + AUTOSCRIBE_PLATFORM_SHADER("310es/stereo") + else() + set(SPIRV_CROSS_ARGS --version 410 --no-420pack-extension) + AUTOSCRIBE_PLATFORM_SHADER("410") + AUTOSCRIBE_PLATFORM_SHADER("410/stereo") + if (NOT APPLE) + set(SPIRV_CROSS_ARGS --version 450) + AUTOSCRIBE_PLATFORM_SHADER("450") + AUTOSCRIBE_PLATFORM_SHADER("450/stereo") + endif() + endif() - source_group("Reflected/${SHADER_LIB}" FILES ${REFLECTED_SHADER}) - list(APPEND REFLECTED_SHADERS ${REFLECTED_SHADER}) - - string(CONCAT SHADER_QRC "${SHADER_QRC}" "${COMPILED_SHADER}\n") - string(CONCAT SHADER_QRC "${SHADER_QRC}" "${REFLECTED_SHADER}\n") string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${SHADER_NAME} = ${SHADER_COUNT},\n") - + string(CONCAT SHADER_SHADERS_ARRAY "${SHADER_SHADERS_ARRAY}" "${SHADER_COUNT},\n") MATH(EXPR SHADER_COUNT "${SHADER_COUNT}+1") endmacro() @@ -86,6 +145,8 @@ macro(AUTOSCRIBE_SHADER_LIB) message(FATAL_ERROR "AUTOSCRIBE_SHADER_LIB can only be used by the shaders library") endif() + file(MAKE_DIRECTORY "${SHADERS_DIR}/${SHADER_LIB}") + list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES "${CMAKE_SOURCE_DIR}/libraries/${SHADER_LIB}/src") string(REGEX REPLACE "[-]" "_" SHADER_NAMESPACE ${SHADER_LIB}) string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace ${SHADER_NAMESPACE} {\n") @@ -165,66 +226,81 @@ macro(AUTOSCRIBE_SHADER_LIB) # Finish the shader enums string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "} // namespace ${SHADER_NAMESPACE}\n") - #file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}") - #foreach(HIFI_LIBRARY ${ARGN}) - #list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src) - #endforeach() - #endif() endmacro() macro(AUTOSCRIBE_SHADER_LIBS) - set(SCRIBE_COMMAND scribe) - set(SHREFLECT_COMMAND shreflect) - set(SHREFLECT_DEPENDENCY shreflect) - - # Target dependant Custom rule on the SHADER_FILE - if (ANDROID) - set(GLPROFILE LINUX_GL) - set(SCRIBE_COMMAND ${NATIVE_SCRIBE}) - set(SHREFLECT_COMMAND ${NATIVE_SHREFLECT}) - unset(SHREFLECT_DEPENDENCY) - else() - if (APPLE) - set(GLPROFILE MAC_GL) - elseif(UNIX) - set(GLPROFILE LINUX_GL) - else() - set(GLPROFILE PC_GL) - endif() - endif() - + message(STATUS "Shader processing start") + set(AUTOSCRIBE_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/headers) # Start the shader IDs - set(SHADER_COUNT 1) set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders") - set(SHADER_ENUMS "") file(MAKE_DIRECTORY ${SHADERS_DIR}) + set(SHADER_ENUMS "") + set(SHADER_COUNT 1) # # Scribe generation & program defintiion # foreach(SHADER_LIB ${ARGN}) + list(APPEND AUTOSCRIBE_SHADER_SEEN_LIBS ${SHADER_LIB}) AUTOSCRIBE_SHADER_LIB(${SHADER_LIB}) endforeach() # Generate the library files configure_file( ShaderEnums.cpp.in - ${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp) + ${CMAKE_CURRENT_BINARY_DIR}/ShaderEnums.cpp) configure_file( ShaderEnums.h.in - ${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h) - configure_file( - shaders.qrc.in - ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc) + ${CMAKE_CURRENT_BINARY_DIR}/ShaderEnums.h) - set(AUTOSCRIBE_SHADER_LIB_SRC "${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h;${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp") - set(QT_RESOURCES_FILE ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc) + configure_file(shaders.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc) + list(APPEND QT_RESOURCES_FILE ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc) + + list(APPEND AUTOSCRIBE_SHADER_HEADERS ${AUTOSCRIBE_HEADER_DIR}/mono.glsl ${AUTOSCRIBE_HEADER_DIR}/stereo.glsl) + list(APPEND AUTOSCRIBE_SHADER_HEADERS ${AUTOSCRIBE_HEADER_DIR}/450/header.glsl ${AUTOSCRIBE_HEADER_DIR}/410/header.glsl ${AUTOSCRIBE_HEADER_DIR}/310es/header.glsl) + source_group("Shader Headers" FILES ${AUTOSCRIBE_HEADER_DIR}/mono.glsl ${AUTOSCRIBE_HEADER_DIR}/stereo.glsl) + source_group("Shader Headers\\450" FILES ${AUTOSCRIBE_HEADER_DIR}/450/header.glsl) + source_group("Shader Headers\\410" FILES ${AUTOSCRIBE_HEADER_DIR}/410/header.glsl) + source_group("Shader Headers\\310es" FILES ${AUTOSCRIBE_HEADER_DIR}/310es/header.glsl) + + list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_HEADERS}) + list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/ShaderEnums.h ${CMAKE_CURRENT_BINARY_DIR}/ShaderEnums.cpp) + # Write the shadergen command list + set(AUTOSCRIBE_SHADERGEN_COMMANDS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shadergen.txt) + file(WRITE ${AUTOSCRIBE_SHADERGEN_COMMANDS_FILE} "${AUTOSCRIBE_SHADERGEN_COMMANDS}") + + # A custom python script which will generate all our shader artifacts + add_custom_command( + OUTPUT ${SCRIBED_SHADERS} ${SPIRV_SHADERS} ${REFLECTED_SHADERS} + COMMENT "Generating/updating shaders" + COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_SOURCE_DIR}/tools/shadergen.py + --commands ${AUTOSCRIBE_SHADERGEN_COMMANDS_FILE} + --tools-dir ${VCPKG_TOOLS_DIR} + --build-dir ${CMAKE_CURRENT_BINARY_DIR} + --source-dir ${CMAKE_SOURCE_DIR} + DEPENDS ${AUTOSCRIBE_SHADER_HEADERS} ${CMAKE_SOURCE_DIR}/tools/shadergen.py ${ALL_SCRIBE_SHADERS}) + + add_custom_target(shadergen DEPENDS ${SCRIBED_SHADERS} ${SPIRV_SHADERS} ${REFLECTED_SHADERS}) + set_target_properties(shadergen PROPERTIES FOLDER "Shaders") + # Custom targets required to force generation of the shaders via scribe - add_custom_target(scribe_shaders SOURCES ${ALL_SCRIBE_SHADERS}) - add_custom_target(compiled_shaders SOURCES ${COMPILED_SHADERS}) - add_custom_target(reflected_shaders SOURCES ${REFLECTED_SHADERS}) + add_custom_target(scribe_shaders SOURCES ${ALL_SCRIBE_SHADERS} ${AUTOSCRIBE_SHADER_HEADERS}) set_target_properties(scribe_shaders PROPERTIES FOLDER "Shaders") - set_target_properties(compiled_shaders PROPERTIES FOLDER "Shaders") + + add_custom_target(scribed_shaders SOURCES ${SCRIBED_SHADERS}) + set_target_properties(scribed_shaders PROPERTIES FOLDER "Shaders") + add_dependencies(scribed_shaders shadergen) + + add_custom_target(spirv_shaders SOURCES ${SPIRV_SHADERS}) + set_target_properties(spirv_shaders PROPERTIES FOLDER "Shaders") + add_dependencies(spirv_shaders shadergen) + + add_custom_target(reflected_shaders SOURCES ${REFLECTED_SHADERS}) set_target_properties(reflected_shaders PROPERTIES FOLDER "Shaders") + add_dependencies(reflected_shaders shadergen) + + message(STATUS "Shader processing end") endmacro() + + diff --git a/cmake/macros/ConfigureCCache.cmake b/cmake/macros/ConfigureCCache.cmake new file mode 100644 index 0000000000..bec159ef09 --- /dev/null +++ b/cmake/macros/ConfigureCCache.cmake @@ -0,0 +1,45 @@ +# +# ConfigureCCache.cmake +# cmake/macros +# +# Created by Clement Brisset on 10/10/18. +# Copyright 2018 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html +# + +macro(configure_ccache) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + message(STATUS "Configuring ccache") + + # Set up wrapper scripts + set(C_LAUNCHER "${CCACHE_PROGRAM}") + set(CXX_LAUNCHER "${CCACHE_PROGRAM}") + + set(LAUNCH_C_IN "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/launch-c.in") + set(LAUNCH_CXX_IN "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/launch-cxx.in") + set(LAUNCH_C "${CMAKE_BINARY_DIR}/CMakeFiles/launch-c") + set(LAUNCH_CXX "${CMAKE_BINARY_DIR}/CMakeFiles/launch-cxx") + + configure_file(${LAUNCH_C_IN} ${LAUNCH_C}) + configure_file(${LAUNCH_CXX_IN} ${LAUNCH_CXX}) + execute_process(COMMAND chmod a+rx ${LAUNCH_C} ${LAUNCH_CXX}) + + if(CMAKE_GENERATOR STREQUAL "Xcode") + # Set Xcode project attributes to route compilation and linking + # through our scripts + set(CMAKE_XCODE_ATTRIBUTE_CC ${LAUNCH_C}) + set(CMAKE_XCODE_ATTRIBUTE_CXX ${LAUNCH_CXX}) + set(CMAKE_XCODE_ATTRIBUTE_LD ${LAUNCH_C}) + set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS ${LAUNCH_CXX}) + else() + # Support Unix Makefiles and Ninja + set(CMAKE_C_COMPILER_LAUNCHER ${LAUNCH_C}) + set(CMAKE_CXX_COMPILER_LAUNCHER ${LAUNCH_CXX}) + endif() + else() + message(WARNING "Could not find ccache") + endif() +endmacro() diff --git a/cmake/macros/ManuallyInstallOpenSSLForQt.cmake b/cmake/macros/ManuallyInstallOpenSSLForQt.cmake index ea91bbb83b..eae0eaca58 100644 --- a/cmake/macros/ManuallyInstallOpenSSLForQt.cmake +++ b/cmake/macros/ManuallyInstallOpenSSLForQt.cmake @@ -19,13 +19,13 @@ macro(manually_install_openssl_for_qt) find_package(OpenSSL REQUIRED) install( - FILES "${OPENSSL_DLL_PATH}/ssleay32.dll" + FILES "${VCPKG_INSTALL_ROOT}/bin/ssleay32.dll" DESTINATION ${TARGET_INSTALL_DIR} COMPONENT ${TARGET_INSTALL_COMPONENT} ) install( - FILES "${OPENSSL_DLL_PATH}/libeay32.dll" + FILES "${VCPKG_INSTALL_ROOT}/bin/libeay32.dll" DESTINATION ${TARGET_INSTALL_DIR} COMPONENT ${TARGET_INSTALL_COMPONENT} ) diff --git a/cmake/macros/TargetBullet.cmake b/cmake/macros/TargetBullet.cmake index 48fe0e0c05..1f4050dd42 100644 --- a/cmake/macros/TargetBullet.cmake +++ b/cmake/macros/TargetBullet.cmake @@ -16,7 +16,6 @@ macro(TARGET_BULLET) list(APPEND BULLET_LIBRARIES ${LIB_DIR}/libLinearMath.a) list(APPEND BULLET_LIBRARIES ${LIB_DIR}/libBulletSoftBody.a) else() - add_dependency_external_projects(bullet) find_package(Bullet REQUIRED) endif() # perform the system include hack for OS X to ignore warnings diff --git a/cmake/macros/TargetDraco.cmake b/cmake/macros/TargetDraco.cmake index c198ac35b0..9dbfa865b8 100755 --- a/cmake/macros/TargetDraco.cmake +++ b/cmake/macros/TargetDraco.cmake @@ -1,18 +1,24 @@ 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 TYPE INTERNAL) - set(LIB_DIR ${INSTALL_DIR}/lib) list(APPEND DRACO_LIBRARIES ${LIB_DIR}/libdraco.a) list(APPEND DRACO_LIBRARIES ${LIB_DIR}/libdracodec.a) list(APPEND DRACO_LIBRARIES ${LIB_DIR}/libdracoenc.a) + target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARIES}) else() - add_dependency_external_projects(draco) - find_package(Draco REQUIRED) - list(APPEND DRACO_LIBRARIES ${DRACO_LIBRARY}) - list(APPEND DRACO_LIBRARIES ${DRACO_ENCODER_LIBRARY}) + set(LIB_SEARCH_PATH_RELEASE ${VCPKG_INSTALL_ROOT}/lib/) + set(LIB_SEARCH_PATH_DEBUG ${VCPKG_INSTALL_ROOT}/debug/lib/) + foreach(LIB ${LIBS}) + find_library(${LIB}_LIBPATH ${LIB} PATHS ${LIB_SEARCH_PATH_RELEASE} NO_DEFAULT_PATH) + list(APPEND DRACO_LIBRARY_RELEASE ${${LIB}_LIBPATH}) + find_library(${LIB}D_LIBPATH ${LIB} PATHS ${LIB_SEARCH_PATH_DEBUG} NO_DEFAULT_PATH) + list(APPEND DRACO_LIBRARY_DEBUG ${${LIB}D_LIBPATH}) + endforeach() + select_library_configurations(DRACO) + target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY}) endif() - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARIES}) -endmacro() +endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetEtc2Comp.cmake b/cmake/macros/TargetEtc2Comp.cmake index 44152a58d2..d6668e62eb 100644 --- a/cmake/macros/TargetEtc2Comp.cmake +++ b/cmake/macros/TargetEtc2Comp.cmake @@ -8,15 +8,16 @@ macro(TARGET_ETC2COMP) if (ANDROID) set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/etc2comp) - set(ETC2COMP_INCLUDE_DIRS "${INSTALL_DIR}/include/Etc" "${INSTALL_DIR}/include/EtcCodec") + set(ETC2COMP_INCLUDE_DIRS "${INSTALL_DIR}/include" "${INSTALL_DIR}/include/Etc" "${INSTALL_DIR}/include/EtcCodec") set(ETC2COMP_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libEtcLib.a) set(ETC2COMP_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libEtcLib.a) - select_library_configurations(ETC2COMP) + target_include_directories(${TARGET_NAME} PRIVATE ${ETC2COMP_INCLUDE_DIRS}) else() - add_dependency_external_projects(etc2comp) - find_package(Etc2Comp REQUIRED) + find_library(ETC2COMP_LIBRARY_DEBUG EtcLib PATHS ${VCPKG_INSTALL_ROOT}/debug/lib/ NO_DEFAULT_PATH) + find_library(ETC2COMP_LIBRARY_RELEASE EtcLib PATHS ${VCPKG_INSTALL_ROOT}/lib/ NO_DEFAULT_PATH) endif() - target_include_directories(${TARGET_NAME} PRIVATE ${ETC2COMP_INCLUDE_DIRS}) + select_library_configurations(ETC2COMP) target_link_libraries(${TARGET_NAME} ${ETC2COMP_LIBRARIES}) endmacro() + diff --git a/cmake/macros/TargetGli.cmake b/cmake/macros/TargetGli.cmake index 664fcc00c5..2ed1020b4b 100644 --- a/cmake/macros/TargetGli.cmake +++ b/cmake/macros/TargetGli.cmake @@ -6,7 +6,6 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_GLI) - add_dependency_external_projects(gli) - find_package(GLI REQUIRED) - target_include_directories(${TARGET_NAME} PUBLIC ${GLI_INCLUDE_DIRS}) + # We use vcpkg for both gli and glm, so we just re-use the target_glm macro here + target_glm() endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetGlm.cmake b/cmake/macros/TargetGlm.cmake index b58e6ba177..99ea9de755 100644 --- a/cmake/macros/TargetGlm.cmake +++ b/cmake/macros/TargetGlm.cmake @@ -7,10 +7,12 @@ # macro(TARGET_GLM) if (ANDROID) - set(GLM_INCLUDE_DIRS "${HIFI_ANDROID_PRECOMPILED}/glm/include") + target_include_directories(${TARGET_NAME} PUBLIC "${VCPKG_INSTALL_ROOT}/include") else() - add_dependency_external_projects(glm) - find_package(GLM REQUIRED) + find_package(glm CONFIG REQUIRED) + target_link_libraries(${TARGET_NAME} glm) endif() - target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + target_compile_definitions(${TARGET_NAME} PUBLIC GLM_FORCE_RADIANS) + target_compile_definitions(${TARGET_NAME} PUBLIC GLM_ENABLE_EXPERIMENTAL) + target_compile_definitions(${TARGET_NAME} PUBLIC GLM_FORCE_CTOR_INIT) endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetJson.cmake b/cmake/macros/TargetJson.cmake index 9262c2ce48..b96ed90da5 100644 --- a/cmake/macros/TargetJson.cmake +++ b/cmake/macros/TargetJson.cmake @@ -6,7 +6,6 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_JSON) - add_dependency_external_projects(json) - find_package(JSON REQUIRED) - target_include_directories(${TARGET_NAME} PUBLIC ${JSON_INCLUDE_DIRS}) + # We use vcpkg for both json and glm, so we just re-use the target_glm macro here + target_glm() endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetNvtt.cmake b/cmake/macros/TargetNvtt.cmake index b8198c0cf0..8227355cb9 100644 --- a/cmake/macros/TargetNvtt.cmake +++ b/cmake/macros/TargetNvtt.cmake @@ -15,12 +15,17 @@ macro(TARGET_NVTT) list(APPEND NVTT_LIBS "${NVTT_LIB_DIR}/libnvimage.so") list(APPEND NVTT_LIBS "${NVTT_LIB_DIR}/libnvtt.so") set(NVTT_LIBRARIES ${NVTT_LIBS} CACHE TYPE INTERNAL) + target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS}) else() - add_dependency_external_projects(nvtt) - find_package(NVTT REQUIRED) - add_paths_to_fixup_libs(${NVTT_DLL_PATH}) + find_library(NVTT_LIBRARY_RELEASE nvtt PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + find_library(NVTT_LIBRARY_DEBUG nvtt PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + select_library_configurations(NVTT) endif() - target_include_directories(${TARGET_NAME} PRIVATE ${NVTT_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${NVTT_LIBRARIES}) + if ((NOT WIN32) AND (NOT ANDROID) AND (NOT APPLE)) + find_package(OpenMP) + target_link_libraries(${TARGET_NAME} OpenMP::OpenMP_C OpenMP::OpenMP_CXX) + endif() + endmacro() diff --git a/cmake/macros/TargetOpenSSL.cmake b/cmake/macros/TargetOpenSSL.cmake index 82601bf6aa..3faaab5801 100644 --- a/cmake/macros/TargetOpenSSL.cmake +++ b/cmake/macros/TargetOpenSSL.cmake @@ -11,15 +11,8 @@ macro(TARGET_OPENSSL) set(OPENSSL_INCLUDE_DIR "${OPENSSL_INSTALL_DIR}/include" CACHE TYPE INTERNAL) set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE TYPE INTERNAL) else() - + # using VCPKG for OpenSSL find_package(OpenSSL REQUIRED) - - if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") - # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto - message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." - "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") - endif() - endif() include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") diff --git a/cmake/macros/TargetOpenVR.cmake b/cmake/macros/TargetOpenVR.cmake new file mode 100644 index 0000000000..6c08aa605a --- /dev/null +++ b/cmake/macros/TargetOpenVR.cmake @@ -0,0 +1,13 @@ +# +# Created by Bradley Austin Davis on 2018/10/24 +# Copyright 2013-2018 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_OPENVR) + find_library(OPENVR_LIBRARY_RELEASE NAMES openvr_api PATHS ${VCPKG_INSTALL_ROOT}/lib) + find_library(OPENVR_LIBRARY_DEBUG NAMES openvr_api PATHS ${VCPKG_INSTALL_ROOT}/debug/lib) + select_library_configurations(OPENVR) + target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARY}) +endmacro() diff --git a/cmake/macros/TargetPython.cmake b/cmake/macros/TargetPython.cmake new file mode 100644 index 0000000000..cd0ea0f34c --- /dev/null +++ b/cmake/macros/TargetPython.cmake @@ -0,0 +1,22 @@ +macro(TARGET_PYTHON) + if (NOT HIFI_PYTHON_EXEC) + # Find the python interpreter + if (CAME_VERSION VERSION_LESS 3.12) + # this logic is deprecated in CMake after 3.12 + # FIXME eventually we should make 3.12 the min cmake verion and just use the Python3 find_package path + set(Python_ADDITIONAL_VERSIONS 3) + find_package(PythonInterp) + set(HIFI_PYTHON_VERSION ${PYTHON_VERSION_STRING}) + set(HIFI_PYTHON_EXEC ${PYTHON_EXECUTABLE}) + else() + # the new hotness + find_package(Python3) + set(HIFI_PYTHON_VERSION ${Python3_VERSION}) + set(HIFI_PYTHON_EXEC ${Python3_EXECUTABLE}) + endif() + + if ((NOT HIFI_PYTHON_EXEC) OR (HIFI_PYTHON_VERSION VERSION_LESS 3.5)) + message(FATAL_ERROR "Unable to locate Python interpreter 3.5 or higher") + endif() + endif() +endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetSDL2.cmake b/cmake/macros/TargetSDL2.cmake index ee2328dfff..8163a5ebd9 100644 --- a/cmake/macros/TargetSDL2.cmake +++ b/cmake/macros/TargetSDL2.cmake @@ -6,9 +6,12 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_SDL2) - add_dependency_external_projects(sdl2) - find_package(SDL2 REQUIRED) - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SDL2_INCLUDE_DIR}) - target_link_libraries(${TARGET_NAME} ${SDL2_LIBRARY}) + # using VCPKG for SDL2 + find_package(SDL2 CONFIG REQUIRED) + if (WIN32) + target_link_libraries(${TARGET_NAME} SDL2::SDL2) + else() + target_link_libraries(${TARGET_NAME} SDL2::SDL2-static) + endif() add_definitions(-DHAVE_SDL2) -endmacro() \ No newline at end of file +endmacro() diff --git a/cmake/macros/TargetSPIRV.cmake b/cmake/macros/TargetSPIRV.cmake new file mode 100644 index 0000000000..94c9df9d13 --- /dev/null +++ b/cmake/macros/TargetSPIRV.cmake @@ -0,0 +1,15 @@ +macro(TARGET_SPIRV) + add_dependency_external_projects(spirv_cross) + target_link_libraries(${TARGET_NAME} ${SPIRV_CROSS_LIBRARIES}) + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SPIRV_CROSS_INCLUDE_DIRS}) + + # spirv-tools requires spirv-headers + add_dependency_external_projects(spirv_headers) + add_dependency_external_projects(spirv_tools) + target_link_libraries(${TARGET_NAME} ${SPIRV_TOOLS_LIBRARIES}) + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SPIRV_TOOLS_INCLUDE_DIRS}) + + add_dependency_external_projects(glslang) + target_link_libraries(${TARGET_NAME} ${GLSLANG_LIBRARIES}) + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${GLSLANG_INCLUDE_DIRS}) +endmacro() diff --git a/cmake/macros/TargetSpirvBinaries.cmake b/cmake/macros/TargetSpirvBinaries.cmake new file mode 100644 index 0000000000..6cc102d38e --- /dev/null +++ b/cmake/macros/TargetSpirvBinaries.cmake @@ -0,0 +1,10 @@ +# +# Created by Bradley Austin Davis on 2016/02/16 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_SPIRV_BINARIES) + add_dependency_external_projects(spirv_binaries) +endmacro() + diff --git a/cmake/macros/TargetTBB.cmake b/cmake/macros/TargetTBB.cmake index 1e2e69eeaa..b2aeeb99aa 100644 --- a/cmake/macros/TargetTBB.cmake +++ b/cmake/macros/TargetTBB.cmake @@ -13,12 +13,17 @@ if (ANDROID) set(TBB_LIBRARY ${TBB_INSTALL_DIR}/lib/release/libtbb.so CACHE FILEPATH "TBB library location") set(TBB_MALLOC_LIBRARY ${TBB_INSTALL_DIR}/lib/release/libtbbmalloc.so CACHE FILEPATH "TBB malloc library location") set(TBB_LIBRARIES ${TBB_LIBRARY} ${TBB_MALLOC_LIBRARY}) -else() + target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) +elseif(APPLE) add_dependency_external_projects(tbb) find_package(TBB REQUIRED) + target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) + target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) +else() + # using VCPKG for TBB + find_package(TBB CONFIG REQUIRED) + target_link_libraries(${TARGET_NAME} TBB::tbb) endif() -target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) -target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) - endmacro() diff --git a/cmake/macros/TargetVulkan.cmake b/cmake/macros/TargetVulkan.cmake new file mode 100644 index 0000000000..e220487eb5 --- /dev/null +++ b/cmake/macros/TargetVulkan.cmake @@ -0,0 +1,11 @@ +# +# Created by Bradley Austin Davis on 2016/02/16 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_VULKAN) + find_package(Vulkan REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${VULKAN_INCLUDE_DIR}) + target_link_libraries(${TARGET_NAME} ${VULKAN_LIBRARY}) +endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetZlib.cmake b/cmake/macros/TargetZlib.cmake index 3eb0322ea0..79dce01a3d 100644 --- a/cmake/macros/TargetZlib.cmake +++ b/cmake/macros/TargetZlib.cmake @@ -1,22 +1,13 @@ -# +# # Copyright 2015 High Fidelity, Inc. # Created by Bradley Austin Davis on 2015/10/10 # # Distributed under the Apache License, Version 2.0. # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# +# macro(TARGET_ZLIB) - - if (WIN32) - add_dependency_external_projects(zlib) - endif() - + # using VCPKG for zlib find_package(ZLIB REQUIRED) - - if (WIN32) - add_paths_to_fixup_libs(${ZLIB_DLL_PATH}) - endif() - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) endmacro() diff --git a/cmake/modules/FindDraco.cmake b/cmake/modules/FindDraco.cmake deleted file mode 100644 index 342797b62e..0000000000 --- a/cmake/modules/FindDraco.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# -# FindDraco.cmake -# -# Try to find Draco libraries and include path. -# Once done this will define -# -# DRACO_FOUND -# DRACO_INCLUDE_DIRS -# DRACO_LIBRARY -# DRACO_ENCODER_LIBRARY -# DRACO_DECODER_LIBRARY -# -# Created on 8/8/2017 by Stephen Birarda -# Copyright 2017 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("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("draco") - -find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src include HINTS ${DRACO_SEARCH_DIRS}) - -find_library(DRACO_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) -find_library(DRACO_ENCODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) -find_library(DRACO_DECODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(DRACO DEFAULT_MSG DRACO_INCLUDE_DIRS DRACO_LIBRARY DRACO_ENCODER_LIBRARY DRACO_DECODER_LIBRARY) diff --git a/cmake/modules/FindEtc2Comp.cmake b/cmake/modules/FindEtc2Comp.cmake deleted file mode 100644 index 1b990368fd..0000000000 --- a/cmake/modules/FindEtc2Comp.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# -# FindEtc2Comp.cmake -# -# Try to find the Etc2Comp compression library. -# -# Once done this will define -# -# ETC2COMP_FOUND - system found Etc2Comp -# ETC2COMP_INCLUDE_DIRS - the Etc2Comp include directory -# ETC2COMP_LIBRARIES - link to this to use Etc2Comp -# -# Created on 5/2/2018 by Sam Gondelman -# 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 -# - -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("etc2comp") - -find_path(ETC_INCLUDE_DIR NAMES Etc.h HINTS ${ETC2COMP_SEARCH_DIRS}) -find_path(ETCCODEC_INCLUDE_DIR NAMES EtcBlock4x4.h HINTS ${ETC2COMP_SEARCH_DIRS}) -set(ETC2COMP_INCLUDE_DIRS "${ETC_INCLUDE_DIR}" "${ETCCODEC_INCLUDE_DIR}") - -find_library(ETC2COMP_LIBRARY_DEBUG NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Debug HINTS ${ETC2COMP_SEARCH_DIRS}) -find_library(ETC2COMP_LIBRARY_RELEASE NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Release EtcLib HINTS ${ETC2COMP_SEARCH_DIRS}) - -include(SelectLibraryConfigurations) -select_library_configurations(ETC2COMP) - -set(ETC2COMP_LIBRARIES ${ETC2COMP_LIBRARY}) - -find_package_handle_standard_args(ETC2COMP "Could NOT find ETC2COMP, try to set the path to ETC2COMP root folder in the system variable ETC2COMP_ROOT_DIR or create a directory etc2comp in HIFI_LIB_DIR and paste the necessary files there" - ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES) - -mark_as_advanced(ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES ETC2COMP_SEARCH_DIRS) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake deleted file mode 100644 index f4eca0eddf..0000000000 --- a/cmake/modules/FindGLEW.cmake +++ /dev/null @@ -1,33 +0,0 @@ -# -# FindGLEW.cmake -# -# Try to find GLEW library and include path. Note that this only handles static GLEW. -# Once done this will define -# -# GLEW_FOUND -# GLEW_INCLUDE_DIRS -# GLEW_LIBRARIES -# -# Created on 2/6/2014 by Stephen Birarda -# Copyright 2014 High Fidelity, Inc. -# -# Adapted from FindGLEW.cmake available in the nvidia-texture-tools repository -# (https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/cmake/FindGLEW.cmake?r=96) -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# - -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("glew") - -find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${GLEW_SEARCH_DIRS}) - -find_library(GLEW_LIBRARY_RELEASE glew32 PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS}) -find_library(GLEW_LIBRARY_DEBUG glew32d PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS}) - -include(SelectLibraryConfigurations) -select_library_configurations(GLEW) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindJSON.cmake b/cmake/modules/FindJSON.cmake deleted file mode 100644 index d5011bd5dd..0000000000 --- a/cmake/modules/FindJSON.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# -# Created by Bradley Austin Davis on 2018/07/22 -# Copyright 2013-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 -# - -# setup hints for JSON search -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("json") - -# locate header -find_path(JSON_INCLUDE_DIRS "json/json.hpp" HINTS ${JSON_SEARCH_DIRS}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(JSON DEFAULT_MSG JSON_INCLUDE_DIRS) - -mark_as_advanced(JSON_INCLUDE_DIRS JSON_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake deleted file mode 100644 index 0619c4d587..0000000000 --- a/cmake/modules/FindOpenSSL.cmake +++ /dev/null @@ -1,174 +0,0 @@ -# - Try to find the OpenSSL encryption library -# Once done this will define -# -# OPENSSL_ROOT_DIR - Set this variable to the root installation of OpenSSL -# -# Read-Only variables: -# OPENSSL_FOUND - system has the OpenSSL library -# OPENSSL_INCLUDE_DIR - the OpenSSL include directory -# OPENSSL_LIBRARIES - The libraries needed to use OpenSSL -# OPENSSL_VERSION - This is set to $major.$minor.$revision$path (eg. 0.9.8s) -# -# Modified on 7/16/2014 by Stephen Birarda -# This is an adapted version of the FindOpenSSL.cmake module distributed with Cmake 2.8.12.2 -# The original license for that file is displayed below. -# -#============================================================================= -# Copyright 2006-2009 Kitware, Inc. -# Copyright 2006 Alexander Neundorf -# Copyright 2009-2011 Mathieu Malaterre -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -if (UNIX) - find_package(PkgConfig QUIET) - pkg_check_modules(_OPENSSL QUIET openssl) -endif () - -if (WIN32) - if (("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")) - set(_OPENSSL_ROOT_HINTS_AND_PATHS $ENV{VCPKG_ROOT}/installed/x64-windows) - else() - set(_OPENSSL_ROOT_HINTS_AND_PATHS $ENV{VCPKG_ROOT}/installed/x86-windows) - endif() -else () - include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") - hifi_library_search_hints("openssl") - - set(_OPENSSL_ROOT_HINTS_AND_PATHS ${OPENSSL_SEARCH_DIRS}) -endif () - -find_path(OPENSSL_INCLUDE_DIR NAMES openssl/ssl.h HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_INCLUDEDIR} - PATH_SUFFIXES include -) - -if (WIN32 AND NOT CYGWIN) - if (MSVC) - # Using vcpkg builds of openssl - find_library(LIB_EAY_LIBRARY_RELEASE NAMES libeay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib") - find_library(SSL_EAY_LIBRARY_RELEASE NAMES ssleay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib") - - include(SelectLibraryConfigurations) - select_library_configurations(LIB_EAY) - select_library_configurations(SSL_EAY) - set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY}) - find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} NO_DEFAULT_PATH) - endif() -else() - - find_library(OPENSSL_SSL_LIBRARY NAMES ssl ssleay32 ssleay32MD HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_LIBDIR} - PATH_SUFFIXES lib - ) - - find_library(OPENSSL_CRYPTO_LIBRARY NAMES crypto HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_LIBDIR} - PATH_SUFFIXES lib - ) - - mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY) - - # compat defines - set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY}) - set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) - - set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) - -endif () - -function(from_hex HEX DEC) - string(TOUPPER "${HEX}" HEX) - set(_res 0) - string(LENGTH "${HEX}" _strlen) - - while (_strlen GREATER 0) - math(EXPR _res "${_res} * 16") - string(SUBSTRING "${HEX}" 0 1 NIBBLE) - string(SUBSTRING "${HEX}" 1 -1 HEX) - if (NIBBLE STREQUAL "A") - math(EXPR _res "${_res} + 10") - elseif (NIBBLE STREQUAL "B") - math(EXPR _res "${_res} + 11") - elseif (NIBBLE STREQUAL "C") - math(EXPR _res "${_res} + 12") - elseif (NIBBLE STREQUAL "D") - math(EXPR _res "${_res} + 13") - elseif (NIBBLE STREQUAL "E") - math(EXPR _res "${_res} + 14") - elseif (NIBBLE STREQUAL "F") - math(EXPR _res "${_res} + 15") - else() - math(EXPR _res "${_res} + ${NIBBLE}") - endif() - - string(LENGTH "${HEX}" _strlen) - endwhile() - - set(${DEC} ${_res} PARENT_SCOPE) -endfunction() - -if (OPENSSL_INCLUDE_DIR) - if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h") - file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str - REGEX "^#[ ]?define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*") - - # The version number is encoded as 0xMNNFFPPS: major minor fix patch status - # The status gives if this is a developer or prerelease and is ignored here. - # Major, minor, and fix directly translate into the version numbers shown in - # the string. The patch field translates to the single character suffix that - # indicates the bug fix state, which 00 -> nothing, 01 -> a, 02 -> b and so - # on. - - string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$" - "\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}") - list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR) - list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR) - from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR) - list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX) - from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX) - list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH) - - if (NOT OPENSSL_VERSION_PATCH STREQUAL "00") - from_hex("${OPENSSL_VERSION_PATCH}" _tmp) - # 96 is the ASCII code of 'a' minus 1 - math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96") - unset(_tmp) - # Once anyone knows how OpenSSL would call the patch versions beyond 'z' - # this should be updated to handle that, too. This has not happened yet - # so it is simply ignored here for now. - string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING) - endif () - - set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}") - endif () -endif () - -include(FindPackageHandleStandardArgs) - -set(OPENSSL_REQUIREMENTS OPENSSL_LIBRARIES OPENSSL_INCLUDE_DIR) -if (WIN32) - list(APPEND OPENSSL_REQUIREMENTS OPENSSL_DLL_PATH) -endif () - -if (OPENSSL_VERSION) - find_package_handle_standard_args(OpenSSL - REQUIRED_VARS - ${OPENSSL_REQUIREMENTS} - VERSION_VAR - OPENSSL_VERSION - FAIL_MESSAGE - "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR" - ) -else () - find_package_handle_standard_args(OpenSSL "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR" - ${OPENSSL_REQUIREMENTS} - ) -endif () - -mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_SEARCH_DIRS) diff --git a/cmake/modules/FindOpenVR.cmake b/cmake/modules/FindOpenVR.cmake deleted file mode 100644 index 9cbe790a7f..0000000000 --- a/cmake/modules/FindOpenVR.cmake +++ /dev/null @@ -1,21 +0,0 @@ -# -# FindLibOVR.cmake -# -# Try to find the LibOVR library to use the Oculus - -# Once done this will define -# -# OPENVR_FOUND - system found LibOVR -# OPENVR_INCLUDE_DIRS - the LibOVR include directory -# OPENVR_LIBRARIES - Link this to use LibOVR -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# - -if (NOT ANDROID) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(OPENVR DEFAULT_MSG OPENVR_INCLUDE_DIRS OPENVR_LIBRARIES) -endif() - -mark_as_advanced(OPENVR_INCLUDE_DIRS OPENVR_LIBRARIES OPENVR_SEARCH_DIRS) diff --git a/cmake/modules/FindSDL2.cmake b/cmake/modules/FindSDL2.cmake deleted file mode 100644 index 1a089bcb60..0000000000 --- a/cmake/modules/FindSDL2.cmake +++ /dev/null @@ -1,224 +0,0 @@ -# Locate SDL2 library -# This module defines -# SDL2_LIBRARY, the name of the library to link against -# SDL2_FOUND, if false, do not try to link to SDL2 -# SDL2_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL2_BUILDING_LIBRARY -# If this is defined, then no SDL2_main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL2_LIBRARY variable. -# -# Don't forget to include SDL2main.h and SDL2main.m your project for the -# OS X framework based version. (Other versions link to -lSDL2main which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration -# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library -# (SDL2.dll, libsdl2.so, SDL2.framework, etc). -# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. -# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL2_LIBRARY -# variable, but when these values are unset, SDL2_LIBRARY does not get created. -# -# -# $SDL2 is an environment variable that would -# correspond to the ./configure --prefix=$SDL2 -# used in building SDL2. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL2 guidelines. -# Added a search for SDL2main which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL2_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL2/SDL.h to just SDL.h -# This needed to change because "proper" SDL2 convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL2/ (see FreeBSD). -# -# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake -# module with the minor edit of changing "SDL" to "SDL2" where necessary. This -# was not created for redistribution, and exists temporarily pending official -# SDL2 CMake modules. -# -# Note that on windows this will only search for the 32bit libraries, to search -# for 64bit change x86/i686-w64 to x64/x86_64-w64 - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# CMake - Cross Platform Makefile Generator -# Copyright 2000-2014 Kitware, Inc. -# Copyright 2000-2011 Insight Software Consortium -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the names of Kitware, Inc., the Insight Software Consortium, -# nor the names of their contributors may be used to endorse or promote -# products derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("sdl2") - -FIND_PATH(SDL2_INCLUDE_DIR SDL.h - HINTS - ${SDL2_SEARCH_DIRS} - $ENV{SDL2} - PATH_SUFFIXES include/SDL2 include SDL2 - i686-w64-mingw32/include/SDL2 - x86_64-w64-mingw32/include/SDL2 - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local/include/SDL2 - /usr/include/SDL2 - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) - -# Lookup the 64 bit libs on x64 -IF(CMAKE_SIZEOF_VOID_P EQUAL 8) - FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 - HINTS - ${SDL2_SEARCH_DIRS} - $ENV{SDL2} - PATH_SUFFIXES lib64 lib - lib/x64 - x86_64-w64-mingw32/lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - - if (WIN32) - find_path(SDL2_DLL_PATH SDL2.dll PATH_SUFFIXES lib/x64 HINTS ${SDL2_SEARCH_DIRS}) - endif () -# On 32bit build find the 32bit libs -ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) - FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 - HINTS - ${SDL2_SEARCH_DIRS} - $ENV{SDL2} - PATH_SUFFIXES lib - lib/x86 - i686-w64-mingw32/lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - - if (WIN32) - find_path(SDL2_DLL_PATH SDL2.dll PATH_SUFFIXES lib/x86 HINTS ${SDL2_SEARCH_DIRS}) - endif () -ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) - -# SDL2 may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -SET(SDL2_FOUND "NO") - IF(SDL2_LIBRARY_TEMP) - # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(MINGW) - - # Set the final string here so the GUI reflects the final state. - SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") - - SET(SDL2_FOUND "YES") -ENDIF(SDL2_LIBRARY_TEMP) - -INCLUDE(FindPackageHandleStandardArgs) - -set(SDL2_REQUIREMENTS SDL2_LIBRARY SDL2_INCLUDE_DIR) -if (WIN32) - list(APPEND SDL2_REQUIREMENTS SDL2_DLL_PATH) -endif () - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS ${SDL2_REQUIREMENTS}) - -if (WIN32) - add_paths_to_fixup_libs(${SDL2_DLL_PATH}) -endif () diff --git a/cmake/ports/bullet3/CONTROL b/cmake/ports/bullet3/CONTROL new file mode 100644 index 0000000000..4941ec3e7e --- /dev/null +++ b/cmake/ports/bullet3/CONTROL @@ -0,0 +1,3 @@ +Source: bullet3 +Version: ab8f16961e19a86ee20c6a1d61f662392524cc77 +Description: Bullet Physics is a professional collision detection, rigid body, and soft body dynamics library diff --git a/cmake/ports/bullet3/portfile.cmake b/cmake/ports/bullet3/portfile.cmake new file mode 100644 index 0000000000..d4e7aaf787 --- /dev/null +++ b/cmake/ports/bullet3/portfile.cmake @@ -0,0 +1,57 @@ +# Common Ambient Variables: +# CURRENT_BUILDTREES_DIR = ${VCPKG_ROOT_DIR}\buildtrees\${PORT} +# CURRENT_PACKAGES_DIR = ${VCPKG_ROOT_DIR}\packages\${PORT}_${TARGET_TRIPLET} +# CURRENT_PORT_DIR = ${VCPKG_ROOT_DIR}\ports\${PORT} +# PORT = current port name (zlib, etc) +# TARGET_TRIPLET = current triplet (x86-windows, x64-windows-static, etc) +# VCPKG_CRT_LINKAGE = C runtime linkage type (static, dynamic) +# VCPKG_LIBRARY_LINKAGE = target library linkage type (static, dynamic) +# VCPKG_ROOT_DIR = +# VCPKG_TARGET_ARCHITECTURE = target architecture (x64, x86, arm) +# +include(vcpkg_common_functions) + +if (VCPKG_LIBRARY_LINKAGE STREQUAL dynamic) + message(WARNING "Dynamic not supported, building static") + set(VCPKG_LIBRARY_LINKAGE static) + set(VCPKG_CRT_LINKAGE dynamic) +endif() + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO bulletphysics/bullet3 + REF ab8f16961e19a86ee20c6a1d61f662392524cc77 + SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55 + HEAD_REF master +) + + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + OPTIONS + -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=ON + -DUSE_MSVC_RUNTIME_LIBRARY_DLL=ON + -DUSE_GLUT=0 + -DUSE_DX11=0 + -DBUILD_DEMOS=OFF + -DBUILD_OPENGL3_DEMOS=OFF + -DBUILD_BULLET3=OFF + -DBUILD_BULLET2_DEMOS=OFF + -DBUILD_CPU_DEMOS=OFF + -DBUILD_EXTRAS=OFF + -DBUILD_UNIT_TESTS=OFF + -DBUILD_SHARED_LIBS=ON + -DINSTALL_LIBS=ON +) + +vcpkg_install_cmake() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/cmake) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/cmake) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/include/bullet/BulletInverseDynamics/details) + +vcpkg_copy_pdbs() + +# Handle copyright +file(INSTALL ${SOURCE_PATH}/LICENSE.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/bullet3 RENAME copyright) \ No newline at end of file diff --git a/cmake/ports/draco/CONTROL b/cmake/ports/draco/CONTROL new file mode 100644 index 0000000000..faa63303ae --- /dev/null +++ b/cmake/ports/draco/CONTROL @@ -0,0 +1,4 @@ +Source: draco +Version: 1.3.3 +Description: A library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. +Build-Depends: diff --git a/cmake/ports/draco/portfile.cmake b/cmake/ports/draco/portfile.cmake new file mode 100644 index 0000000000..853d45e862 --- /dev/null +++ b/cmake/ports/draco/portfile.cmake @@ -0,0 +1,56 @@ +# Common Ambient Variables: +# CURRENT_BUILDTREES_DIR = ${VCPKG_ROOT_DIR}\buildtrees\${PORT} +# CURRENT_PACKAGES_DIR = ${VCPKG_ROOT_DIR}\packages\${PORT}_${TARGET_TRIPLET} +# CURRENT_PORT DIR = ${VCPKG_ROOT_DIR}\ports\${PORT} +# PORT = current port name (zlib, etc) +# TARGET_TRIPLET = current triplet (x86-windows, x64-windows-static, etc) +# VCPKG_CRT_LINKAGE = C runtime linkage type (static, dynamic) +# VCPKG_LIBRARY_LINKAGE = target library linkage type (static, dynamic) +# VCPKG_ROOT_DIR = +# VCPKG_TARGET_ARCHITECTURE = target architecture (x64, x86, arm) +# + +if (VCPKG_LIBRARY_LINKAGE STREQUAL dynamic) + message(STATUS "Warning: Dynamic building not supported yet. Building static.") + set(VCPKG_LIBRARY_LINKAGE static) +endif() + +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO google/draco + REF 1.3.3 + SHA512 80ed5a623046822f5bb26b2454c8ee8cc93ffe9eb3012e8461cefdfc577b26d69a92ea0f0c5e14f5f48c1ef99f9a7263b01710df376792e74358ae14e49c3897 + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA +) + +vcpkg_install_cmake() + +vcpkg_fixup_cmake_targets(CONFIG_PATH lib/draco/cmake) + +# Install tools and plugins +file(GLOB TOOLS "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/*.exe") +if(TOOLS) + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/tools/draco) + file(COPY ${TOOLS} DESTINATION ${CURRENT_PACKAGES_DIR}/tools/draco) +endif() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/lib/draco) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/bin) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/draco) + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) + +vcpkg_copy_pdbs() + +# Handle copyright +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/draco) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/draco/LICENSE ${CURRENT_PACKAGES_DIR}/share/draco/copyright) diff --git a/cmake/ports/etc2comp/CONTROL b/cmake/ports/etc2comp/CONTROL new file mode 100644 index 0000000000..e9034dea19 --- /dev/null +++ b/cmake/ports/etc2comp/CONTROL @@ -0,0 +1,3 @@ +Source: etc2comp +Version: 7f1843bf07825c21cab711360c1ddbad04641036 +Description: ETC2 image compression library diff --git a/cmake/ports/etc2comp/portfile.cmake b/cmake/ports/etc2comp/portfile.cmake new file mode 100644 index 0000000000..d25f24cd39 --- /dev/null +++ b/cmake/ports/etc2comp/portfile.cmake @@ -0,0 +1,39 @@ +# Common Ambient Variables: +# CURRENT_BUILDTREES_DIR = ${VCPKG_ROOT_DIR}\buildtrees\${PORT} +# CURRENT_PACKAGES_DIR = ${VCPKG_ROOT_DIR}\packages\${PORT}_${TARGET_TRIPLET} +# CURRENT_PORT_DIR = ${VCPKG_ROOT_DIR}\ports\${PORT} +# PORT = current port name (zlib, etc) +# TARGET_TRIPLET = current triplet (x86-windows, x64-windows-static, etc) +# VCPKG_CRT_LINKAGE = C runtime linkage type (static, dynamic) +# VCPKG_LIBRARY_LINKAGE = target library linkage type (static, dynamic) +# VCPKG_ROOT_DIR = +# VCPKG_TARGET_ARCHITECTURE = target architecture (x64, x86, arm) +# +if (VCPKG_LIBRARY_LINKAGE STREQUAL dynamic) + message(STATUS "Warning: Dynamic building not supported yet. Building static.") + set(VCPKG_LIBRARY_LINKAGE static) + set(VCPKG_CRT_LINKAGE dynamic) +endif() + +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO highfidelity/etc2comp + REF 7f1843bf07825c21cab711360c1ddbad04641036 + SHA512 d747076acda8537d39585858c793a35c3dcc9ef283d723619a47f8c81ec1454c95b3340ad35f0655a939eae5b8271c801c48a9a7568311a01903a344c44af25b + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} +) + +vcpkg_install_cmake() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/etc2comp) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/etc2comp/LICENSE ${CURRENT_PACKAGES_DIR}/share/etc2comp/copyright) + +vcpkg_copy_pdbs() + diff --git a/cmake/ports/gli/CONTROL b/cmake/ports/gli/CONTROL new file mode 100644 index 0000000000..b8edac798c --- /dev/null +++ b/cmake/ports/gli/CONTROL @@ -0,0 +1,4 @@ +Source: gli +Version: 0.8.2-1 +Build-Depends: glm +Description: OpenGL Image (GLI) https://gli.g-truc.net diff --git a/cmake/ports/gli/portfile.cmake b/cmake/ports/gli/portfile.cmake new file mode 100644 index 0000000000..3052336198 --- /dev/null +++ b/cmake/ports/gli/portfile.cmake @@ -0,0 +1,19 @@ +#header-only library +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO g-truc/gli + REF 0.8.2.0 + SHA512 c254a4e1497d0add985e4a882c552db99c512cc0e9cc72145d51a6e7deada817d624d9818099a47136a8a3ef1223a26a34e355e3c713166f0bb062e506059834 + HEAD_REF master +) + +# Put the license file where vcpkg expects it +# manual.md contains the "licenses" section for the project +file(COPY ${SOURCE_PATH}/manual.md DESTINATION ${CURRENT_PACKAGES_DIR}/share/gli/) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/gli/manual.md ${CURRENT_PACKAGES_DIR}/share/gli/copyright) + +# Copy the glm header files +file(GLOB HEADER_FILES "${SOURCE_PATH}/gli/*.hpp" "${SOURCE_PATH}/gli/core") +file(COPY ${HEADER_FILES} DESTINATION ${CURRENT_PACKAGES_DIR}/include/gli) diff --git a/cmake/ports/glm/CONTROL b/cmake/ports/glm/CONTROL new file mode 100644 index 0000000000..3da56cf543 --- /dev/null +++ b/cmake/ports/glm/CONTROL @@ -0,0 +1,3 @@ +Source: glm +Version: 0.9.9.3 +Description: OpenGL Mathematics (GLM) https://glm.g-truc.net diff --git a/cmake/ports/glm/disable_warnings_as_error.patch b/cmake/ports/glm/disable_warnings_as_error.patch new file mode 100644 index 0000000000..f87616b1ec --- /dev/null +++ b/cmake/ports/glm/disable_warnings_as_error.patch @@ -0,0 +1,13 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 756673a3..5fbc8906 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -216,7 +216,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + message("GLM: Visual C++ - ${CMAKE_CXX_COMPILER_ID} compiler") + endif() + +- add_compile_options(/W4 /WX) ++ add_compile_options(/W4) + add_compile_options(/wd4309 /wd4324 /wd4389 /wd4127 /wd4267 /wd4146 /wd4201 /wd4464 /wd4514 /wd4701 /wd4820 /wd4365) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + endif() diff --git a/cmake/ports/glm/portfile.cmake b/cmake/ports/glm/portfile.cmake new file mode 100644 index 0000000000..69ddd267cf --- /dev/null +++ b/cmake/ports/glm/portfile.cmake @@ -0,0 +1,32 @@ +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO g-truc/glm + REF 0.9.9.3 + SHA512 44152ea6438763feda3b78813287fd59d3574a9630a41647a157825bf5ce4a18fbbecae5a5ccd94acc118ed3d42cbce53d3a67f25632d0c00ab77e7de2bb4650 + HEAD_REF master +) + +vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES "${CMAKE_CURRENT_LIST_DIR}/disable_warnings_as_error.patch" +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + OPTIONS -DGLM_TEST_ENABLE=OFF +) + +vcpkg_install_cmake() + +vcpkg_fixup_cmake_targets(CONFIG_PATH "lib/cmake/glm") + +vcpkg_copy_pdbs() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) + +# Put the license file where vcpkg expects it +file(COPY ${SOURCE_PATH}/manual.md DESTINATION ${CURRENT_PACKAGES_DIR}/share/glm/) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/glm/manual.md ${CURRENT_PACKAGES_DIR}/share/glm/copyright) diff --git a/cmake/ports/glslang/CONTROL b/cmake/ports/glslang/CONTROL new file mode 100644 index 0000000000..74a2530e4c --- /dev/null +++ b/cmake/ports/glslang/CONTROL @@ -0,0 +1,3 @@ +Source: glslang +Version: untagged-048c4dbc7f021224a933-1 +Description: Khronos reference front-end for GLSL and ESSL, and sample SPIR-V generator diff --git a/cmake/ports/glslang/copyright b/cmake/ports/glslang/copyright new file mode 100644 index 0000000000..dfffea6a8c --- /dev/null +++ b/cmake/ports/glslang/copyright @@ -0,0 +1,35 @@ +// +//Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +//Copyright (C) 2012-2013 LunarG, Inc. +// +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions +//are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +//POSSIBILITY OF SUCH DAMAGE. +// diff --git a/cmake/ports/glslang/portfile.cmake b/cmake/ports/glslang/portfile.cmake new file mode 100644 index 0000000000..72d62d26f3 --- /dev/null +++ b/cmake/ports/glslang/portfile.cmake @@ -0,0 +1,26 @@ +include(vcpkg_common_functions) + +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO KhronosGroup/glslang + REF untagged-048c4dbc7f021224a933 + SHA512 e3097dd2db88320982d7da1ddce138839daf3251935909c3998a114aeadd408760b26b2d7c7ee547fb0519895ac1853a45c23df2eecf61135849b080252e9dec + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS -DCMAKE_DEBUG_POSTFIX=d +) + +vcpkg_install_cmake() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(RENAME "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/tools") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/bin") + +# Handle copyright +file(COPY ${CMAKE_CURRENT_LIST_DIR}/copyright DESTINATION ${CURRENT_PACKAGES_DIR}/share/glslang) diff --git a/cmake/ports/hifi-client-deps/CONTROL b/cmake/ports/hifi-client-deps/CONTROL new file mode 100644 index 0000000000..7d4727b364 --- /dev/null +++ b/cmake/ports/hifi-client-deps/CONTROL @@ -0,0 +1,4 @@ +Source: hifi-client-deps +Version: 0 +Description: Collected dependencies for High Fidelity applications +Build-Depends: hifi-deps, glslang, nlohmann-json, openvr (windows), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), vulkanmemoryallocator diff --git a/cmake/ports/hifi-client-deps/portfile.cmake b/cmake/ports/hifi-client-deps/portfile.cmake new file mode 100644 index 0000000000..065116c276 --- /dev/null +++ b/cmake/ports/hifi-client-deps/portfile.cmake @@ -0,0 +1 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) diff --git a/cmake/ports/hifi-deps/CONTROL b/cmake/ports/hifi-deps/CONTROL new file mode 100644 index 0000000000..e202d0c8f8 --- /dev/null +++ b/cmake/ports/hifi-deps/CONTROL @@ -0,0 +1,4 @@ +Source: hifi-deps +Version: 0 +Description: Collected dependencies for High Fidelity applications +Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openssl (windows), tbb (!android&!osx), zlib diff --git a/cmake/ports/hifi-deps/portfile.cmake b/cmake/ports/hifi-deps/portfile.cmake new file mode 100644 index 0000000000..065116c276 --- /dev/null +++ b/cmake/ports/hifi-deps/portfile.cmake @@ -0,0 +1 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) diff --git a/cmake/ports/hifi-host-tools/CONTROL b/cmake/ports/hifi-host-tools/CONTROL new file mode 100644 index 0000000000..ec8eda965e --- /dev/null +++ b/cmake/ports/hifi-host-tools/CONTROL @@ -0,0 +1,4 @@ +Source: hifi-host-tools +Version: 0 +Description: Host build system compatible tools for building High Fidelity applications +Build-Depends: hifi-scribe, glslang, spirv-cross, spirv-tools diff --git a/cmake/ports/hifi-host-tools/portfile.cmake b/cmake/ports/hifi-host-tools/portfile.cmake new file mode 100644 index 0000000000..065116c276 --- /dev/null +++ b/cmake/ports/hifi-host-tools/portfile.cmake @@ -0,0 +1 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) diff --git a/cmake/ports/hifi-scribe/CONTROL b/cmake/ports/hifi-scribe/CONTROL new file mode 100644 index 0000000000..630bba8593 --- /dev/null +++ b/cmake/ports/hifi-scribe/CONTROL @@ -0,0 +1,3 @@ +Source: hifi-scribe +Version: 1bd638a36ca771e5a68d01985b6389b71835cbd2 +Description: Scribe is a language for generating glsl files from templates diff --git a/cmake/ports/hifi-scribe/portfile.cmake b/cmake/ports/hifi-scribe/portfile.cmake new file mode 100644 index 0000000000..2b69f7b887 --- /dev/null +++ b/cmake/ports/hifi-scribe/portfile.cmake @@ -0,0 +1,19 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO highfidelity/scribe + REF 1bd638a36ca771e5a68d01985b6389b71835cbd2 + SHA512 dbe241d86df3912e544f6b9839873f9875df54efc93822b145e7b13243eaf2e3d690bc8a28b1e52d05bdcd7e68fca6b0b2f5c43ffd0f56a9b7a50d54dcf9e31e + HEAD_REF master +) + +vcpkg_configure_cmake(SOURCE_PATH ${SOURCE_PATH}) +vcpkg_install_cmake() +vcpkg_copy_pdbs() + +# cleanup +configure_file(${SOURCE_PATH}/LICENSE ${CURRENT_PACKAGES_DIR}/share/hifi-scribe/copyright COPYONLY) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/tools) + diff --git a/cmake/ports/nlohmann-json/CONTROL b/cmake/ports/nlohmann-json/CONTROL new file mode 100644 index 0000000000..8ee107a92c --- /dev/null +++ b/cmake/ports/nlohmann-json/CONTROL @@ -0,0 +1,3 @@ +Source: nlohmann-json +Version: 3.3.0 +Description: JSON for Modern C++ diff --git a/cmake/ports/nlohmann-json/portfile.cmake b/cmake/ports/nlohmann-json/portfile.cmake new file mode 100644 index 0000000000..5cee9565e8 --- /dev/null +++ b/cmake/ports/nlohmann-json/portfile.cmake @@ -0,0 +1,18 @@ +include(vcpkg_common_functions) + +set(SOURCE_VERSION 3.3.0) + +vcpkg_download_distfile(HEADER + URLS "https://github.com/nlohmann/json/releases/download/v${SOURCE_VERSION}/json.hpp" + FILENAME "nlohmann-json-${SOURCE_VERSION}.hpp" + SHA512 c4e4bb84d1488f87a02c4e12409491225e345cc508e6dbbee1a3542fbd4953052c256d0fe78c4d3ce02d44c3a2155fe66f0c8a93a3851ddf94fec4f9f3fd6918 +) + +vcpkg_download_distfile(LICENSE + URLS "https://github.com/nlohmann/json/raw/v${SOURCE_VERSION}/LICENSE.MIT" + FILENAME "nlohmann-json-LICENSE-${SOURCE_VERSION}.txt" + SHA512 0fdb404547467f4523579acde53066badf458504d33edbb6e39df0ae145ed27d48a720189a60c225c0aab05f2aa4ce4050dcb241b56dc693f7ee9f54c8728a75 +) + +file(INSTALL ${HEADER} DESTINATION ${CURRENT_PACKAGES_DIR}/include/nlohmann RENAME json.hpp) +file(INSTALL ${LICENSE} DESTINATION ${CURRENT_PACKAGES_DIR}/share/nlohmann-json RENAME copyright) diff --git a/cmake/ports/nvtt/CONTROL b/cmake/ports/nvtt/CONTROL new file mode 100644 index 0000000000..59ba36d830 --- /dev/null +++ b/cmake/ports/nvtt/CONTROL @@ -0,0 +1,3 @@ +Source: nvtt +Version: 8c7e6b40ee5095f227b75880fabd89c99d6f34c0 +Description: Texture processing tools with support for Direct3D 10 and 11 formats. \ No newline at end of file diff --git a/cmake/ports/nvtt/portfile.cmake b/cmake/ports/nvtt/portfile.cmake new file mode 100644 index 0000000000..13b1253322 --- /dev/null +++ b/cmake/ports/nvtt/portfile.cmake @@ -0,0 +1,39 @@ +# Common Ambient Variables: +# VCPKG_ROOT_DIR = +# TARGET_TRIPLET is the current triplet (x86-windows, etc) +# PORT is the current port name (zlib, etc) +# CURRENT_BUILDTREES_DIR = ${VCPKG_ROOT_DIR}\buildtrees\${PORT} +# CURRENT_PACKAGES_DIR = ${VCPKG_ROOT_DIR}\packages\${PORT}_${TARGET_TRIPLET} +# +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO highfidelity/nvidia-texture-tools + REF 8c7e6b40ee5095f227b75880fabd89c99d6f34c0 + SHA512 f107d19dbbd6651ef2126b1422a5db8db291bf70311ac4fb1dbacb5ceaa8752fee38becbd32964f57596f0b84e1223bb2c3ff9d9c4fdc65c3e77a47836657cef + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + OPTIONS + -DBUILD_TESTS=OFF + -DBUILD_TOOLS=OFF +) + +vcpkg_install_cmake() + +if(VCPKG_LIBRARY_LINKAGE STREQUAL static) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin) +endif() + +vcpkg_copy_pdbs() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) + +# Handle copyright +file(REMOVE ${CURRENT_PACKAGES_DIR}/share/doc/nvtt/LICENSE) +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/nvtt) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/nvtt/LICENSE ${CURRENT_PACKAGES_DIR}/share/nvtt/copyright) diff --git a/cmake/ports/openssl-android/CONTROL b/cmake/ports/openssl-android/CONTROL new file mode 100644 index 0000000000..66a7b67ba6 --- /dev/null +++ b/cmake/ports/openssl-android/CONTROL @@ -0,0 +1,3 @@ +Source: openssl-android +Version: 1.0.2p +Description: OpenSSL is an open source project that provides a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library. diff --git a/cmake/ports/openssl-android/LICENSE.txt b/cmake/ports/openssl-android/LICENSE.txt new file mode 100644 index 0000000000..e953f590cb --- /dev/null +++ b/cmake/ports/openssl-android/LICENSE.txt @@ -0,0 +1,125 @@ + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a double license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + diff --git a/cmake/ports/openssl-android/portfile.cmake b/cmake/ports/openssl-android/portfile.cmake new file mode 100644 index 0000000000..54936d254d --- /dev/null +++ b/cmake/ports/openssl-android/portfile.cmake @@ -0,0 +1,24 @@ +include(vcpkg_common_functions) +set(OPENSSL_VERSION 1.1.0g) +set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src) + +message("MASTER_COPY_SOURCE_PATH ${MASTER_COPY_SOURCE_PATH}") +vcpkg_download_distfile( + OPENSSL_SOURCE_ARCHIVE + URLS https://hifi-public.s3.amazonaws.com/dependencies/android/openssl-1.1.0g_armv8.tgz?versionId=AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW + SHA512 5d7bb6e5d3db2340449e2789bcd72da821f0e57483bac46cf06f735dffb5d73c1ca7cc53dd48f3b3979d0fe22b3ae61997c516fc0c4611af4b4b7f480e42b992 + FILENAME openssl-1.1.0g_armv8.tgz +) + +vcpkg_extract_source_archive(${OPENSSL_SOURCE_ARCHIVE}) + +file(COPY ${MASTER_COPY_SOURCE_PATH}/include DESTINATION ${CURRENT_PACKAGES_DIR}) +file(GLOB LIBS ${MASTER_COPY_SOURCE_PATH}/lib/*.a) +file(COPY ${LIBS} DESTINATION ${CURRENT_PACKAGES_DIR}/libs) +file(COPY ${LIBS} DESTINATION ${CURRENT_PACKAGES_DIR}/debug/libs) +file(INSTALL ${CMAKE_CURRENT_LIST_DIR}/LICENSE.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/openssl-android RENAME copyright) + + + + + diff --git a/cmake/ports/openssl-unix/CMakeLists.txt b/cmake/ports/openssl-unix/CMakeLists.txt new file mode 100644 index 0000000000..7757a4fe78 --- /dev/null +++ b/cmake/ports/openssl-unix/CMakeLists.txt @@ -0,0 +1,136 @@ +cmake_minimum_required(VERSION 3.9) +project(openssl C) + +if(NOT SOURCE_PATH) + message(FATAL_ERROR "Requires SOURCE_PATH") +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(PLATFORM android) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64") + set(PLATFORM linux-generic64) + else() + set(PLATFORM linux-generic32) + endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(PLATFORM darwin64-x86_64-cc) +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(PLATFORM BSD-generic64) +else() + message(FATAL_ERROR "Unknown platform") +endif() + +get_filename_component(COMPILER_ROOT "${CMAKE_C_COMPILER}" DIRECTORY) + +message("CMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +message("COMPILER_ROOT=${COMPILER_ROOT}") +message("CMAKE_SYSROOT=${CMAKE_SYSROOT}") +message("CMAKE_C_FLAGS=${CMAKE_C_FLAGS}") +message("CMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}") +message("CMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}") +message("CMAKE_INCLUDE_SYSTEM_FLAG_C=${CMAKE_INCLUDE_SYSTEM_FLAG_C}") + +set(CFLAGS "${CMAKE_C_FLAGS}") +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CFLAGS "-Wno-error=unused-command-line-argument ${CMAKE_C_FLAGS}") +endif() +if(CMAKE_C_COMPILER_TARGET) + set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}") +endif() +if(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN) + set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN}${CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN}") +endif() +if(CMAKE_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT) + set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") +endif() + +string(REGEX REPLACE "^ " "" CFLAGS "${CFLAGS}") + +if(CMAKE_HOST_WIN32) + file(TO_NATIVE_PATH ENV_PATH "${COMPILER_ROOT};$ENV{PATH}") +else() + file(TO_NATIVE_PATH ENV_PATH "${COMPILER_ROOT}:$ENV{PATH}") +endif() +set(ENV{ANDROID_DEV} "${CMAKE_SYSROOT}/usr") +set(ENV{CC} "${CMAKE_C_COMPILER}") + +message("ENV{ANDROID_DEV}=$ENV{ANDROID_DEV}") + +get_filename_component(SOURCE_PATH_NAME "${SOURCE_PATH}" NAME) +set(BUILDDIR "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_PATH_NAME}") + +if(NOT EXISTS "${BUILDDIR}") + file(COPY ${SOURCE_PATH} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +endif() + +get_filename_component(MSYS_BIN_DIR "${MAKE}" DIRECTORY) + + +file(READ "${BUILDDIR}/Configure" _contents) +string(REPLACE "-mandroid" "" _contents "${_contents}") +file(WRITE "${BUILDDIR}/Configure" "${_contents}") + +if(BUILD_SHARED_LIBS) + set(SHARED shared) +else() + set(SHARED no-shared) +endif() + +if(CMAKE_HOST_WIN32) + set(ENV_COMMAND set) + set(PATH_VAR ";%PATH%") +else() + set(ENV_COMMAND export) + set(PATH_VAR ":$ENV{PATH}") +endif() + +add_custom_command( + OUTPUT "${BUILDDIR}/Makefile" + COMMAND ${ENV_COMMAND} CC=${CMAKE_C_COMPILER} + COMMAND ${ENV_COMMAND} AR=${CMAKE_AR} + COMMAND ${ENV_COMMAND} LD=${CMAKE_LINKER} + COMMAND ${ENV_COMMAND} RANLIB=${CMAKE_RANLIB} + COMMAND ${ENV_COMMAND} MAKE=${MAKE} + COMMAND ${ENV_COMMAND} MAKEDEPPROG=${CMAKE_C_COMPILER} + COMMAND ${ENV_COMMAND} "PATH=${MSYS_BIN_DIR}${PATH_VAR}" + COMMAND "${PERL}" Configure + ${SHARED} + enable-static-engine + no-ssl2 + no-krb5 + no-idea + no-bf + no-cast + no-seed + no-md2 + ${PLATFORM} + "--prefix=${CMAKE_INSTALL_PREFIX}" + "--openssldir=/etc/ssl" + ${CFLAGS} + COMMAND "${CMAKE_COMMAND}" "-DDIR=${BUILDDIR}" -P "${CMAKE_CURRENT_LIST_DIR}/remove-deps.cmake" + VERBATIM + WORKING_DIRECTORY "${BUILDDIR}" +) + +add_custom_target(depend + COMMAND ${ENV_COMMAND} "PATH=${MSYS_BIN_DIR}${PATH_VAR}" + COMMAND "${MAKE}" links # depend MAKEDEPPROG=${CMAKE_C_COMPILER} + VERBATIM + WORKING_DIRECTORY "${BUILDDIR}" + DEPENDS "${BUILDDIR}/Makefile" +) +add_custom_target(build_libs ALL + COMMAND ${ENV_COMMAND} "PATH=${MSYS_BIN_DIR}${PATH_VAR}" + COMMAND "${CMAKE_COMMAND}" -E touch "${BUILDDIR}/krb5.h" + COMMAND "${MAKE}" build_libs + VERBATIM + WORKING_DIRECTORY "${BUILDDIR}" + DEPENDS depend + BYPRODUCTS "${BUILDDIR}/libssl.a" "${BUILDDIR}/libcrypto.a" +) + +install( + FILES "${BUILDDIR}/libssl.a" "${BUILDDIR}/libcrypto.a" + DESTINATION lib +) diff --git a/cmake/ports/openssl-unix/CONTROL b/cmake/ports/openssl-unix/CONTROL new file mode 100644 index 0000000000..6413eb3712 --- /dev/null +++ b/cmake/ports/openssl-unix/CONTROL @@ -0,0 +1,3 @@ +Source: openssl-unix +Version: 1.0.2p +Description: OpenSSL is an open source project that provides a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library. diff --git a/cmake/ports/openssl-unix/ConfigureIncludeQuotesFix.patch b/cmake/ports/openssl-unix/ConfigureIncludeQuotesFix.patch new file mode 100644 index 0000000000..09494f5650 --- /dev/null +++ b/cmake/ports/openssl-unix/ConfigureIncludeQuotesFix.patch @@ -0,0 +1,13 @@ +diff --git a/Configure b/Configure +index c98107a..77ad9d3 100644 +--- a/Configure ++++ b/Configure +@@ -972,7 +972,7 @@ PROCESS_ARGS: + } + elsif (/^--with-zlib-include=(.*)$/) + { +- $withargs{"zlib-include"}="-I$1"; ++ $withargs{"zlib-include"}="-I\"$1\""; + } + elsif (/^--with-fipsdir=(.*)$/) + { diff --git a/cmake/ports/openssl-unix/EmbedSymbolsInStaticLibsZ7.patch b/cmake/ports/openssl-unix/EmbedSymbolsInStaticLibsZ7.patch new file mode 100644 index 0000000000..1a8de2c4bd --- /dev/null +++ b/cmake/ports/openssl-unix/EmbedSymbolsInStaticLibsZ7.patch @@ -0,0 +1,25 @@ +diff --git a/util/pl/VC-32.pl b/util/pl/VC-32.pl +index dba96cb..5722f6e 100644 +--- a/util/pl/VC-32.pl ++++ b/util/pl/VC-32.pl +@@ -154,9 +154,17 @@ else + $cflags=$opt_cflags.$base_cflags; + } + +-# generate symbols.pdb unconditionally +-$app_cflag.=" /Zi /Fd\$(TMP_D)/app"; +-$lib_cflag.=" /Zi /Fd\$(TMP_D)/lib"; ++# generate symbols.pdb when building dlls and embed symbols when building static libs ++if ($shlib) ++ { ++ $app_cflag.=" /Zi /Fd\$(TMP_D)/app.pdb"; ++ $lib_cflag.=" /Zi /Fd\$(TMP_D)/lib.pdb"; ++ } ++else ++ { ++ $app_cflag.=" /Z7"; ++ $lib_cflag.=" /Z7"; ++ } + $lflags.=" /debug"; + + $obj='.obj'; diff --git a/cmake/ports/openssl-unix/STRINGIFYPatch.patch b/cmake/ports/openssl-unix/STRINGIFYPatch.patch new file mode 100644 index 0000000000..dd8f9c2972 --- /dev/null +++ b/cmake/ports/openssl-unix/STRINGIFYPatch.patch @@ -0,0 +1,23 @@ +diff --git a/crypto/cversion.c b/crypto/cversion.c +index bfff699..17b7912 100644 +--- a/crypto/cversion.c ++++ b/crypto/cversion.c +@@ -56,6 +56,9 @@ + * [including the GNU Public Licence.] + */ + ++#define STRINGIFY2(x) #x ++#define STRINGIFY(x) STRINGIFY2(x) ++ + #include "cryptlib.h" + + #ifndef NO_WINDOWS_BRAINDEATH +@@ -79,7 +82,7 @@ const char *SSLeay_version(int t) + } + if (t == SSLEAY_CFLAGS) { + #ifdef CFLAGS +- return (CFLAGS); ++ return STRINGIFY(CFLAGS); + #else + return ("compiler: information not available"); + #endif diff --git a/cmake/ports/openssl-unix/portfile.cmake b/cmake/ports/openssl-unix/portfile.cmake new file mode 100644 index 0000000000..1484fc66c6 --- /dev/null +++ b/cmake/ports/openssl-unix/portfile.cmake @@ -0,0 +1,63 @@ +if(VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" OR NOT VCPKG_CMAKE_SYSTEM_NAME) + message(FATAL_ERROR "This port is only for openssl on Unix-like systems") +endif() + +include(vcpkg_common_functions) +set(OPENSSL_VERSION 1.0.2p) +set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/openssl-${OPENSSL_VERSION}) + +vcpkg_find_acquire_program(PERL) + +vcpkg_download_distfile(OPENSSL_SOURCE_ARCHIVE + URLS "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" "https://www.openssl.org/source/old/1.0.2/openssl-${OPENSSL_VERSION}.tar.gz" + FILENAME "openssl-${OPENSSL_VERSION}.tar.gz" + SHA512 958c5a7c3324bbdc8f07dfb13e11329d9a1b4452c07cf41fbd2d42b5fe29c95679332a3476d24c2dc2b88be16e4a24744aba675a05a388c0905756c77a8a2f16 +) + +vcpkg_extract_source_archive(${OPENSSL_SOURCE_ARCHIVE}) +vcpkg_apply_patches( + SOURCE_PATH ${MASTER_COPY_SOURCE_PATH} + PATCHES ${CMAKE_CURRENT_LIST_DIR}/ConfigureIncludeQuotesFix.patch + ${CMAKE_CURRENT_LIST_DIR}/STRINGIFYPatch.patch + ${CMAKE_CURRENT_LIST_DIR}/EmbedSymbolsInStaticLibsZ7.patch +) + +if(CMAKE_HOST_WIN32) + vcpkg_acquire_msys(MSYS_ROOT PACKAGES make) + set(BASH ${MSYS_ROOT}/usr/bin/bash.exe) + set(MAKE ${MSYS_ROOT}/usr/bin/make.exe) +else() + find_program(MAKE make) + if(NOT MAKE) + message(FATAL_ERROR "Could not find make. Please install it through your package manager.") + endif() +endif() + +vcpkg_configure_cmake( + SOURCE_PATH ${CMAKE_CURRENT_LIST_DIR} + PREFER_NINJA + OPTIONS + -DSOURCE_PATH=${MASTER_COPY_SOURCE_PATH} + -DPERL=${PERL} + -DMAKE=${MAKE} + OPTIONS_RELEASE + -DINSTALL_HEADERS=ON +) + +vcpkg_install_cmake() + +file(GLOB HEADERS ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/*/include/openssl/*.h) +set(RESOLVED_HEADERS) +foreach(HEADER ${HEADERS}) + get_filename_component(X "${HEADER}" REALPATH) + list(APPEND RESOLVED_HEADERS "${X}") +endforeach() + +file(INSTALL ${RESOLVED_HEADERS} DESTINATION ${CURRENT_PACKAGES_DIR}/include/openssl) +file(INSTALL ${MASTER_COPY_SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/openssl-unix RENAME copyright) + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + file(COPY ${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake DESTINATION ${CURRENT_PACKAGES_DIR}/share/openssl) +endif() + +vcpkg_test_cmake(PACKAGE_NAME OpenSSL MODULE) diff --git a/cmake/ports/openssl-unix/remove-deps.cmake b/cmake/ports/openssl-unix/remove-deps.cmake new file mode 100644 index 0000000000..53ad6ef290 --- /dev/null +++ b/cmake/ports/openssl-unix/remove-deps.cmake @@ -0,0 +1,7 @@ +file(GLOB_RECURSE MAKEFILES ${DIR}/*/Makefile) +foreach(MAKEFILE ${MAKEFILES}) + message("removing deps from ${MAKEFILE}") + file(READ "${MAKEFILE}" _contents) + string(REGEX REPLACE "\n# DO NOT DELETE THIS LINE.*" "" _contents "${_contents}") + file(WRITE "${MAKEFILE}" "${_contents}") +endforeach() diff --git a/cmake/ports/openssl-unix/usage b/cmake/ports/openssl-unix/usage new file mode 100644 index 0000000000..cf83f33916 --- /dev/null +++ b/cmake/ports/openssl-unix/usage @@ -0,0 +1,4 @@ +The package openssl is compatible with built-in CMake targets: + + find_package(OpenSSL REQUIRED) + target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto) diff --git a/cmake/ports/openssl-unix/vcpkg-cmake-wrapper.cmake b/cmake/ports/openssl-unix/vcpkg-cmake-wrapper.cmake new file mode 100644 index 0000000000..3d7255c5dd --- /dev/null +++ b/cmake/ports/openssl-unix/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,10 @@ +_find_package(${ARGS}) +if(OPENSSL_FOUND) + find_library(OPENSSL_DL_LIBRARY NAMES dl) + if(OPENSSL_DL_LIBRARY) + list(APPEND OPENSSL_LIBRARIES "dl") + if(TARGET OpenSSL::Crypto) + set_property(TARGET OpenSSL::Crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES "dl") + endif() + endif() +endif() diff --git a/cmake/ports/openssl-windows/CONTROL b/cmake/ports/openssl-windows/CONTROL new file mode 100644 index 0000000000..881c311d00 --- /dev/null +++ b/cmake/ports/openssl-windows/CONTROL @@ -0,0 +1,3 @@ +Source: openssl-windows +Version: 1.0.2p-1 +Description: OpenSSL is an open source project that provides a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library. diff --git a/cmake/ports/openssl-windows/ConfigureIncludeQuotesFix.patch b/cmake/ports/openssl-windows/ConfigureIncludeQuotesFix.patch new file mode 100644 index 0000000000..09494f5650 --- /dev/null +++ b/cmake/ports/openssl-windows/ConfigureIncludeQuotesFix.patch @@ -0,0 +1,13 @@ +diff --git a/Configure b/Configure +index c98107a..77ad9d3 100644 +--- a/Configure ++++ b/Configure +@@ -972,7 +972,7 @@ PROCESS_ARGS: + } + elsif (/^--with-zlib-include=(.*)$/) + { +- $withargs{"zlib-include"}="-I$1"; ++ $withargs{"zlib-include"}="-I\"$1\""; + } + elsif (/^--with-fipsdir=(.*)$/) + { diff --git a/cmake/ports/openssl-windows/EmbedSymbolsInStaticLibsZ7.patch b/cmake/ports/openssl-windows/EmbedSymbolsInStaticLibsZ7.patch new file mode 100644 index 0000000000..1a8de2c4bd --- /dev/null +++ b/cmake/ports/openssl-windows/EmbedSymbolsInStaticLibsZ7.patch @@ -0,0 +1,25 @@ +diff --git a/util/pl/VC-32.pl b/util/pl/VC-32.pl +index dba96cb..5722f6e 100644 +--- a/util/pl/VC-32.pl ++++ b/util/pl/VC-32.pl +@@ -154,9 +154,17 @@ else + $cflags=$opt_cflags.$base_cflags; + } + +-# generate symbols.pdb unconditionally +-$app_cflag.=" /Zi /Fd\$(TMP_D)/app"; +-$lib_cflag.=" /Zi /Fd\$(TMP_D)/lib"; ++# generate symbols.pdb when building dlls and embed symbols when building static libs ++if ($shlib) ++ { ++ $app_cflag.=" /Zi /Fd\$(TMP_D)/app.pdb"; ++ $lib_cflag.=" /Zi /Fd\$(TMP_D)/lib.pdb"; ++ } ++else ++ { ++ $app_cflag.=" /Z7"; ++ $lib_cflag.=" /Z7"; ++ } + $lflags.=" /debug"; + + $obj='.obj'; diff --git a/cmake/ports/openssl-windows/STRINGIFYPatch.patch b/cmake/ports/openssl-windows/STRINGIFYPatch.patch new file mode 100644 index 0000000000..dd8f9c2972 --- /dev/null +++ b/cmake/ports/openssl-windows/STRINGIFYPatch.patch @@ -0,0 +1,23 @@ +diff --git a/crypto/cversion.c b/crypto/cversion.c +index bfff699..17b7912 100644 +--- a/crypto/cversion.c ++++ b/crypto/cversion.c +@@ -56,6 +56,9 @@ + * [including the GNU Public Licence.] + */ + ++#define STRINGIFY2(x) #x ++#define STRINGIFY(x) STRINGIFY2(x) ++ + #include "cryptlib.h" + + #ifndef NO_WINDOWS_BRAINDEATH +@@ -79,7 +82,7 @@ const char *SSLeay_version(int t) + } + if (t == SSLEAY_CFLAGS) { + #ifdef CFLAGS +- return (CFLAGS); ++ return STRINGIFY(CFLAGS); + #else + return ("compiler: information not available"); + #endif diff --git a/cmake/ports/openssl-windows/portfile.cmake b/cmake/ports/openssl-windows/portfile.cmake new file mode 100644 index 0000000000..d58d51431e --- /dev/null +++ b/cmake/ports/openssl-windows/portfile.cmake @@ -0,0 +1,163 @@ +if(VCPKG_CMAKE_SYSTEM_NAME) + message(FATAL_ERROR "This port is only for building openssl on Windows Desktop") +endif() + +include(vcpkg_common_functions) +set(OPENSSL_VERSION 1.0.2p) +set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/openssl-${OPENSSL_VERSION}) + +vcpkg_find_acquire_program(PERL) + +get_filename_component(PERL_EXE_PATH ${PERL} DIRECTORY) +set(ENV{PATH} "$ENV{PATH};${PERL_EXE_PATH}") + +vcpkg_download_distfile(OPENSSL_SOURCE_ARCHIVE + URLS "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" "https://www.openssl.org/source/old/1.0.2/openssl-${OPENSSL_VERSION}.tar.gz" + FILENAME "openssl-${OPENSSL_VERSION}.tar.gz" + SHA512 958c5a7c3324bbdc8f07dfb13e11329d9a1b4452c07cf41fbd2d42b5fe29c95679332a3476d24c2dc2b88be16e4a24744aba675a05a388c0905756c77a8a2f16 +) + +vcpkg_extract_source_archive(${OPENSSL_SOURCE_ARCHIVE}) +vcpkg_apply_patches( + SOURCE_PATH ${MASTER_COPY_SOURCE_PATH} + PATCHES ${CMAKE_CURRENT_LIST_DIR}/ConfigureIncludeQuotesFix.patch + ${CMAKE_CURRENT_LIST_DIR}/STRINGIFYPatch.patch + ${CMAKE_CURRENT_LIST_DIR}/EmbedSymbolsInStaticLibsZ7.patch +) + +vcpkg_find_acquire_program(NASM) +get_filename_component(NASM_EXE_PATH ${NASM} DIRECTORY) +set(ENV{PATH} "${NASM_EXE_PATH};$ENV{PATH}") + +vcpkg_find_acquire_program(JOM) + +set(CONFIGURE_COMMAND ${PERL} Configure + enable-static-engine + enable-capieng + no-ssl2 + -utf-8 +) + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(OPENSSL_ARCH VC-WIN32) + set(OPENSSL_DO "ms\\do_nasm.bat") +elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(OPENSSL_ARCH VC-WIN64A) + set(OPENSSL_DO "ms\\do_win64a.bat") +else() + message(FATAL_ERROR "Unsupported target architecture: ${VCPKG_TARGET_ARCHITECTURE}") +endif() + +if(VCPKG_LIBRARY_LINKAGE STREQUAL dynamic) + set(OPENSSL_MAKEFILE "ms\\ntdll.mak") +else() + set(OPENSSL_MAKEFILE "ms\\nt.mak") +endif() + +file(REMOVE_RECURSE ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg) + + +if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(COPY ${MASTER_COPY_SOURCE_PATH} DESTINATION ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel) + set(SOURCE_PATH_RELEASE ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/openssl-${OPENSSL_VERSION}) + set(OPENSSLDIR_RELEASE ${CURRENT_PACKAGES_DIR}) + + message(STATUS "Configure ${TARGET_TRIPLET}-rel") + vcpkg_execute_required_process( + COMMAND ${CONFIGURE_COMMAND} ${OPENSSL_ARCH} "--prefix=${OPENSSLDIR_RELEASE}" "--openssldir=${OPENSSLDIR_RELEASE}" -FS + WORKING_DIRECTORY ${SOURCE_PATH_RELEASE} + LOGNAME configure-perl-${TARGET_TRIPLET}-${CMAKE_BUILD_TYPE}-rel + ) + vcpkg_execute_required_process( + COMMAND ${OPENSSL_DO} + WORKING_DIRECTORY ${SOURCE_PATH_RELEASE} + LOGNAME configure-do-${TARGET_TRIPLET}-${CMAKE_BUILD_TYPE}-rel + ) + message(STATUS "Configure ${TARGET_TRIPLET}-rel done") + + message(STATUS "Build ${TARGET_TRIPLET}-rel") + # Openssl's buildsystem has a race condition which will cause JOM to fail at some point. + # This is ok; we just do as much work as we can in parallel first, then follow up with a single-threaded build. + make_directory(${SOURCE_PATH_RELEASE}/inc32/openssl) + execute_process( + COMMAND ${JOM} -k -j $ENV{NUMBER_OF_PROCESSORS} -f ${OPENSSL_MAKEFILE} + WORKING_DIRECTORY ${SOURCE_PATH_RELEASE} + OUTPUT_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-0-out.log + ERROR_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-0-err.log + ) + vcpkg_execute_required_process( + COMMAND nmake -f ${OPENSSL_MAKEFILE} install + WORKING_DIRECTORY ${SOURCE_PATH_RELEASE} + LOGNAME build-${TARGET_TRIPLET}-rel-1) + + message(STATUS "Build ${TARGET_TRIPLET}-rel done") +endif() + + +if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + message(STATUS "Configure ${TARGET_TRIPLET}-dbg") + file(COPY ${MASTER_COPY_SOURCE_PATH} DESTINATION ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg) + set(SOURCE_PATH_DEBUG ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/openssl-${OPENSSL_VERSION}) + set(OPENSSLDIR_DEBUG ${CURRENT_PACKAGES_DIR}/debug) + + vcpkg_execute_required_process( + COMMAND ${CONFIGURE_COMMAND} debug-${OPENSSL_ARCH} "--prefix=${OPENSSLDIR_DEBUG}" "--openssldir=${OPENSSLDIR_DEBUG}" -FS + WORKING_DIRECTORY ${SOURCE_PATH_DEBUG} + LOGNAME configure-perl-${TARGET_TRIPLET}-${CMAKE_BUILD_TYPE}-dbg + ) + vcpkg_execute_required_process( + COMMAND ${OPENSSL_DO} + WORKING_DIRECTORY ${SOURCE_PATH_DEBUG} + LOGNAME configure-do-${TARGET_TRIPLET}-${CMAKE_BUILD_TYPE}-dbg + ) + message(STATUS "Configure ${TARGET_TRIPLET}-dbg done") + + message(STATUS "Build ${TARGET_TRIPLET}-dbg") + make_directory(${SOURCE_PATH_DEBUG}/inc32/openssl) + execute_process( + COMMAND ${JOM} -k -j $ENV{NUMBER_OF_PROCESSORS} -f ${OPENSSL_MAKEFILE} + WORKING_DIRECTORY ${SOURCE_PATH_DEBUG} + OUTPUT_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-dbg-0-out.log + ERROR_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-dbg-0-err.log + ) + vcpkg_execute_required_process( + COMMAND nmake -f ${OPENSSL_MAKEFILE} install + WORKING_DIRECTORY ${SOURCE_PATH_DEBUG} + LOGNAME build-${TARGET_TRIPLET}-dbg-1) + + message(STATUS "Build ${TARGET_TRIPLET}-dbg done") +endif() + + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE + ${CURRENT_PACKAGES_DIR}/debug/bin/openssl.exe + ${CURRENT_PACKAGES_DIR}/debug/openssl.cnf + ${CURRENT_PACKAGES_DIR}/openssl.cnf +) + +file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/tools/openssl/) +file(RENAME ${CURRENT_PACKAGES_DIR}/bin/openssl.exe ${CURRENT_PACKAGES_DIR}/tools/openssl/openssl.exe) + +vcpkg_copy_tool_dependencies(${CURRENT_PACKAGES_DIR}/tools/openssl) + +if(VCPKG_LIBRARY_LINKAGE STREQUAL static) + # They should be empty, only the exes deleted above were in these directories + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/bin/) + file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin/) +endif() + +file(READ "${CURRENT_PACKAGES_DIR}/include/openssl/dtls1.h" _contents) +string(REPLACE "" "" _contents "${_contents}") +file(WRITE "${CURRENT_PACKAGES_DIR}/include/openssl/dtls1.h" "${_contents}") + +file(READ "${CURRENT_PACKAGES_DIR}/include/openssl/rand.h" _contents) +string(REPLACE "# include " "#ifndef _WINSOCKAPI_\n#define _WINSOCKAPI_\n#endif\n# include " _contents "${_contents}") +file(WRITE "${CURRENT_PACKAGES_DIR}/include/openssl/rand.h" "${_contents}") + +vcpkg_copy_pdbs() + +file(COPY ${CMAKE_CURRENT_LIST_DIR}/usage DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT}) +file(INSTALL ${MASTER_COPY_SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) + +vcpkg_test_cmake(PACKAGE_NAME OpenSSL MODULE) diff --git a/cmake/ports/openssl-windows/usage b/cmake/ports/openssl-windows/usage new file mode 100644 index 0000000000..cf83f33916 --- /dev/null +++ b/cmake/ports/openssl-windows/usage @@ -0,0 +1,4 @@ +The package openssl is compatible with built-in CMake targets: + + find_package(OpenSSL REQUIRED) + target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto) diff --git a/cmake/ports/openssl/CONTROL b/cmake/ports/openssl/CONTROL new file mode 100644 index 0000000000..16a1932485 --- /dev/null +++ b/cmake/ports/openssl/CONTROL @@ -0,0 +1,4 @@ +Source: openssl +Version: 0 +Description: OpenSSL is an open source project that provides a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library. +Build-Depends: openssl-windows (windows), openssl-android (android), openssl-unix (!android&!windows) diff --git a/cmake/ports/openssl/portfile.cmake b/cmake/ports/openssl/portfile.cmake new file mode 100644 index 0000000000..3d55c367f5 --- /dev/null +++ b/cmake/ports/openssl/portfile.cmake @@ -0,0 +1,2 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) +file(INSTALL ${CMAKE_CURRENT_LIST_DIR}/usage DESTINATION ${CURRENT_PACKAGES_DIR}/share/openssl/) diff --git a/cmake/ports/openssl/usage b/cmake/ports/openssl/usage new file mode 100644 index 0000000000..cf83f33916 --- /dev/null +++ b/cmake/ports/openssl/usage @@ -0,0 +1,4 @@ +The package openssl is compatible with built-in CMake targets: + + find_package(OpenSSL REQUIRED) + target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto) diff --git a/cmake/ports/openvr/CONTROL b/cmake/ports/openvr/CONTROL new file mode 100644 index 0000000000..b0f337d0a9 --- /dev/null +++ b/cmake/ports/openvr/CONTROL @@ -0,0 +1,3 @@ +Source: openvr +Version: 1.0.16 +Description: an API and runtime that allows access to VR hardware from multiple vendors without requiring that applications have specific knowledge of the hardware they are targeting. diff --git a/cmake/ports/openvr/portfile.cmake b/cmake/ports/openvr/portfile.cmake new file mode 100644 index 0000000000..5e4211907d --- /dev/null +++ b/cmake/ports/openvr/portfile.cmake @@ -0,0 +1,47 @@ +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO ValveSoftware/openvr + REF v1.0.16 + SHA512 967356563ba4232da5361510c7519d3058e09eced4571aadc00d8a75ab1f299a0aebda2b0b10b0ffb6c6a443fd718634d0c0103964e289961449c93e8d7b9d02 + HEAD_REF master +) + +set(VCPKG_LIBRARY_LINKAGE dynamic) + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(ARCH_PATH "win64") +elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(ARCH_PATH "win32") +else() + message(FATAL_ERROR "Package only supports x64 and x86 windows.") +endif() + +if(VCPKG_CMAKE_SYSTEM_NAME) + message(FATAL_ERROR "Package only supports windows desktop.") +endif() + +file(MAKE_DIRECTORY + ${CURRENT_PACKAGES_DIR}/lib + ${CURRENT_PACKAGES_DIR}/bin + ${CURRENT_PACKAGES_DIR}/debug/lib + ${CURRENT_PACKAGES_DIR}/debug/bin +) +file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/lib) +file(COPY ${SOURCE_PATH}/lib/${ARCH_PATH}/openvr_api.lib DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib) +file(COPY + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb + DESTINATION ${CURRENT_PACKAGES_DIR}/bin +) +file(COPY + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.dll + ${SOURCE_PATH}/bin/${ARCH_PATH}/openvr_api.pdb + DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin +) +file(COPY ${SOURCE_PATH}/headers DESTINATION ${CURRENT_PACKAGES_DIR}) +file(RENAME ${CURRENT_PACKAGES_DIR}/headers ${CURRENT_PACKAGES_DIR}/include) + +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/openvr) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/openvr/LICENSE ${CURRENT_PACKAGES_DIR}/share/openvr/copyright) diff --git a/cmake/ports/sdl2/CONTROL b/cmake/ports/sdl2/CONTROL new file mode 100644 index 0000000000..6f39484dd6 --- /dev/null +++ b/cmake/ports/sdl2/CONTROL @@ -0,0 +1,3 @@ +Source: sdl2 +Version: 2.0.8-1 +Description: Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D. diff --git a/cmake/ports/sdl2/enable-winrt-cmake.patch b/cmake/ports/sdl2/enable-winrt-cmake.patch new file mode 100644 index 0000000000..dcd2afa67c --- /dev/null +++ b/cmake/ports/sdl2/enable-winrt-cmake.patch @@ -0,0 +1,206 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 73d9407..082fbc5 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -3,7 +3,11 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + endif() + + cmake_minimum_required(VERSION 2.8.11) +-project(SDL2 C) ++if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") ++ project(SDL2 C CXX) ++else() ++ project(SDL2 C) ++endif() + + # !!! FIXME: this should probably do "MACOSX_RPATH ON" as a target property + # !!! FIXME: for the SDL2 shared library (so you get an +@@ -358,7 +362,6 @@ file(GLOB SOURCE_FILES + ${SDL2_SOURCE_DIR}/src/timer/*.c + ${SDL2_SOURCE_DIR}/src/video/*.c) + +- + if(ASSERTIONS STREQUAL "auto") + # Do nada - use optimization settings to determine the assertion level + elseif(ASSERTIONS STREQUAL "disabled") +@@ -1132,6 +1135,22 @@ elseif(WINDOWS) + file(GLOB CORE_SOURCES ${SDL2_SOURCE_DIR}/src/core/windows/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${CORE_SOURCES}) + ++ if(WINDOWS_STORE) ++ cmake_minimum_required(VERSION 3.0) ++ add_definitions(-DSDL_BUILDING_WINRT=1 -ZW) ++ link_libraries( ++ -nodefaultlib:vccorlib$<$:d> ++ -nodefaultlib:msvcrt$<$:d> ++ vccorlib$<$:d>.lib ++ msvcrt$<$:d>.lib ++ ) ++ endif() ++ ++ if(WINDOWS_STORE) ++ file(GLOB WINRT_SOURCE_FILES ${SDL2_SOURCE_DIR}/src/core/winrt/*.c ${SDL2_SOURCE_DIR}/src/core/winrt/*.cpp) ++ set(SOURCE_FILES ${SOURCE_FILES} ${WINRT_SOURCE_FILES}) ++ endif() ++ + if(MSVC) + # Prevent codegen that would use the VC runtime libraries. + set_property(DIRECTORY . APPEND PROPERTY COMPILE_OPTIONS "/GS-") +@@ -1176,7 +1195,11 @@ elseif(WINDOWS) + check_include_file(d3d11_1.h HAVE_D3D11_H) + check_include_file(ddraw.h HAVE_DDRAW_H) + check_include_file(dsound.h HAVE_DSOUND_H) +- check_include_file(dinput.h HAVE_DINPUT_H) ++ if(WINDOWS_STORE) ++ set(HAVE_DINPUT_H 0) ++ else() ++ check_include_file(dinput.h HAVE_DINPUT_H) ++ endif() + check_include_file(xaudio2.h HAVE_XAUDIO2_H) + check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H) + check_include_file(audioclient.h HAVE_AUDIOCLIENT_H) +@@ -1193,12 +1216,14 @@ elseif(WINDOWS) + endif() + + if(SDL_AUDIO) +- set(SDL_AUDIO_DRIVER_WINMM 1) +- file(GLOB WINMM_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/winmm/*.c) +- set(SOURCE_FILES ${SOURCE_FILES} ${WINMM_AUDIO_SOURCES}) +- set(HAVE_SDL_AUDIO TRUE) ++ if(NOT WINDOWS_STORE) ++ set(SDL_AUDIO_DRIVER_WINMM 1) ++ file(GLOB WINMM_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/winmm/*.c) ++ set(SOURCE_FILES ${SOURCE_FILES} ${WINMM_AUDIO_SOURCES}) ++ set(HAVE_SDL_AUDIO TRUE) ++ endif() + +- if(HAVE_DSOUND_H) ++ if(HAVE_DSOUND_H AND NOT WINDOWS_STORE) + set(SDL_AUDIO_DRIVER_DSOUND 1) + file(GLOB DSOUND_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/directsound/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${DSOUND_AUDIO_SOURCES}) +@@ -1208,9 +1233,10 @@ elseif(WINDOWS) + set(SDL_AUDIO_DRIVER_XAUDIO2 1) + file(GLOB XAUDIO2_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/xaudio2/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${XAUDIO2_AUDIO_SOURCES}) ++ set(HAVE_SDL_AUDIO TRUE) + endif() + +- if(HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H) ++ if(HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H AND NOT WINDOWS_STORE) + set(SDL_AUDIO_DRIVER_WASAPI 1) + file(GLOB WASAPI_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/wasapi/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${WASAPI_AUDIO_SOURCES}) +@@ -1222,11 +1248,20 @@ elseif(WINDOWS) + if(NOT SDL_LOADSO) + message_error("SDL_VIDEO requires SDL_LOADSO, which is not enabled") + endif() +- set(SDL_VIDEO_DRIVER_WINDOWS 1) +- file(GLOB WIN_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/windows/*.c) ++ if(WINDOWS_STORE) ++ set(SDL_VIDEO_DRIVER_WINRT 1) ++ file(GLOB WIN_VIDEO_SOURCES ++ ${SDL2_SOURCE_DIR}/src/video/winrt/*.c ++ ${SDL2_SOURCE_DIR}/src/video/winrt/*.cpp ++ ${SDL2_SOURCE_DIR}/src/render/direct3d11/*.cpp ++ ) ++ else() ++ set(SDL_VIDEO_DRIVER_WINDOWS 1) ++ file(GLOB WIN_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/windows/*.c) ++ endif() + set(SOURCE_FILES ${SOURCE_FILES} ${WIN_VIDEO_SOURCES}) + +- if(RENDER_D3D AND HAVE_D3D_H) ++ if(RENDER_D3D AND HAVE_D3D_H AND NOT WINDOWS_STORE) + set(SDL_VIDEO_RENDER_D3D 1) + set(HAVE_RENDER_D3D TRUE) + endif() +@@ -1249,20 +1284,31 @@ elseif(WINDOWS) + endif() + + if(SDL_POWER) +- set(SDL_POWER_WINDOWS 1) +- set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/windows/SDL_syspower.c) ++ if(WINDOWS_STORE) ++ set(SDL_POWER_WINRT 1) ++ set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/winrt/SDL_syspower.cpp) ++ else() ++ set(SDL_POWER_WINDOWS 1) ++ set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/power/windows/SDL_syspower.c) ++ endif() + set(HAVE_SDL_POWER TRUE) + endif() + + if(SDL_FILESYSTEM) + set(SDL_FILESYSTEM_WINDOWS 1) +- file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/windows/*.c) ++ if(WINDOWS_STORE) ++ file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/winrt/*.cpp) ++ else() ++ file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/windows/*.c) ++ endif() + set(SOURCE_FILES ${SOURCE_FILES} ${FILESYSTEM_SOURCES}) + set(HAVE_SDL_FILESYSTEM TRUE) + endif() + + # Libraries for Win32 native and MinGW +- list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid) ++ if(NOT WINDOWS_STORE) ++ list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid) ++ endif() + + # TODO: in configure.in the check for timers is set on + # cygwin | mingw32* - does this include mingw32CE? +@@ -1284,7 +1330,7 @@ elseif(WINDOWS) + set(SOURCE_FILES ${SOURCE_FILES} ${CORE_SOURCES}) + + if(SDL_VIDEO) +- if(VIDEO_OPENGL) ++ if(VIDEO_OPENGL AND NOT WINDOWS_STORE) + set(SDL_VIDEO_OPENGL 1) + set(SDL_VIDEO_OPENGL_WGL 1) + set(SDL_VIDEO_RENDER_OGL 1) +@@ -1688,9 +1734,11 @@ endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + + # Always build SDLmain +-add_library(SDL2main STATIC ${SDLMAIN_SOURCES}) +-target_include_directories(SDL2main PUBLIC $) +-set(_INSTALL_LIBS "SDL2main") ++if(NOT WINDOWS_STORE) ++ add_library(SDL2main STATIC ${SDLMAIN_SOURCES}) ++ target_include_directories(SDL2main PUBLIC $) ++ set(_INSTALL_LIBS "SDL2main") ++endif() + + if(SDL_SHARED) + add_library(SDL2 SHARED ${SOURCE_FILES} ${VERSION_SOURCES}) +diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake +index 9b20398..7ae6e35 100644 +--- a/include/SDL_config.h.cmake ++++ b/include/SDL_config.h.cmake +@@ -298,6 +298,7 @@ + #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC @SDL_VIDEO_DRIVER_DIRECTFB_DYNAMIC@ + #cmakedefine SDL_VIDEO_DRIVER_DUMMY @SDL_VIDEO_DRIVER_DUMMY@ + #cmakedefine SDL_VIDEO_DRIVER_WINDOWS @SDL_VIDEO_DRIVER_WINDOWS@ ++#cmakedefine SDL_VIDEO_DRIVER_WINRT @SDL_VIDEO_DRIVER_WINRT@ + #cmakedefine SDL_VIDEO_DRIVER_WAYLAND @SDL_VIDEO_DRIVER_WAYLAND@ + #cmakedefine SDL_VIDEO_DRIVER_RPI @SDL_VIDEO_DRIVER_RPI@ + #cmakedefine SDL_VIDEO_DRIVER_VIVANTE @SDL_VIDEO_DRIVER_VIVANTE@ +@@ -365,6 +366,7 @@ + #cmakedefine SDL_POWER_ANDROID @SDL_POWER_ANDROID@ + #cmakedefine SDL_POWER_LINUX @SDL_POWER_LINUX@ + #cmakedefine SDL_POWER_WINDOWS @SDL_POWER_WINDOWS@ ++#cmakedefine SDL_POWER_WINRT @SDL_POWER_WINRT@ + #cmakedefine SDL_POWER_MACOSX @SDL_POWER_MACOSX@ + #cmakedefine SDL_POWER_HAIKU @SDL_POWER_HAIKU@ + #cmakedefine SDL_POWER_EMSCRIPTEN @SDL_POWER_EMSCRIPTEN@ +@@ -387,7 +389,7 @@ + #cmakedefine SDL_LIBSAMPLERATE_DYNAMIC @SDL_LIBSAMPLERATE_DYNAMIC@ + + /* Platform specific definitions */ +-#if !defined(__WIN32__) ++#if !defined(__WIN32__) && !defined(__WINRT__) + # if !defined(_STDINT_H_) && !defined(_STDINT_H) && !defined(HAVE_STDINT_H) && !defined(_HAVE_STDINT_H) + typedef unsigned int size_t; + typedef signed char int8_t; diff --git a/cmake/ports/sdl2/export-symbols-only-in-shared-build.patch b/cmake/ports/sdl2/export-symbols-only-in-shared-build.patch new file mode 100644 index 0000000000..7e6d503e23 --- /dev/null +++ b/cmake/ports/sdl2/export-symbols-only-in-shared-build.patch @@ -0,0 +1,24 @@ +# HG changeset patch +# User Mikhail Paulyshka +# Date 1506252750 -10800 +# Sun Sep 24 14:32:30 2017 +0300 +# Branch SDL2-WIN-SYMBOLS_LEACKAGE +# Node ID 46ec9baae30cd4e0c584de125cae4a3cce2864ad +# Parent 8df7a59b55283aa09889522369a2b32674c048de +win32: fix symbols leakage for static libraries + +diff -r 8df7a59b5528 -r 46ec9baae30c include/begin_code.h +--- a/include/begin_code.h Fri Sep 22 11:25:52 2017 -0700 ++++ b/include/begin_code.h Sun Sep 24 14:32:30 2017 +0300 +@@ -58,8 +58,10 @@ + # else + # define DECLSPEC __declspec(dllimport) + # endif ++# elif defined(_DLL) ++# define DECLSPEC __declspec(dllexport) + # else +-# define DECLSPEC __declspec(dllexport) ++# define DECLSPEC + # endif + # elif defined(__OS2__) + # ifdef BUILD_SDL diff --git a/cmake/ports/sdl2/portfile.cmake b/cmake/ports/sdl2/portfile.cmake new file mode 100644 index 0000000000..5d7ac5314d --- /dev/null +++ b/cmake/ports/sdl2/portfile.cmake @@ -0,0 +1,79 @@ +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO SDL-Mirror/SDL + REF release-2.0.8 + SHA512 5922dbeb14bb22991160251664b417d3f846867c18b5ecc1bd19c328ffd69b16252b7d45b9a317bafd1207fdb66d93a022dfb239e02447db9babd941956b6b37 + HEAD_REF master +) + +vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/export-symbols-only-in-shared-build.patch + ${CMAKE_CURRENT_LIST_DIR}/enable-winrt-cmake.patch +) + +string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC) +string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED) +string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS + -DSDL_STATIC=${SDL_STATIC} + -DSDL_SHARED=${SDL_SHARED} + -DVIDEO_VULKAN=OFF + -DFORCE_STATIC_VCRT=${FORCE_STATIC_VCRT} + -DLIBC=ON +) + +vcpkg_install_cmake() + +if(EXISTS "${CURRENT_PACKAGES_DIR}/cmake") + vcpkg_fixup_cmake_targets(CONFIG_PATH "cmake") +elseif(EXISTS "${CURRENT_PACKAGES_DIR}/lib/cmake/SDL2") + vcpkg_fixup_cmake_targets(CONFIG_PATH "lib/cmake/SDL2") +elseif(EXISTS "${CURRENT_PACKAGES_DIR}/SDL2.framework/Resources") + vcpkg_fixup_cmake_targets(CONFIG_PATH "SDL2.framework/Resources") +endif() + +file(REMOVE_RECURSE + ${CURRENT_PACKAGES_DIR}/debug/include + ${CURRENT_PACKAGES_DIR}/debug/share + ${CURRENT_PACKAGES_DIR}/bin/sdl2-config + ${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config + ${CURRENT_PACKAGES_DIR}/SDL2.framework + ${CURRENT_PACKAGES_DIR}/debug/SDL2.framework +) + +file(GLOB BINS ${CURRENT_PACKAGES_DIR}/debug/bin/* ${CURRENT_PACKAGES_DIR}/bin/*) +if(NOT BINS) + file(REMOVE_RECURSE + ${CURRENT_PACKAGES_DIR}/bin + ${CURRENT_PACKAGES_DIR}/debug/bin + ) +endif() + +if(NOT VCPKG_CMAKE_SYSTEM_NAME) + if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/lib/manual-link) + file(RENAME ${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib ${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib) + endif() + if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/lib/manual-link) + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib ${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib) + endif() + + file(GLOB SHARE_FILES ${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake) + foreach(SHARE_FILE ${SHARE_FILES}) + file(READ "${SHARE_FILE}" _contents) + string(REPLACE "lib/SDL2main" "lib/manual-link/SDL2main" _contents "${_contents}") + file(WRITE "${SHARE_FILE}" "${_contents}") + endforeach() +endif() + +file(INSTALL ${SOURCE_PATH}/COPYING.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/sdl2 RENAME copyright) +vcpkg_copy_pdbs() diff --git a/cmake/ports/shaderc/0001-Do-not-generate-build-version.inc.patch b/cmake/ports/shaderc/0001-Do-not-generate-build-version.inc.patch new file mode 100644 index 0000000000..41cc812201 --- /dev/null +++ b/cmake/ports/shaderc/0001-Do-not-generate-build-version.inc.patch @@ -0,0 +1,30 @@ +From e8e12e856cbc41f9bdcc83bc87eb5013df199ee1 Mon Sep 17 00:00:00 2001 +From: vlj +Date: Fri, 2 Dec 2016 16:36:25 +0100 +Subject: [PATCH] Do not generate build-version.inc + +--- + CMakeLists.txt | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index a4c2fac..5544a2d 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -53,8 +53,8 @@ add_subdirectory(libshaderc) + add_subdirectory(glslc) + add_subdirectory(examples) + +-add_custom_target(build-version +- ${PYTHON_EXE} +- ${CMAKE_CURRENT_SOURCE_DIR}/utils/update_build_version.py +- ${shaderc_SOURCE_DIR} ${spirv-tools_SOURCE_DIR} ${glslang_SOURCE_DIR} +- COMMENT "Update build-version.inc in the Shaderc build directory (if necessary).") ++#add_custom_target(build-version ++# ${PYTHON_EXE} ++# ${CMAKE_CURRENT_SOURCE_DIR}/utils/update_build_version.py ++# ${shaderc_SOURCE_DIR} ${spirv-tools_SOURCE_DIR} ${glslang_SOURCE_DIR} ++# COMMENT "Update build-version.inc in the Shaderc build directory (if necessary).") +-- +2.10.2.windows.1 + diff --git a/cmake/ports/shaderc/CMakeLists.txt b/cmake/ports/shaderc/CMakeLists.txt new file mode 100644 index 0000000000..c4f15a8846 --- /dev/null +++ b/cmake/ports/shaderc/CMakeLists.txt @@ -0,0 +1,31 @@ +option(SUFFIX_D "Add d Suffix to lib" ${SUFFIX_D}) +if(NOT SUFFIX_D) + find_library(GLSLANG glslang) + find_library(OSDEPENDENT OSDependent) + find_library(OGLCOMPILER OGLCompiler) + find_library(HLSLLIB HLSL) + find_library(SPIRVLIB SPIRV) +ELSE() + find_library(GLSLANG glslangd) + find_library(OSDEPENDENT OSDependentd) + find_library(OGLCOMPILER OGLCompilerd) + find_library(HLSLLIB HLSLd) + find_library(SPIRVLIB SPIRVd) +ENDIF() + +add_library(glslang STATIC IMPORTED GLOBAL) +set_property(TARGET glslang PROPERTY IMPORTED_LOCATION "${GLSLANG}") +find_path(glslang_SOURCE_DIR glslang/Include/Common) +set_property(TARGET glslang APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${glslang_SOURCE_DIR}") + +add_library(OSDependent STATIC IMPORTED GLOBAL) +set_property(TARGET OSDependent PROPERTY IMPORTED_LOCATION "${OSDEPENDENT}") + +add_library(OGLCompiler STATIC IMPORTED GLOBAL) +set_property(TARGET OGLCompiler PROPERTY IMPORTED_LOCATION "${OGLCOMPILER}") + +add_library(HLSL STATIC IMPORTED GLOBAL) +set_property(TARGET HLSL PROPERTY IMPORTED_LOCATION "${HLSLLIB}") + +add_library(SPIRV STATIC IMPORTED GLOBAL) +set_property(TARGET SPIRV PROPERTY IMPORTED_LOCATION "${SPIRVLIB}") diff --git a/cmake/ports/shaderc/CMakeLists_spirv.txt b/cmake/ports/shaderc/CMakeLists_spirv.txt new file mode 100644 index 0000000000..d9e0136012 --- /dev/null +++ b/cmake/ports/shaderc/CMakeLists_spirv.txt @@ -0,0 +1,8 @@ +find_library(SPIRVTOOLSOPT SPIRV-Tools-opt) +find_library(SPIRVTOOLS SPIRV-Tools) + +add_library(SPIRV-Tools-opt STATIC IMPORTED GLOBAL) +set_property(TARGET SPIRV-Tools-opt PROPERTY IMPORTED_LOCATION "${SPIRVTOOLSOPT}") + +add_library(SPIRV-Tools STATIC IMPORTED GLOBAL) +set_property(TARGET SPIRV-Tools PROPERTY IMPORTED_LOCATION "${SPIRVTOOLS}") diff --git a/cmake/ports/shaderc/CONTROL b/cmake/ports/shaderc/CONTROL new file mode 100644 index 0000000000..0cd3de96d8 --- /dev/null +++ b/cmake/ports/shaderc/CONTROL @@ -0,0 +1,4 @@ +Source: shaderc +Version: 2018.0-1 +Description: A collection of tools, libraries and tests for shader compilation. +Build-Depends: glslang, spirv-tools diff --git a/cmake/ports/shaderc/build-version.inc b/cmake/ports/shaderc/build-version.inc new file mode 100644 index 0000000000..853e89916e --- /dev/null +++ b/cmake/ports/shaderc/build-version.inc @@ -0,0 +1,3 @@ +"shaderc v2016.2-dev unknown hash, 2016-12-02\n" +"spirv-tools v2016.6-dev unknown hash, 2016-12-02\n" +"glslang unknown hash, 2016-12-02\n" diff --git a/cmake/ports/shaderc/portfile.cmake b/cmake/ports/shaderc/portfile.cmake new file mode 100644 index 0000000000..c43e591ec1 --- /dev/null +++ b/cmake/ports/shaderc/portfile.cmake @@ -0,0 +1,49 @@ +include(vcpkg_common_functions) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO google/shaderc + REF v2018.0 + SHA512 7a420fde73c9f2aae3f13558d538a1f4ae43bba19e2b4d2da8fbbd017e9e4f328ece5f330f1bbcb9fe84c91b7eb84b9158dc2e3d144c82939090a0fa6f5b4ef0 + HEAD_REF master + PATCHES + 0001-Do-not-generate-build-version.inc.patch +) + +file(COPY ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt DESTINATION ${SOURCE_PATH}/third_party/glslang) +file(COPY ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_spirv.txt DESTINATION ${SOURCE_PATH}/third_party/spirv-tools) +file(RENAME ${SOURCE_PATH}/third_party/spirv-tools/CMakeLists_spirv.txt ${SOURCE_PATH}/third_party/spirv-tools/CMakeLists.txt) +file(COPY ${CMAKE_CURRENT_LIST_DIR}/build-version.inc DESTINATION ${SOURCE_PATH}/glslc/src) + +#Note: glslang and spir tools doesn't export symbol and need to be build as static lib for cmake to work +set(VCPKG_LIBRARY_LINKAGE "static") +set(OPTIONS) +if(VCPKG_CRT_LINKAGE STREQUAL "dynamic") + list(APPEND OPTIONS -DSHADERC_ENABLE_SHARED_CRT=ON) +endif() + +# shaderc uses python to manipulate copyright information +vcpkg_find_acquire_program(PYTHON3) +get_filename_component(PYTHON3_EXE_PATH ${PYTHON3} DIRECTORY) +vcpkg_add_to_path(PREPEND "${PYTHON3_EXE_PATH}") + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + OPTIONS -DSHADERC_SKIP_TESTS=true ${OPTIONS} -Dglslang_SOURCE_DIR=${CURRENT_INSTALLED_DIR}/include + OPTIONS_DEBUG -DSUFFIX_D=true + OPTIONS_RELEASE -DSUFFIX_D=false +) + +vcpkg_install_cmake() + +file(GLOB EXES "${CURRENT_PACKAGES_DIR}/bin/*.exe") +file(COPY ${EXES} DESTINATION ${CURRENT_PACKAGES_DIR}/tools) + +#Safe to remove as libs are static +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/bin) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) + +# Handle copyright +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/shaderc) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/shaderc/LICENSE ${CURRENT_PACKAGES_DIR}/share/shaderc/copyright) diff --git a/cmake/ports/spirv-cross/CONTROL b/cmake/ports/spirv-cross/CONTROL new file mode 100644 index 0000000000..9068608da6 --- /dev/null +++ b/cmake/ports/spirv-cross/CONTROL @@ -0,0 +1,3 @@ +Source: spirv-cross +Version: 2018-08-07-1 +Description: SPIRV-Cross is a practical tool and library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages. diff --git a/cmake/ports/spirv-cross/portfile.cmake b/cmake/ports/spirv-cross/portfile.cmake new file mode 100644 index 0000000000..6495e82bc3 --- /dev/null +++ b/cmake/ports/spirv-cross/portfile.cmake @@ -0,0 +1,33 @@ +include(vcpkg_common_functions) + +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO KhronosGroup/SPIRV-Cross + REF 2018-08-07 + SHA512 1ac6ee6b2864d950199d4e856ae1576f9435827501baa5d53821a973cd68aaa03ec428094bf74c570784997baac5b2e3802ddc7f02844e2ee546741fa726bf91 + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=OFF +) + +vcpkg_install_cmake() +vcpkg_copy_pdbs() + +foreach(COMPONENT core cpp glsl hlsl msl reflect util) + vcpkg_fixup_cmake_targets(CONFIG_PATH share/spirv_cross_${COMPONENT}/cmake TARGET_PATH share/spirv_cross_${COMPONENT}) +endforeach() + +file(GLOB EXES "${CURRENT_PACKAGES_DIR}/bin/*") +file(COPY ${EXES} DESTINATION ${CURRENT_PACKAGES_DIR}/tools) + +# cleanup +configure_file(${SOURCE_PATH}/LICENSE ${CURRENT_PACKAGES_DIR}/share/spirv-cross/copyright COPYONLY) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin) diff --git a/cmake/ports/spirv-tools/CONTROL b/cmake/ports/spirv-tools/CONTROL new file mode 100644 index 0000000000..057fe5db0a --- /dev/null +++ b/cmake/ports/spirv-tools/CONTROL @@ -0,0 +1,3 @@ +Source: spirv-tools +Version: 2018.5-1 +Description: API and commands for processing SPIR-V modules diff --git a/cmake/ports/spirv-tools/portfile.cmake b/cmake/ports/spirv-tools/portfile.cmake new file mode 100644 index 0000000000..b52a3b96c2 --- /dev/null +++ b/cmake/ports/spirv-tools/portfile.cmake @@ -0,0 +1,43 @@ +include(vcpkg_common_functions) + +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO KhronosGroup/SPIRV-Tools + REF v2018.5 + SHA512 068a39e15111f24ad2a6b27e7ada786b3124b239aa8b13e187a4d512044db57a8e6a0fccadd0451155e1f57c96c8dec91a2338996c59fc883007cf7be07f2cad + HEAD_REF master +) + +vcpkg_from_github( + OUT_SOURCE_PATH SPIRV_HEADERS_PATH + REPO KhronosGroup/SPIRV-Headers + REF 801cca8104245c07e8cc53292da87ee1b76946fe + SHA512 2bfc37beec1f6afb565fa7dad08eb838c8fe4bacda7f80a3a6c75d80c7eb1caea7e7716dc1da11b337b723a0870700d524c6617c5b7cab8b28048fa8c0785ba9 + HEAD_REF master +) + +vcpkg_find_acquire_program(PYTHON3) +get_filename_component(PYTHON3_DIR "${PYTHON3}" DIRECTORY) +vcpkg_add_to_path("${PYTHON3_DIR}") + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS + -DSPIRV-Headers_SOURCE_DIR=${SPIRV_HEADERS_PATH} + -DSPIRV_WERROR=OFF +) + +vcpkg_install_cmake() + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(GLOB EXES "${CURRENT_PACKAGES_DIR}/bin/*") +file(COPY ${EXES} DESTINATION ${CURRENT_PACKAGES_DIR}/tools) +file(REMOVE ${EXES}) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin) + +# Handle copyright +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/spirv-tools) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/spirv-tools/LICENSE ${CURRENT_PACKAGES_DIR}/share/spirv-tools/copyright) diff --git a/cmake/ports/tbb/CMakeLists.txt b/cmake/ports/tbb/CMakeLists.txt new file mode 100644 index 0000000000..edfaf63200 --- /dev/null +++ b/cmake/ports/tbb/CMakeLists.txt @@ -0,0 +1,18 @@ +project(tbb CXX) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*) +file(COPY ${SOURCES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/src) + +include(${CMAKE_CURRENT_BINARY_DIR}/src/cmake/TBBBuild.cmake REQUIRED) +tbb_build(TBB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/src MAKE_ARGS extra_inc=big_iron.inc) + +set(SUBDIR ${CMAKE_CURRENT_BINARY_DIR}/tbb_cmake_build/tbb_cmake_build_subdir) +if(CMAKE_BUILD_TYPE STREQUAL "Release") + set(SUBDIR "${SUBDIR}_release") +else() + set(SUBDIR "${SUBDIR}_debug") +endif() + +file(GLOB OUTPUTS ${SUBDIR}/*.so.* ${SUBDIR}/*.so ${SUBDIR}/*.a ${SUBDIR}/*.dylib ${SUBDIR}/*.dylib.*) + +install(FILES ${OUTPUTS} DESTINATION lib) diff --git a/cmake/ports/tbb/CONTROL b/cmake/ports/tbb/CONTROL new file mode 100644 index 0000000000..e87106dc3c --- /dev/null +++ b/cmake/ports/tbb/CONTROL @@ -0,0 +1,3 @@ +Source: tbb +Version: 2018_U5-4 +Description: Intel's Threading Building Blocks. diff --git a/cmake/ports/tbb/portfile.cmake b/cmake/ports/tbb/portfile.cmake new file mode 100644 index 0000000000..ac47360f8c --- /dev/null +++ b/cmake/ports/tbb/portfile.cmake @@ -0,0 +1,86 @@ +include(vcpkg_common_functions) + +if(NOT VCPKG_CMAKE_SYSTEM_NAME OR VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + vcpkg_check_linkage(ONLY_DYNAMIC_LIBRARY) +endif() + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO 01org/tbb + REF 2018_U5 + SHA512 3e8d20276ccb1b50099f96b6cf968e3d0ada53caea1fa836ecb8652f1dca236fbbbf2c783e64ea2f761f7f21725064d19b72d176e35e4dc29706b8a30965153b + HEAD_REF tbb_2018 +) + +file(COPY ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt DESTINATION ${SOURCE_PATH}) + +if(VCPKG_CMAKE_SYSTEM_NAME AND NOT VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + ) + + vcpkg_install_cmake() + + # Settings for TBBConfigForSource.cmake.in + set(TBB_LIB_EXT a) + set(TBB_LIB_PREFIX lib) +else() + if (VCPKG_CRT_LINKAGE STREQUAL static) + set(RELEASE_CONFIGURATION Release-MT) + set(DEBUG_CONFIGURATION Debug-MT) + else() + set(RELEASE_CONFIGURATION Release) + set(DEBUG_CONFIGURATION Debug) + endif() + + vcpkg_install_msbuild( + SOURCE_PATH ${SOURCE_PATH} + PROJECT_SUBPATH build/vs2013/makefile.sln + RELEASE_CONFIGURATION ${RELEASE_CONFIGURATION} + DEBUG_CONFIGURATION ${DEBUG_CONFIGURATION} + ) + # Settings for TBBConfigForSource.cmake.in + set(TBB_LIB_EXT lib) + set(TBB_LIB_PREFIX) +endif() + +file(COPY + ${SOURCE_PATH}/include/tbb + ${SOURCE_PATH}/include/serial + DESTINATION ${CURRENT_PACKAGES_DIR}/include) + +# Settings for TBBConfigForSource.cmake.in +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + set(TBB_DEFAULT_COMPONENTS tbb tbbmalloc) +else() + set(TBB_DEFAULT_COMPONENTS tbb tbbmalloc tbbmalloc_proxy) +endif() +file(READ "${SOURCE_PATH}/include/tbb/tbb_stddef.h" _tbb_stddef) +string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" _tbb_ver_major "${_tbb_stddef}") +string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" _tbb_ver_minor "${_tbb_stddef}") +string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_INTERFACE_VERSION "${_tbb_stddef}") +set(TBB_VERSION "${_tbb_ver_major}.${_tbb_ver_minor}") +set(TBB_RELEASE_DIR "\${_tbb_root}/lib") +set(TBB_DEBUG_DIR "\${_tbb_root}/debug/lib") + +configure_file( + ${SOURCE_PATH}/cmake/templates/TBBConfigForSource.cmake.in + ${CURRENT_PACKAGES_DIR}/share/tbb/TBBConfig.cmake + @ONLY +) +file(READ ${CURRENT_PACKAGES_DIR}/share/tbb/TBBConfig.cmake _contents) +string(REPLACE + "get_filename_component(_tbb_root \"\${_tbb_root}\" PATH)" + "get_filename_component(_tbb_root \"\${_tbb_root}\" PATH)\nget_filename_component(_tbb_root \"\${_tbb_root}\" PATH)" + _contents + "${_contents}" +) +string(REPLACE "SHARED IMPORTED)" "UNKNOWN IMPORTED)" _contents "${_contents}") +file(WRITE ${CURRENT_PACKAGES_DIR}/share/tbb/TBBConfig.cmake "${_contents}") + +# Handle copyright +file(COPY ${SOURCE_PATH}/LICENSE ${CMAKE_CURRENT_LIST_DIR}/usage DESTINATION ${CURRENT_PACKAGES_DIR}/share/tbb) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/tbb/LICENSE ${CURRENT_PACKAGES_DIR}/share/tbb/copyright) + +vcpkg_test_cmake(PACKAGE_NAME TBB) diff --git a/cmake/ports/tbb/usage b/cmake/ports/tbb/usage new file mode 100644 index 0000000000..2972bafbd6 --- /dev/null +++ b/cmake/ports/tbb/usage @@ -0,0 +1,4 @@ +The package tbb provides CMake targets: + + find_package(TBB CONFIG REQUIRED) + target_link_libraries(main PRIVATE TBB::tbb) diff --git a/cmake/ports/vulkanmemoryallocator/CONTROL b/cmake/ports/vulkanmemoryallocator/CONTROL new file mode 100644 index 0000000000..cd97502527 --- /dev/null +++ b/cmake/ports/vulkanmemoryallocator/CONTROL @@ -0,0 +1,3 @@ +Source: vulkanmemoryallocator +Version: 3a7249f313b047417fbb1d36a3fbe6c3bf1505b5 +Description: Vulkan Memory Allocator diff --git a/cmake/ports/vulkanmemoryallocator/portfile.cmake b/cmake/ports/vulkanmemoryallocator/portfile.cmake new file mode 100644 index 0000000000..2b02bc1175 --- /dev/null +++ b/cmake/ports/vulkanmemoryallocator/portfile.cmake @@ -0,0 +1,14 @@ +include(vcpkg_common_functions) + +set(SOURCE_VERSION 3.3.0) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator + REF 3a7249f313b047417fbb1d36a3fbe6c3bf1505b5 + SHA512 ddeac01a25bfe47d4e749d150aeaea43760c9732db78bf6d15231b9de1b7fb5218f1ced07710cb1929175ddcb05848239edaf0f6cc47be48896211293ffd236c + HEAD_REF master +) + +file(INSTALL ${SOURCE_PATH}/src/vk_mem_alloc.h DESTINATION ${CURRENT_PACKAGES_DIR}/include/vma) +file(INSTALL ${SOURCE_PATH}/LICENSE.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/vulkanmemoryallocator RENAME copyright) diff --git a/cmake/ports/zlib/CONTROL b/cmake/ports/zlib/CONTROL new file mode 100644 index 0000000000..c559637834 --- /dev/null +++ b/cmake/ports/zlib/CONTROL @@ -0,0 +1,3 @@ +Source: zlib +Version: 1.2.11-3 +Description: A compression library diff --git a/cmake/ports/zlib/LICENSE b/cmake/ports/zlib/LICENSE new file mode 100644 index 0000000000..ca5fddfe0d --- /dev/null +++ b/cmake/ports/zlib/LICENSE @@ -0,0 +1,20 @@ + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu \ No newline at end of file diff --git a/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch b/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch new file mode 100644 index 0000000000..229a2d055b --- /dev/null +++ b/cmake/ports/zlib/cmake_dont_build_more_than_needed.patch @@ -0,0 +1,42 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0fe939d..8d2f5f1 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -7,6 +7,7 @@ set(VERSION "1.2.11") + + option(ASM686 "Enable building i686 assembly implementation") + option(AMD64 "Enable building amd64 assembly implementation") ++option(SKIP_BUILD_EXAMPLES "Skip build of the examples" OFF) + + 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) + 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} + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ) +@@ -230,6 +239,7 @@ endif() + # Example binaries + #============================================================================ + ++if (NOT SKIP_BUILD_EXAMPLES) + add_executable(example test/example.c) + target_link_libraries(example zlib) + add_test(example example) +@@ -247,3 +257,4 @@ if(HAVE_OFF64_T) + target_link_libraries(minigzip64 zlib) + set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") + endif() ++endif() diff --git a/cmake/ports/zlib/portfile.cmake b/cmake/ports/zlib/portfile.cmake new file mode 100644 index 0000000000..f2cc68b7a3 --- /dev/null +++ b/cmake/ports/zlib/portfile.cmake @@ -0,0 +1,47 @@ +include(vcpkg_common_functions) +set(SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src/zlib-1.2.11) +vcpkg_download_distfile(ARCHIVE_FILE + URLS "http://www.zlib.net/zlib-1.2.11.tar.gz" "https://downloads.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gz" + FILENAME "zlib1211.tar.gz" + SHA512 73fd3fff4adeccd4894084c15ddac89890cd10ef105dd5e1835e1e9bbb6a49ff229713bd197d203edfa17c2727700fce65a2a235f07568212d820dca88b528ae +) +vcpkg_extract_source_archive(${ARCHIVE_FILE}) + +vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/cmake_dont_build_more_than_needed.patch +) + +# This is generated during the cmake build +file(REMOVE ${SOURCE_PATH}/zconf.h) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS + -DSKIP_INSTALL_FILES=ON + -DSKIP_BUILD_EXAMPLES=ON + OPTIONS_DEBUG + -DSKIP_INSTALL_HEADERS=ON +) + +vcpkg_install_cmake() + +# Both dynamic and static are built, so keep only the one needed +if(VCPKG_LIBRARY_LINKAGE STREQUAL static) + if(EXISTS ${CURRENT_PACKAGES_DIR}/lib/zlibstatic.lib) + file(RENAME ${CURRENT_PACKAGES_DIR}/lib/zlibstatic.lib ${CURRENT_PACKAGES_DIR}/lib/zlib.lib) + endif() + if(EXISTS ${CURRENT_PACKAGES_DIR}/debug/lib/zlibstaticd.lib) + file(RENAME ${CURRENT_PACKAGES_DIR}/debug/lib/zlibstaticd.lib ${CURRENT_PACKAGES_DIR}/debug/lib/zlibd.lib) + endif() +endif() + +file(INSTALL ${CMAKE_CURRENT_LIST_DIR}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/zlib RENAME copyright) + +vcpkg_copy_pdbs() + +file(COPY ${CMAKE_CURRENT_LIST_DIR}/usage DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT}) + +#vcpkg_test_cmake(PACKAGE_NAME ZLIB MODULE) diff --git a/cmake/ports/zlib/usage b/cmake/ports/zlib/usage new file mode 100644 index 0000000000..0dfed74930 --- /dev/null +++ b/cmake/ports/zlib/usage @@ -0,0 +1,4 @@ +The package zlib is compatible with built-in CMake targets: + + find_package(ZLIB REQUIRED) + target_link_libraries(main PRIVATE ZLIB::ZLIB) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index fd48a792dc..9ce11ca032 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -28,6 +28,78 @@ !include "WinVer.nsh" +;-------------------------------- +;Include Installer Logging +; taken from http://nsis.sourceforge.net/Logging:_Simple_Text_File_Logging_Functions_and_Macros +; TextLog.nsh v1.1 - 2005-12-26 +; Written by Mike Schinkel [http://www.mikeschinkel.com/blog/] + + Var /GLOBAL __TextLog_FileHandle + Var /GLOBAL __TextLog_FileName + Var /GLOBAL __TextLog_State + + !define LogMsg '!insertmacro LogMsgCall' + !macro LogMsgCall _text + Call LogSetOn + Push "${_text}" + Call LogText + Call LogSetOff + !macroend + + + !define LogText '!insertmacro LogTextCall' + !macro LogTextCall _text + Push "${_text}" + Call LogText + !macroend + + Function LogText + Exch $0 ; pABC -> 0ABC + FileWrite $__TextLog_FileHandle "$0$\r$\n" + Pop $0 ; 0ABC -> ABC + FunctionEnd + + !define LogSetFileName '!insertmacro LogSetFileNameCall' + !macro LogSetFileNameCall _filename + Push "${_filename}" + Call LogSetFileName + !macroend + + Function LogSetFileName + Exch $0 ; pABC -> 0ABC + StrCpy $__TextLog_FileName "$0" + StrCmp $__TextLog_State "open" +1 +3 + Call LogSetOff + Call LogSetOn + Pop $0 ; 0ABC -> ABC + FunctionEnd + + !define LogSetOn '!insertmacro LogSetOnCall' + !macro LogSetOnCall + Call LogSetOn + !macroend + + Function LogSetOn + StrCmp $__TextLog_FileName "" +1 AlreadySet + StrCpy $__TextLog_FileName "$INSTDIR\install.log" + AlreadySet: + StrCmp $__TextLog_State "open" +2 + FileOpen $__TextLog_FileHandle "$__TextLog_FileName" a + FileSeek $__TextLog_FileHandle 0 END + StrCpy $__TextLog_State "open" + FunctionEnd + + !define LogSetOff '!insertmacro LogSetOffCall' + !macro LogSetOffCall + Call LogSetOff + !macroend + + Function LogSetOff + StrCmp $__TextLog_State "open" +1 +2 + FileClose $__TextLog_FileHandle + StrCpy $__TextLog_State "" + FunctionEnd + ;-------------------------------- ; Utilities and Functions ;-------------------------------- @@ -375,6 +447,10 @@ Var GAClientID !insertmacro CreateGUID $GAClientID !macroend +!macro LogStep Category Action Label Value + ${LogText} "Step: ${Category} ${Action} ${Label} ${Value}" +!macroend + !macro GoogleAnalytics Category Action Label Value ${If} "@GA_TRACKING_ID@" != "" Push $0 @@ -557,11 +633,13 @@ Var Express !macro MaybeSkipPage ; Check if Express is set, if so, abort the post install options page ${If} $Express == "1" + ${LogText} "Express Install: Skipping Post Install Options Page" Abort ${EndIf} !macroend !macro DownloadSlideshowImages + ${LogText} "Download Slideshow Images" InitPluginsDir Push $0 @@ -583,32 +661,40 @@ Var Express !macroend Function OnUserAbort + !insertmacro LogStep "Installer" "Abort" "User Abort" "" !insertmacro GoogleAnalytics "Installer" "Abort" "User Abort" "" FunctionEnd Function PageWelcomePre + !insertmacro LogStep "Installer" "Welcome" "" "" !insertmacro GoogleAnalytics "Installer" "Welcome" "" "" !insertmacro DownloadSlideshowImages FunctionEnd Function PageLicensePre + !insertmacro LogStep "Installer" "License" "" "" !insertmacro GoogleAnalytics "Installer" "License" "" "" FunctionEnd Function PageDirectoryPre !insertmacro MaybeSkipPage + !insertmacro LogStep "Installer" "Directory" "" "" !insertmacro GoogleAnalytics "Installer" "Directory" "" "" FunctionEnd Function PageStartMenuPre !insertmacro MaybeSkipPage + !insertmacro LogStep "Installer" "StartMenu" "" "" !insertmacro GoogleAnalytics "Installer" "StartMenu" "" "" FunctionEnd Function PageComponentsPre !insertmacro MaybeSkipPage + !insertmacro LogStep "Installer" "Components" "" "" !insertmacro GoogleAnalytics "Installer" "Components" "" "" FunctionEnd Function PageInstallFilesPre + !insertmacro LogStep "Installer" "Install" "" "" !insertmacro GoogleAnalytics "Installer" "Install" "" "" FunctionEnd !macro SetInstallOption Checkbox OptionName Default + ${LogText} "SetInstallOption ${OptionName} ${Default}" ; reads the value for the given install option to the registry ReadRegStr $0 HKLM "@REGISTRY_HKLM_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\@POST_INSTALL_OPTIONS_REG_GROUP@" "${OptionName}" @@ -625,6 +711,7 @@ FunctionEnd !macroend Function InstallTypesPage + !insertmacro LogStep "Installer" "Install Types" "" "" !insertmacro GoogleAnalytics "Installer" "Install Types" "" "" !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" @@ -688,6 +775,7 @@ FunctionEnd Function StartInstallSlideshow ; create a slideshow file based on what files we have available + ${LogText} "Start Installs Slideshow" ; stash $0 and $1 Push $0 @@ -730,7 +818,11 @@ Function StartInstallSlideshow FunctionEnd Function PostInstallOptionsPage + + ${LogText} "Install Directory: $INSTDIR" + !insertmacro MaybeSkipPage + !insertmacro LogStep "Installer" "Post Install Options" "" "" !insertmacro GoogleAnalytics "Installer" "Post Install Options" "" "" !insertmacro MUI_HEADER_TEXT "Setup Options" "" @@ -876,30 +968,43 @@ Function ReadPostInstallOptions ; check if the user asked for a desktop shortcut to console ${NSD_GetState} $DesktopConsoleCheckbox $DesktopConsoleState - + ${LogText} "Option: Start Desktop Console: $DesktopConsoleState" + ; check if the user asked to have console launched every startup ${NSD_GetState} $ConsoleStartupCheckbox $ConsoleStartupState + ${LogText} "Option: Start Desktop Console On Startup: $ConsoleStartupState" + ${If} @SERVER_COMPONENT_CONDITIONAL@ + ${LogText} "Option: Install Server" + ${EndIf} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ + ${LogText} "Option: Install Client" ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState + ${LogText} "Option: Create Client Desktop Shortcut: $DesktopClientState" ${EndIf} ${If} @PR_BUILD@ == 1 + ${LogText} "Option: PR Build" ; check if we need to copy settings/content from production for this PR build ${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState + ${LogText} "Option: Copy Settings From Production: $CopyFromProductionState" ${EndIf} ; check if we need to launch the console post-install ${NSD_GetState} $LaunchConsoleNowCheckbox $LaunchConsoleNowState + ${LogText} "Option: Launch Console Now: $LaunchConsoleNowState" ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if we need to launch the client post-install ${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState + ${LogText} "Option: Launch Client Now: $LaunchClientNowState" ${EndIf} ; check if the user asked for a clean install ${NSD_GetState} $CleanInstallCheckbox $CleanInstallState + ${LogText} "Option: Clean Install: $CleanInstallState" FunctionEnd Function HandlePostInstallOptions @@ -1225,6 +1330,7 @@ Section "-Core installation" ; Handle whichever post install options were set Call HandlePostInstallOptions + !insertmacro LogStep "Installer" "Done" "" "" !insertmacro GoogleAnalytics "Installer" "Done" "" "" SectionEnd @@ -1232,7 +1338,6 @@ SectionEnd !macro PromptForRunningApplication applicationName displayName action prompter !define UniqueID ${__LINE__} - Prompt_${UniqueID}: ${nsProcess::FindProcess} ${applicationName} $R0 @@ -1478,6 +1583,11 @@ InstallDirRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_RE Function .onInit + Delete "$TEMP\hifi_install.log" + ${LogSetFileName} "$TEMP\hifi_install.log" + ${LogSetOn} + ${LogText} "In .onInit" + !ifdef INNER ; If INNER is defined, then we aren't supposed to do anything except write out ; the installer. This is better than processing a command line option as it means @@ -1495,6 +1605,7 @@ Function .onInit !insertmacro GoogleAnalytics "Installer" "Start" "$CampaignName" "" ; make sure none of the installed applications are still running + ${LogText} "Checking For Running Applications" !insertmacro CheckForRunningApplications "installed" "Installer" ${nsProcess::Unload} diff --git a/cmake/templates/launch-c.in b/cmake/templates/launch-c.in new file mode 100644 index 0000000000..6c91d96dd9 --- /dev/null +++ b/cmake/templates/launch-c.in @@ -0,0 +1,12 @@ +#!/bin/sh + +# Xcode generator doesn't include the compiler as the +# first argument, Ninja and Makefiles do. Handle both cases. +if [[ "$1" = "${CMAKE_C_COMPILER}" ]] ; then + shift +fi + +export CCACHE_CPP2=true +export CCACHE_HARDLINK=true +export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches +exec "${C_LAUNCHER}" "${CMAKE_C_COMPILER}" "$@" diff --git a/cmake/templates/launch-cxx.in b/cmake/templates/launch-cxx.in new file mode 100644 index 0000000000..4215d89c80 --- /dev/null +++ b/cmake/templates/launch-cxx.in @@ -0,0 +1,12 @@ +#!/bin/sh + +# Xcode generator doesn't include the compiler as the +# first argument, Ninja and Makefiles do. Handle both cases. +if [[ "$1" = "${CMAKE_CXX_COMPILER}" ]] ; then + shift +fi + +export CCACHE_CPP2=true +export CCACHE_HARDLINK=true +export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches +exec "${CXX_LAUNCHER}" "${CMAKE_CXX_COMPILER}" "$@" diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 458dcb5ab8..693132a8f7 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -27,30 +27,8 @@ include_hifi_library_headers(graphics) link_hifi_libraries(embedded-webserver networking shared avatars octree) target_zlib() - -add_dependency_external_projects(quazip) - -find_package(QuaZip REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES}) - -if (WIN32) - add_paths_to_fixup_libs(${QUAZIP_DLL_PATH}) -endif () - -# find OpenSSL -find_package(OpenSSL REQUIRED) - -if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") - # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto - message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." - "\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") -endif () - -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) +target_quazip() +target_openssl() # libcrypto uses dlopen in libdl if (UNIX) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 853e87ae23..346e846748 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -59,6 +59,7 @@ $(document).ready(function(){ $.ajax({ url: '/content/upload', type: 'POST', + timeout: 3600000, // Set timeout to 1h cache: false, processData: false, contentType: false, diff --git a/domain-server/src/AssetsBackupHandler.cpp b/domain-server/src/AssetsBackupHandler.cpp index 6bcabc0bf1..b3ba74a985 100644 --- a/domain-server/src/AssetsBackupHandler.cpp +++ b/domain-server/src/AssetsBackupHandler.cpp @@ -15,9 +15,18 @@ #include #include +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + #include #include +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include #include #include diff --git a/domain-server/src/ContentSettingsBackupHandler.cpp b/domain-server/src/ContentSettingsBackupHandler.cpp index 8ffd25b7a4..de7669b6a5 100644 --- a/domain-server/src/ContentSettingsBackupHandler.cpp +++ b/domain-server/src/ContentSettingsBackupHandler.cpp @@ -11,8 +11,19 @@ #include "ContentSettingsBackupHandler.h" +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + #include #include +#include + +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + ContentSettingsBackupHandler::ContentSettingsBackupHandler(DomainServerSettingsManager& domainServerSettingsManager) : _settingsManager(domainServerSettingsManager) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f90155c786..8cf033c130 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2906,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) // collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and // we cannot recursively take the write lock required by handleKillNode) std::vector nodesToKill; - nodeList->eachNode([direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) { + nodeList->eachNode([&direction, &replicationNodesInSettings, &replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) { if ((direction == Upstream && NodeType::isUpstream(otherNode->getType())) || (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) { bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(), diff --git a/domain-server/src/EntitiesBackupHandler.cpp b/domain-server/src/EntitiesBackupHandler.cpp index 599a730107..90a066036d 100644 --- a/domain-server/src/EntitiesBackupHandler.cpp +++ b/domain-server/src/EntitiesBackupHandler.cpp @@ -13,9 +13,18 @@ #include +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif + #include #include +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include EntitiesBackupHandler::EntitiesBackupHandler(QString entitiesFilePath, QString entitiesReplacementFilePath) : diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index cc9fe8ef4c..50247b1145 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -190,8 +190,6 @@ if (WIN32) add_dependency_external_projects(sixense) endif () - add_dependency_external_projects(sdl2) - add_dependency_external_projects(OpenVR) add_dependency_external_projects(neuron) add_dependency_external_projects(wasapi) add_dependency_external_projects(steamworks) diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml index 46c00e758e..adfd29df9e 100644 --- a/interface/resources/QtWebEngine/UIDelegates/Menu.qml +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 -import "../../qml/controls-uit" -import "../../qml/styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: menu diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml index 6014b6834b..b4d3ca4bb2 100644 --- a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml +++ b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 -import "../../qml/controls-uit" -import "../../qml/styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: root diff --git a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml index e4ab3037ef..089c745571 100644 --- a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml +++ b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 -import "../../qml/controls-uit" -import "../../qml/styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../../qml/dialogs" QtObject { diff --git a/interface/resources/avatar/animations/teleport.fbx b/interface/resources/avatar/animations/teleport.fbx new file mode 100644 index 0000000000..99c950ced6 Binary files /dev/null and b/interface/resources/avatar/animations/teleport.fbx differ diff --git a/interface/resources/avatar/network-animation.json b/interface/resources/avatar/network-animation.json new file mode 100644 index 0000000000..d1046d2c6b --- /dev/null +++ b/interface/resources/avatar/network-animation.json @@ -0,0 +1,160 @@ +{ + "version": "1.1", + "root": { + "id": "networkAnimStateMachine", + "type": "stateMachine", + "data": { + "currentState": "transitAnimStateMachine", + "states": [ + { + "id": "transitAnimStateMachine", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "userNetworkAnimA", "state": "userNetworkAnimA" }, + { "var": "userNetworkAnimB", "state": "userNetworkAnimB" } + ] + }, + { + "id": "userNetworkAnimA", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "transitAnimStateMachine", "state": "transitAnimStateMachine" }, + { "var": "userNetworkAnimB", "state": "userNetworkAnimB" } + ] + }, + { + "id": "userNetworkAnimB", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "transitAnimStateMachine", "state": "transitAnimStateMachine" }, + { "var": "userNetworkAnimA", "state": "userNetworkAnimA" } + ] + } + ] + }, + "children": [ + { + "id": "transitAnimStateMachine", + "type": "stateMachine", + "data": { + "currentState": "idleAnim", + "states": [ + { + "id": "idleAnim", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "postTransitAnim", "state": "postTransitAnim" }, + { "var": "preTransitAnim", "state": "preTransitAnim" } + ] + }, + { + "id": "preTransitAnim", + "interpTarget": 4, + "interpDuration": 4, + "transitions": [ + { "var": "idleAnim", "state": "idleAnim" }, + { "var": "transitAnim", "state": "transitAnim" } + ] + }, + { + "id": "transitAnim", + "interpTarget": 2, + "interpDuration": 2, + "transitions": [ + { "var": "preTransitAnim", "state": "preTransitAnim" }, + { "var": "postTransitAnim", "state": "postTransitAnim" } + ] + }, + { + "id": "postTransitAnim", + "interpTarget": 4, + "interpDuration": 4, + "transitions": [ + { "var": "transitAnim", "state": "transitAnim" }, + { "var": "idleAnim", "state": "idleAnim" } + ] + } + ] + }, + "children" : [ + { + "id": "idleAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "preTransitAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/teleport.fbx", + "startFrame": 0.0, + "endFrame": 10.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "transitAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/teleport.fbx", + "startFrame": 11.0, + "endFrame": 18.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "postTransitAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/teleport.fbx", + "startFrame": 19.0, + "endFrame": 44.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + } + ] + }, + { + "id": "userNetworkAnimA", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "userNetworkAnimB", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] + } +} diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json new file mode 100644 index 0000000000..186a9c1084 --- /dev/null +++ b/interface/resources/config/keyboard.json @@ -0,0 +1,2476 @@ +{ + "anchor": { + "dimensions": { + "x": 0.023600000888109207, + "y": 0.022600000724196434, + "z": 0.1274999976158142 + }, + "position": { + "x": 0.006292800903320312, + "y": 0.004300000742077827, + "z": 0.005427663803100586 + }, + "rotation": { + "w": 1.000, + "x": 0.000, + "y": 0.000, + "z": 0.000 + } + }, + "textDisplay": { + "dimensions": { + "x": 0.15, + "y": 0.045, + "z": 0.1 + }, + "localPosition": { + "x": -0.3032040786743164, + "y": 0.059300000742077827, + "z": 0.06454843521118164 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.906, + "z": 0.423 + }, + "leftMargin": 0.0, + "rightMargin": 0.0, + "topMargin": 0.0, + "bottomMargin": 0.0, + "lineHeight": 0.05 + }, + "useResourcesPath": true, + "layers": [ + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "key": "p", + "texture": { + "file9": "meshes/keyboard/key_p.png", + "file10": "meshes/keyboard/key_p.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "i", + "texture": { + "file9": "meshes/keyboard/key_i.png", + "file10": "meshes/keyboard/key_i.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "o", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/key_o.png", + "file10": "meshes/keyboard/key_o.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.49904823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "l", + "texture": { + "file9": "meshes/keyboard/key_l.png", + "file10": "meshes/keyboard/key_l.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4439973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "k", + "texture": { + "file9": "meshes/keyboard/key_k.png", + "file10": "meshes/keyboard/key_k.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "y", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_y.png", + "file10": "meshes/keyboard/key_y.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "r", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_r.png", + "file10": "meshes/keyboard/key_r.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "d", + "texture": { + "file9": "meshes/keyboard/key_d.png", + "file10": "meshes/keyboard/key_d.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.16272640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "t", + "texture": { + "file9": "meshes/keyboard/key_t.png", + "file10": "meshes/keyboard/key_t.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "f", + "texture": { + "file9": "meshes/keyboard/key_f.png", + "file10": "meshes/keyboard/key_f.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2200756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "g", + "texture": { + "file9": "meshes/keyboard/key_g.png", + "file10": "meshes/keyboard/key_g.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27622222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "w", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_w.png", + "file10": "meshes/keyboard/key_w.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "q", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_q.png", + "file10": "meshes/keyboard/key_q.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "a", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_a.png", + "file10": "meshes/keyboard/key_a.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.050909996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "e", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_e.png", + "file10": "meshes/keyboard/key_e.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "s", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_s.png", + "file10": "meshes/keyboard/key_s.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10659980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "u", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_u.png", + "file10": "meshes/keyboard/key_u.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "h", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_h.png", + "file10": "meshes/keyboard/key_h.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3321561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "j", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_j.png", + "file10": "meshes/keyboard/key_j.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38753604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "c", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_c.png", + "file10": "meshes/keyboard/key_c.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "v", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_v.png", + "file10": "meshes/keyboard/key_v.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "b", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_b.png", + "file10": "meshes/keyboard/key_b.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "z", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_z.png", + "file10": "meshes/keyboard/key_z.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "n", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_n.png", + "file10": "meshes/keyboard/key_n.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "m", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_m.png", + "file10": "meshes/keyboard/key_m.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "x", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_x.png", + "file10": "meshes/keyboard/key_x.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 1, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 2, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_123.png", + "file11": "meshes/keyboard/key_123.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ], + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "key": "p", + "texture": { + "file9": "meshes/keyboard/keyCap_p.png", + "file10": "meshes/keyboard/keyCap_p.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "i", + "texture": { + "file9": "meshes/keyboard/keyCap_i.png", + "file10": "meshes/keyboard/keyCap_i.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "o", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/keyCap_o.png", + "file10": "meshes/keyboard/keyCap_o.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.49904823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "l", + "texture": { + "file9": "meshes/keyboard/keyCap_l.png", + "file10": "meshes/keyboard/keyCap_l.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4439973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "k", + "texture": { + "file9": "meshes/keyboard/keyCap_k.png", + "file10": "meshes/keyboard/keyCap_k.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "y", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_y.png", + "file10": "meshes/keyboard/keyCap_y.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "r", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_r.png", + "file10": "meshes/keyboard/keyCap_r.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "d", + "texture": { + "file9": "meshes/keyboard/keyCap_d.png", + "file10": "meshes/keyboard/keyCap_d.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.16272640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "t", + "texture": { + "file9": "meshes/keyboard/keyCap_t.png", + "file10": "meshes/keyboard/keyCap_t.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "f", + "texture": { + "file9": "meshes/keyboard/keyCap_F.png", + "file10": "meshes/keyboard/keyCap_F.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2200756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "g", + "texture": { + "file9": "meshes/keyboard/keyCap_g.png", + "file10": "meshes/keyboard/keyCap_g.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27622222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "w", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_w.png", + "file10": "meshes/keyboard/keyCap_w.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "q", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_q.png", + "file10": "meshes/keyboard/keyCap_q.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "a", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_a.png", + "file10": "meshes/keyboard/keyCap_a.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.050909996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "e", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_e.png", + "file10": "meshes/keyboard/keyCap_e.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "s", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_s.png", + "file10": "meshes/keyboard/keyCap_s.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10659980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "u", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_u.png", + "file10": "meshes/keyboard/keyCap_u.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "h", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_h.png", + "file10": "meshes/keyboard/keyCap_h.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3321561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "j", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_j.png", + "file10": "meshes/keyboard/keyCap_j.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38753604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "c", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_c.png", + "file10": "meshes/keyboard/keyCap_c.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "v", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_v.png", + "file10": "meshes/keyboard/keyCap_v.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "b", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_b.png", + "file10": "meshes/keyboard/keyCap_b.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "z", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_z.png", + "file10": "meshes/keyboard/keyCap_z.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "n", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_n.png", + "file10": "meshes/keyboard/keyCap_n.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "m", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_m.png", + "file10": "meshes/keyboard/keyCap_m.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "x", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_x.png", + "file10": "meshes/keyboard/keyCap_x.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 0, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 2, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_123.png", + "file11": "meshes/keyboard/key_123.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ], + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "key": "0", + "texture": { + "file9": "meshes/keyboard/key_0.png", + "file10": "meshes/keyboard/key_0.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "8", + "texture": { + "file9": "meshes/keyboard/key_8.png", + "file10": "meshes/keyboard/key_8.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "9", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/key_9.png", + "file10": "meshes/keyboard/key_9.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47764823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "(", + "texture": { + "file9": "meshes/keyboard/key_open_paren.png", + "file10": "meshes/keyboard/key_open_paren.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53364823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": ")", + "texture": { + "file9": "meshes/keyboard/key_close_paren.png", + "file10": "meshes/keyboard/key_close_paren.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59634823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "/", + "texture": { + "file9": "meshes/keyboard/key_slash.png", + "file10": "meshes/keyboard/key_slash.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4206973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "+", + "texture": { + "file9": "meshes/keyboard/key_plus.png", + "file10": "meshes/keyboard/key_plus.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "6", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_6.png", + "file10": "meshes/keyboard/key_6.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "4", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_4.png", + "file10": "meshes/keyboard/key_4.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "$", + "texture": { + "file9": "meshes/keyboard/key_dollar.png", + "file10": "meshes/keyboard/key_dollar.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13772640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "5", + "texture": { + "file9": "meshes/keyboard/key_5.png", + "file10": "meshes/keyboard/key_5.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "%", + "texture": { + "file9": "meshes/keyboard/key_percentage.png", + "file10": "meshes/keyboard/key_percentage.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1947756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "_", + "texture": { + "file9": "meshes/keyboard/key_under.png", + "file10": "meshes/keyboard/key_under.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.25172222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "2", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_2.png", + "file10": "meshes/keyboard/key_2.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "1", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_1.png", + "file10": "meshes/keyboard/key_1.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "@", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_at.png", + "file10": "meshes/keyboard/key_at.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026209996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "3", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_3.png", + "file10": "meshes/keyboard/key_3.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "#", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_hashtag.png", + "file10": "meshes/keyboard/key_hashtag.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08209980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "7", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_7.png", + "file10": "meshes/keyboard/key_7.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "&", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_ampersand.png", + "file10": "meshes/keyboard/key_ampersand.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3090561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "-", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_min.png", + "file10": "meshes/keyboard/key_min.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36433604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "'", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_squote.png", + "file10": "meshes/keyboard/key_squote.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ":", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_colon.png", + "file10": "meshes/keyboard/key_colon.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ";", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_semi.png", + "file10": "meshes/keyboard/key_semi.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "*", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_ast.png", + "file10": "meshes/keyboard/key_ast.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "!", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exclam.png", + "file10": "meshes/keyboard/key_exclam.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "?", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_question.png", + "file10": "meshes/keyboard/key_question.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "\"", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_dquote.png", + "file10": "meshes/keyboard/key_dquote.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 1, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 0, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_abc.png", + "file11": "meshes/keyboard/key_abc.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ".", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_period.png", + "file10": "meshes/keyboard/key_period.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ",", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_comma.png", + "file10": "meshes/keyboard/key_comma.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ] + ] +} diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 80933a2489..2ad07911c6 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -133,7 +133,7 @@ { "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, - { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 8a7744efb3..24b1587691 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -51,32 +51,34 @@ { "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" }, { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" }, - { "from": "Vive.LeftHand", "to": "Standard.LeftHand"}, - { "from": "Vive.RightHand", "to": "Standard.RightHand"}, + { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Vive.RightHand", "to": "Standard.RightHand" }, + { "from": "Vive.Head", "to" : "Standard.Head" }, { "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", - "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.15}] }, - { "from": "Vive.RightFoot", "to" : "Standard.RightFoot", - "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.15}] }, - { "from": "Vive.Hips", "to" : "Standard.Hips", - "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.15}] }, - { "from": "Vive.Spine2", "to" : "Standard.Spine2", - "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.15}] + }, + { + "from": "Vive.RightArm", "to" : "Standard.RightArm", + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.15}] + }, + { + "from": "Vive.LeftArm", "to" : "Standard.LeftArm", + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.15}] }, - - { "from": "Vive.Head", "to" : "Standard.Head"}, - { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, { "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" }, { "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" }, diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index 7045abed75..f19c399f3a 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/icons/tablet-icons/inventory-a-msg.svg b/interface/resources/icons/tablet-icons/inventory-a-msg.svg new file mode 100644 index 0000000000..794bd1e414 --- /dev/null +++ b/interface/resources/icons/tablet-icons/inventory-a-msg.svg @@ -0,0 +1,4 @@ + + + + diff --git a/interface/resources/icons/tablet-icons/inventory-a.svg b/interface/resources/icons/tablet-icons/inventory-a.svg new file mode 100644 index 0000000000..8b6f34eaa3 --- /dev/null +++ b/interface/resources/icons/tablet-icons/inventory-a.svg @@ -0,0 +1,3 @@ + + + diff --git a/interface/resources/icons/tablet-icons/inventory-i-msg.svg b/interface/resources/icons/tablet-icons/inventory-i-msg.svg new file mode 100644 index 0000000000..35d4fb54ae --- /dev/null +++ b/interface/resources/icons/tablet-icons/inventory-i-msg.svg @@ -0,0 +1,4 @@ + + + + diff --git a/interface/resources/icons/tablet-icons/inventory-i.svg b/interface/resources/icons/tablet-icons/inventory-i.svg new file mode 100644 index 0000000000..071fabce88 --- /dev/null +++ b/interface/resources/icons/tablet-icons/inventory-i.svg @@ -0,0 +1,3 @@ + + + diff --git a/interface/resources/icons/tablet-icons/wallet-a-msg.svg b/interface/resources/icons/tablet-icons/wallet-a-msg.svg deleted file mode 100644 index d51c3e99a2..0000000000 --- a/interface/resources/icons/tablet-icons/wallet-a-msg.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/wallet-a.svg b/interface/resources/icons/tablet-icons/wallet-a.svg deleted file mode 100644 index 50ea64848f..0000000000 --- a/interface/resources/icons/tablet-icons/wallet-a.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/wallet-i-msg.svg b/interface/resources/icons/tablet-icons/wallet-i-msg.svg deleted file mode 100644 index 676f97a966..0000000000 --- a/interface/resources/icons/tablet-icons/wallet-i-msg.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/interface/resources/icons/tablet-icons/wallet-i.svg b/interface/resources/icons/tablet-icons/wallet-i.svg deleted file mode 100644 index 4e27e41b44..0000000000 --- a/interface/resources/icons/tablet-icons/wallet-i.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx b/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx deleted file mode 100644 index 6d46791fa2..0000000000 Binary files a/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx and /dev/null differ diff --git a/interface/resources/images/Default-Sky-9-cubemap.ktx b/interface/resources/images/Default-Sky-9-cubemap.ktx deleted file mode 100644 index 70f495c3ab..0000000000 Binary files a/interface/resources/images/Default-Sky-9-cubemap.ktx and /dev/null differ diff --git a/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.jpg b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.jpg new file mode 100644 index 0000000000..7977396159 Binary files /dev/null and b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.jpg differ diff --git a/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.ktx b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.ktx new file mode 100644 index 0000000000..4231bf7650 Binary files /dev/null and b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.ktx differ diff --git a/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.texmeta.json b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.texmeta.json new file mode 100644 index 0000000000..28512662d9 --- /dev/null +++ b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.texmeta.json @@ -0,0 +1,8 @@ +{ + "compressed": { + "COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT": "Default-Sky-9-cubemap_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT.ktx", + "COMPRESSED_SRGB8_ETC2": "Default-Sky-9-cubemap_COMPRESSED_SRGB8_ETC2.ktx" + }, + "original": "Default-Sky-9-cubemap.jpg", + "uncompressed": "Default-Sky-9-cubemap.ktx" +} diff --git a/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT.ktx b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT.ktx new file mode 100644 index 0000000000..c789fa4ac5 Binary files /dev/null and b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT.ktx differ diff --git a/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap_COMPRESSED_SRGB8_ETC2.ktx b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap_COMPRESSED_SRGB8_ETC2.ktx new file mode 100644 index 0000000000..deede32614 Binary files /dev/null and b/interface/resources/images/Default-Sky-9-cubemap/Default-Sky-9-cubemap_COMPRESSED_SRGB8_ETC2.ktx differ diff --git a/interface/resources/meshes/controller/touch/touch_l_bumper.fbx b/interface/resources/meshes/controller/touch/touch_l_bumper.fbx index 7d051735c3..d6cdf59bf3 100644 Binary files a/interface/resources/meshes/controller/touch/touch_l_bumper.fbx and b/interface/resources/meshes/controller/touch/touch_l_bumper.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_l_button_x.fbx b/interface/resources/meshes/controller/touch/touch_l_button_x.fbx index 8297e623bb..9a3f6928f7 100644 Binary files a/interface/resources/meshes/controller/touch/touch_l_button_x.fbx and b/interface/resources/meshes/controller/touch/touch_l_button_x.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_l_button_y.fbx b/interface/resources/meshes/controller/touch/touch_l_button_y.fbx index c87ad5c8fc..020b26c4ca 100644 Binary files a/interface/resources/meshes/controller/touch/touch_l_button_y.fbx and b/interface/resources/meshes/controller/touch/touch_l_button_y.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_l_joystick.fbx b/interface/resources/meshes/controller/touch/touch_l_joystick.fbx index eaf8b3e382..49c55b60df 100644 Binary files a/interface/resources/meshes/controller/touch/touch_l_joystick.fbx and b/interface/resources/meshes/controller/touch/touch_l_joystick.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_l_trigger.fbx b/interface/resources/meshes/controller/touch/touch_l_trigger.fbx index 2cbf15272d..bc8ca86fb2 100644 Binary files a/interface/resources/meshes/controller/touch/touch_l_trigger.fbx and b/interface/resources/meshes/controller/touch/touch_l_trigger.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_r_bumper.fbx b/interface/resources/meshes/controller/touch/touch_r_bumper.fbx index ddff4ba40f..47d692e004 100644 Binary files a/interface/resources/meshes/controller/touch/touch_r_bumper.fbx and b/interface/resources/meshes/controller/touch/touch_r_bumper.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_r_button_a.fbx b/interface/resources/meshes/controller/touch/touch_r_button_a.fbx index 8386ed565c..73adb34e4b 100644 Binary files a/interface/resources/meshes/controller/touch/touch_r_button_a.fbx and b/interface/resources/meshes/controller/touch/touch_r_button_a.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_r_button_b.fbx b/interface/resources/meshes/controller/touch/touch_r_button_b.fbx index 504374c26b..2c7a813832 100644 Binary files a/interface/resources/meshes/controller/touch/touch_r_button_b.fbx and b/interface/resources/meshes/controller/touch/touch_r_button_b.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_r_joystick.fbx b/interface/resources/meshes/controller/touch/touch_r_joystick.fbx index 8602f28d70..0fd3bbec96 100644 Binary files a/interface/resources/meshes/controller/touch/touch_r_joystick.fbx and b/interface/resources/meshes/controller/touch/touch_r_joystick.fbx differ diff --git a/interface/resources/meshes/controller/touch/touch_r_trigger.fbx b/interface/resources/meshes/controller/touch/touch_r_trigger.fbx index e43f066bce..694de0530c 100644 Binary files a/interface/resources/meshes/controller/touch/touch_r_trigger.fbx and b/interface/resources/meshes/controller/touch/touch_r_trigger.fbx differ diff --git a/interface/resources/meshes/drumstick.fbx b/interface/resources/meshes/drumstick.fbx new file mode 100644 index 0000000000..0243d9fd1b Binary files /dev/null and b/interface/resources/meshes/drumstick.fbx differ diff --git a/interface/resources/meshes/keyboard/SM_enter.fbx b/interface/resources/meshes/keyboard/SM_enter.fbx new file mode 100644 index 0000000000..119e3fc535 Binary files /dev/null and b/interface/resources/meshes/keyboard/SM_enter.fbx differ diff --git a/interface/resources/meshes/keyboard/SM_key.fbx b/interface/resources/meshes/keyboard/SM_key.fbx new file mode 100644 index 0000000000..02684a42d8 Binary files /dev/null and b/interface/resources/meshes/keyboard/SM_key.fbx differ diff --git a/interface/resources/meshes/keyboard/SM_space.fbx b/interface/resources/meshes/keyboard/SM_space.fbx new file mode 100644 index 0000000000..77632eb795 Binary files /dev/null and b/interface/resources/meshes/keyboard/SM_space.fbx differ diff --git a/interface/resources/meshes/keyboard/keyCap_F.png b/interface/resources/meshes/keyboard/keyCap_F.png new file mode 100644 index 0000000000..fba21a7d77 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_F.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_a.png b/interface/resources/meshes/keyboard/keyCap_a.png new file mode 100644 index 0000000000..f409254292 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_a.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_b.png b/interface/resources/meshes/keyboard/keyCap_b.png new file mode 100644 index 0000000000..5ab85290d1 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_b.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_c.png b/interface/resources/meshes/keyboard/keyCap_c.png new file mode 100644 index 0000000000..9a020163ab Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_c.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_d.png b/interface/resources/meshes/keyboard/keyCap_d.png new file mode 100644 index 0000000000..4eccee2dd5 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_d.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_e.png b/interface/resources/meshes/keyboard/keyCap_e.png new file mode 100644 index 0000000000..09bd5bc289 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_e.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_g.png b/interface/resources/meshes/keyboard/keyCap_g.png new file mode 100644 index 0000000000..9c52605428 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_g.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_h.png b/interface/resources/meshes/keyboard/keyCap_h.png new file mode 100644 index 0000000000..f29323da11 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_h.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_i.png b/interface/resources/meshes/keyboard/keyCap_i.png new file mode 100644 index 0000000000..721e6b84b1 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_i.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_j.png b/interface/resources/meshes/keyboard/keyCap_j.png new file mode 100644 index 0000000000..7186df71c1 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_j.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_k.png b/interface/resources/meshes/keyboard/keyCap_k.png new file mode 100644 index 0000000000..69667f42e0 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_k.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_l.png b/interface/resources/meshes/keyboard/keyCap_l.png new file mode 100644 index 0000000000..d59c24f7e2 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_l.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_m.png b/interface/resources/meshes/keyboard/keyCap_m.png new file mode 100644 index 0000000000..a6bc6729a8 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_m.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_n.png b/interface/resources/meshes/keyboard/keyCap_n.png new file mode 100644 index 0000000000..950fb88e41 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_n.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_o.png b/interface/resources/meshes/keyboard/keyCap_o.png new file mode 100644 index 0000000000..2c1755c887 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_o.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_p.png b/interface/resources/meshes/keyboard/keyCap_p.png new file mode 100644 index 0000000000..366a9ef3ff Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_p.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_q.png b/interface/resources/meshes/keyboard/keyCap_q.png new file mode 100644 index 0000000000..2d58bc2b46 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_q.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_r.png b/interface/resources/meshes/keyboard/keyCap_r.png new file mode 100644 index 0000000000..053151aada Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_r.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_s.png b/interface/resources/meshes/keyboard/keyCap_s.png new file mode 100644 index 0000000000..365a833af2 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_s.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_t.png b/interface/resources/meshes/keyboard/keyCap_t.png new file mode 100644 index 0000000000..6ee42e3ae4 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_t.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_u.png b/interface/resources/meshes/keyboard/keyCap_u.png new file mode 100644 index 0000000000..731467227a Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_u.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_v.png b/interface/resources/meshes/keyboard/keyCap_v.png new file mode 100644 index 0000000000..1dbe395005 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_v.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_w.png b/interface/resources/meshes/keyboard/keyCap_w.png new file mode 100644 index 0000000000..a71de5124d Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_w.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_x.png b/interface/resources/meshes/keyboard/keyCap_x.png new file mode 100644 index 0000000000..232725dd6c Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_x.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_y.png b/interface/resources/meshes/keyboard/keyCap_y.png new file mode 100644 index 0000000000..ed68e21384 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_y.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_z.png b/interface/resources/meshes/keyboard/keyCap_z.png new file mode 100644 index 0000000000..a1fe8d4181 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_z.png differ diff --git a/interface/resources/meshes/keyboard/key_0.png b/interface/resources/meshes/keyboard/key_0.png new file mode 100644 index 0000000000..ff852cda9d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_0.png differ diff --git a/interface/resources/meshes/keyboard/key_1.png b/interface/resources/meshes/keyboard/key_1.png new file mode 100644 index 0000000000..7115e92be8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_1.png differ diff --git a/interface/resources/meshes/keyboard/key_123.png b/interface/resources/meshes/keyboard/key_123.png new file mode 100644 index 0000000000..07a3187a70 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_123.png differ diff --git a/interface/resources/meshes/keyboard/key_2.png b/interface/resources/meshes/keyboard/key_2.png new file mode 100644 index 0000000000..99a7e650f1 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_2.png differ diff --git a/interface/resources/meshes/keyboard/key_3.png b/interface/resources/meshes/keyboard/key_3.png new file mode 100644 index 0000000000..5ec80db3e0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_3.png differ diff --git a/interface/resources/meshes/keyboard/key_4.png b/interface/resources/meshes/keyboard/key_4.png new file mode 100644 index 0000000000..e97261f2cd Binary files /dev/null and b/interface/resources/meshes/keyboard/key_4.png differ diff --git a/interface/resources/meshes/keyboard/key_5.png b/interface/resources/meshes/keyboard/key_5.png new file mode 100644 index 0000000000..59e059fbf4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_5.png differ diff --git a/interface/resources/meshes/keyboard/key_6.png b/interface/resources/meshes/keyboard/key_6.png new file mode 100644 index 0000000000..bf4e81a7a1 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_6.png differ diff --git a/interface/resources/meshes/keyboard/key_7.png b/interface/resources/meshes/keyboard/key_7.png new file mode 100644 index 0000000000..5d9765b37e Binary files /dev/null and b/interface/resources/meshes/keyboard/key_7.png differ diff --git a/interface/resources/meshes/keyboard/key_8.png b/interface/resources/meshes/keyboard/key_8.png new file mode 100644 index 0000000000..f905e2220c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_8.png differ diff --git a/interface/resources/meshes/keyboard/key_9.png b/interface/resources/meshes/keyboard/key_9.png new file mode 100644 index 0000000000..89a6397c82 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_9.png differ diff --git a/interface/resources/meshes/keyboard/key_a.png b/interface/resources/meshes/keyboard/key_a.png new file mode 100644 index 0000000000..74d57d5bd4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_a.png differ diff --git a/interface/resources/meshes/keyboard/key_abc.png b/interface/resources/meshes/keyboard/key_abc.png new file mode 100644 index 0000000000..5b7f1bcb0f Binary files /dev/null and b/interface/resources/meshes/keyboard/key_abc.png differ diff --git a/interface/resources/meshes/keyboard/key_ampersand.png b/interface/resources/meshes/keyboard/key_ampersand.png new file mode 100644 index 0000000000..e8e06892f8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_ampersand.png differ diff --git a/interface/resources/meshes/keyboard/key_ast.png b/interface/resources/meshes/keyboard/key_ast.png new file mode 100644 index 0000000000..1c6f03ed4a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_ast.png differ diff --git a/interface/resources/meshes/keyboard/key_at.png b/interface/resources/meshes/keyboard/key_at.png new file mode 100644 index 0000000000..0d0e9019a8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_at.png differ diff --git a/interface/resources/meshes/keyboard/key_b.png b/interface/resources/meshes/keyboard/key_b.png new file mode 100644 index 0000000000..50b607cbd3 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_b.png differ diff --git a/interface/resources/meshes/keyboard/key_backspace.png b/interface/resources/meshes/keyboard/key_backspace.png new file mode 100644 index 0000000000..db56841a4a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_backspace.png differ diff --git a/interface/resources/meshes/keyboard/key_c.png b/interface/resources/meshes/keyboard/key_c.png new file mode 100644 index 0000000000..da8a4a4f2d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_c.png differ diff --git a/interface/resources/meshes/keyboard/key_cap.png b/interface/resources/meshes/keyboard/key_cap.png new file mode 100644 index 0000000000..1e3c4c1b9f Binary files /dev/null and b/interface/resources/meshes/keyboard/key_cap.png differ diff --git a/interface/resources/meshes/keyboard/key_caret.png b/interface/resources/meshes/keyboard/key_caret.png new file mode 100644 index 0000000000..05011b905a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_caret.png differ diff --git a/interface/resources/meshes/keyboard/key_close_paren.png b/interface/resources/meshes/keyboard/key_close_paren.png new file mode 100644 index 0000000000..6a93b0fe05 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_close_paren.png differ diff --git a/interface/resources/meshes/keyboard/key_colon.png b/interface/resources/meshes/keyboard/key_colon.png new file mode 100644 index 0000000000..a4f025349c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_colon.png differ diff --git a/interface/resources/meshes/keyboard/key_comma.png b/interface/resources/meshes/keyboard/key_comma.png new file mode 100644 index 0000000000..69da507ec6 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_comma.png differ diff --git a/interface/resources/meshes/keyboard/key_d.png b/interface/resources/meshes/keyboard/key_d.png new file mode 100644 index 0000000000..557f3816f0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_d.png differ diff --git a/interface/resources/meshes/keyboard/key_dollar.png b/interface/resources/meshes/keyboard/key_dollar.png new file mode 100644 index 0000000000..0105debb68 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_dollar.png differ diff --git a/interface/resources/meshes/keyboard/key_dquote.png b/interface/resources/meshes/keyboard/key_dquote.png new file mode 100644 index 0000000000..393ab9b748 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_dquote.png differ diff --git a/interface/resources/meshes/keyboard/key_e.png b/interface/resources/meshes/keyboard/key_e.png new file mode 100644 index 0000000000..1b356d9d5b Binary files /dev/null and b/interface/resources/meshes/keyboard/key_e.png differ diff --git a/interface/resources/meshes/keyboard/key_enter.png b/interface/resources/meshes/keyboard/key_enter.png new file mode 100644 index 0000000000..cf935fe07e Binary files /dev/null and b/interface/resources/meshes/keyboard/key_enter.png differ diff --git a/interface/resources/meshes/keyboard/key_exclam.png b/interface/resources/meshes/keyboard/key_exclam.png new file mode 100644 index 0000000000..e9f68ced1a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_exclam.png differ diff --git a/interface/resources/meshes/keyboard/key_exit.png b/interface/resources/meshes/keyboard/key_exit.png new file mode 100644 index 0000000000..4c06660d56 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_exit.png differ diff --git a/interface/resources/meshes/keyboard/key_f.png b/interface/resources/meshes/keyboard/key_f.png new file mode 100644 index 0000000000..93306c6035 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_f.png differ diff --git a/interface/resources/meshes/keyboard/key_g.png b/interface/resources/meshes/keyboard/key_g.png new file mode 100644 index 0000000000..9fda692c04 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_g.png differ diff --git a/interface/resources/meshes/keyboard/key_h.png b/interface/resources/meshes/keyboard/key_h.png new file mode 100644 index 0000000000..c73f37c271 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_h.png differ diff --git a/interface/resources/meshes/keyboard/key_hashtag.png b/interface/resources/meshes/keyboard/key_hashtag.png new file mode 100644 index 0000000000..df673653b0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_hashtag.png differ diff --git a/interface/resources/meshes/keyboard/key_i.png b/interface/resources/meshes/keyboard/key_i.png new file mode 100644 index 0000000000..6277d8085c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_i.png differ diff --git a/interface/resources/meshes/keyboard/key_j.png b/interface/resources/meshes/keyboard/key_j.png new file mode 100644 index 0000000000..f723de1f55 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_j.png differ diff --git a/interface/resources/meshes/keyboard/key_k.png b/interface/resources/meshes/keyboard/key_k.png new file mode 100644 index 0000000000..aa3e806a8d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_k.png differ diff --git a/interface/resources/meshes/keyboard/key_l.png b/interface/resources/meshes/keyboard/key_l.png new file mode 100644 index 0000000000..4b31a84f32 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_l.png differ diff --git a/interface/resources/meshes/keyboard/key_m.png b/interface/resources/meshes/keyboard/key_m.png new file mode 100644 index 0000000000..2e83b7b214 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_m.png differ diff --git a/interface/resources/meshes/keyboard/key_min.png b/interface/resources/meshes/keyboard/key_min.png new file mode 100644 index 0000000000..e45beb4e7c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_min.png differ diff --git a/interface/resources/meshes/keyboard/key_n.png b/interface/resources/meshes/keyboard/key_n.png new file mode 100644 index 0000000000..be1a6a9e7a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_n.png differ diff --git a/interface/resources/meshes/keyboard/key_o.png b/interface/resources/meshes/keyboard/key_o.png new file mode 100644 index 0000000000..883feec871 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_o.png differ diff --git a/interface/resources/meshes/keyboard/key_open_paren.png b/interface/resources/meshes/keyboard/key_open_paren.png new file mode 100644 index 0000000000..9dcee0b9a0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_open_paren.png differ diff --git a/interface/resources/meshes/keyboard/key_p.png b/interface/resources/meshes/keyboard/key_p.png new file mode 100644 index 0000000000..8d55d351ce Binary files /dev/null and b/interface/resources/meshes/keyboard/key_p.png differ diff --git a/interface/resources/meshes/keyboard/key_percentage.png b/interface/resources/meshes/keyboard/key_percentage.png new file mode 100644 index 0000000000..fbe1fa9599 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_percentage.png differ diff --git a/interface/resources/meshes/keyboard/key_period.png b/interface/resources/meshes/keyboard/key_period.png new file mode 100644 index 0000000000..ff726df39b Binary files /dev/null and b/interface/resources/meshes/keyboard/key_period.png differ diff --git a/interface/resources/meshes/keyboard/key_plus.png b/interface/resources/meshes/keyboard/key_plus.png new file mode 100644 index 0000000000..3eab84c34d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_plus.png differ diff --git a/interface/resources/meshes/keyboard/key_q.png b/interface/resources/meshes/keyboard/key_q.png new file mode 100644 index 0000000000..fc733fd7fd Binary files /dev/null and b/interface/resources/meshes/keyboard/key_q.png differ diff --git a/interface/resources/meshes/keyboard/key_question.png b/interface/resources/meshes/keyboard/key_question.png new file mode 100644 index 0000000000..57f5187213 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_question.png differ diff --git a/interface/resources/meshes/keyboard/key_r.png b/interface/resources/meshes/keyboard/key_r.png new file mode 100644 index 0000000000..6277c64097 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_r.png differ diff --git a/interface/resources/meshes/keyboard/key_s.png b/interface/resources/meshes/keyboard/key_s.png new file mode 100644 index 0000000000..1fc1085391 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_s.png differ diff --git a/interface/resources/meshes/keyboard/key_semi.png b/interface/resources/meshes/keyboard/key_semi.png new file mode 100644 index 0000000000..5cb1b495a4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_semi.png differ diff --git a/interface/resources/meshes/keyboard/key_slash.png b/interface/resources/meshes/keyboard/key_slash.png new file mode 100644 index 0000000000..be7b0fecb4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_slash.png differ diff --git a/interface/resources/meshes/keyboard/key_squote.png b/interface/resources/meshes/keyboard/key_squote.png new file mode 100644 index 0000000000..3239c19ceb Binary files /dev/null and b/interface/resources/meshes/keyboard/key_squote.png differ diff --git a/interface/resources/meshes/keyboard/key_t.png b/interface/resources/meshes/keyboard/key_t.png new file mode 100644 index 0000000000..c2082b8f51 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_t.png differ diff --git a/interface/resources/meshes/keyboard/key_u.png b/interface/resources/meshes/keyboard/key_u.png new file mode 100644 index 0000000000..657527f6c0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_u.png differ diff --git a/interface/resources/meshes/keyboard/key_under.png b/interface/resources/meshes/keyboard/key_under.png new file mode 100644 index 0000000000..3694dd3109 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_under.png differ diff --git a/interface/resources/meshes/keyboard/key_v.png b/interface/resources/meshes/keyboard/key_v.png new file mode 100644 index 0000000000..c061ca6fa0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_v.png differ diff --git a/interface/resources/meshes/keyboard/key_w.png b/interface/resources/meshes/keyboard/key_w.png new file mode 100644 index 0000000000..15de9b25a8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_w.png differ diff --git a/interface/resources/meshes/keyboard/key_x.png b/interface/resources/meshes/keyboard/key_x.png new file mode 100644 index 0000000000..d81a423f3b Binary files /dev/null and b/interface/resources/meshes/keyboard/key_x.png differ diff --git a/interface/resources/meshes/keyboard/key_y.png b/interface/resources/meshes/keyboard/key_y.png new file mode 100644 index 0000000000..cb85af5b32 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_y.png differ diff --git a/interface/resources/meshes/keyboard/key_z.png b/interface/resources/meshes/keyboard/key_z.png new file mode 100644 index 0000000000..462531351d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_z.png differ diff --git a/interface/resources/meshes/keyboard/text_placard.png b/interface/resources/meshes/keyboard/text_placard.png new file mode 100644 index 0000000000..b0a5953a30 Binary files /dev/null and b/interface/resources/meshes/keyboard/text_placard.png differ diff --git a/interface/resources/meshes/keyboard/white.png b/interface/resources/meshes/keyboard/white.png new file mode 100644 index 0000000000..9673f508fc Binary files /dev/null and b/interface/resources/meshes/keyboard/white.png differ diff --git a/interface/resources/meshes/mannequin/+gles/Eyes.ktx b/interface/resources/meshes/mannequin/+gles/Eyes.ktx deleted file mode 100644 index 0cccc03dc5..0000000000 Binary files a/interface/resources/meshes/mannequin/+gles/Eyes.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/+gles/lambert1_Base_Color.ktx b/interface/resources/meshes/mannequin/+gles/lambert1_Base_Color.ktx deleted file mode 100644 index b0baa696d5..0000000000 Binary files a/interface/resources/meshes/mannequin/+gles/lambert1_Base_Color.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/+gles/lambert1_Normal_OpenGL.ktx b/interface/resources/meshes/mannequin/+gles/lambert1_Normal_OpenGL.ktx deleted file mode 100644 index 0960d30ce6..0000000000 Binary files a/interface/resources/meshes/mannequin/+gles/lambert1_Normal_OpenGL.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/+gles/lambert1_Roughness.ktx b/interface/resources/meshes/mannequin/+gles/lambert1_Roughness.ktx deleted file mode 100644 index 6d87e6e337..0000000000 Binary files a/interface/resources/meshes/mannequin/+gles/lambert1_Roughness.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/Eyes.texmeta.json b/interface/resources/meshes/mannequin/Eyes.texmeta.json new file mode 100644 index 0000000000..c5b215cbe8 --- /dev/null +++ b/interface/resources/meshes/mannequin/Eyes.texmeta.json @@ -0,0 +1,8 @@ +{ + "compressed": { + "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC": "../Eyes_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx", + "COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT": "../Eyes_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT.ktx" + }, + "original": "../Eyes.png", + "uncompressed": "" +} diff --git a/interface/resources/meshes/mannequin/Eyes_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx b/interface/resources/meshes/mannequin/Eyes_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx new file mode 100644 index 0000000000..3abfdebce0 Binary files /dev/null and b/interface/resources/meshes/mannequin/Eyes_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx differ diff --git a/interface/resources/meshes/mannequin/Eyes_bcn.ktx b/interface/resources/meshes/mannequin/Eyes_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT.ktx similarity index 100% rename from interface/resources/meshes/mannequin/Eyes_bcn.ktx rename to interface/resources/meshes/mannequin/Eyes_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT.ktx diff --git a/interface/resources/meshes/mannequin/StingrayPBS10_Base_Color.png b/interface/resources/meshes/mannequin/StingrayPBS10_Base_Color.png deleted file mode 100644 index 941955916a..0000000000 Binary files a/interface/resources/meshes/mannequin/StingrayPBS10_Base_Color.png and /dev/null differ diff --git a/interface/resources/meshes/mannequin/StingrayPBS10_Base_Color_bcn.ktx b/interface/resources/meshes/mannequin/StingrayPBS10_Base_Color_bcn.ktx deleted file mode 100644 index cbb6cfa992..0000000000 Binary files a/interface/resources/meshes/mannequin/StingrayPBS10_Base_Color_bcn.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/StingrayPBS10_Normal_OpenGL.png b/interface/resources/meshes/mannequin/StingrayPBS10_Normal_OpenGL.png deleted file mode 100644 index fc86048656..0000000000 Binary files a/interface/resources/meshes/mannequin/StingrayPBS10_Normal_OpenGL.png and /dev/null differ diff --git a/interface/resources/meshes/mannequin/StingrayPBS10_Normal_OpenGL_bcn.ktx b/interface/resources/meshes/mannequin/StingrayPBS10_Normal_OpenGL_bcn.ktx deleted file mode 100644 index aad0b108b3..0000000000 Binary files a/interface/resources/meshes/mannequin/StingrayPBS10_Normal_OpenGL_bcn.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/lambert1_Base_Color.png b/interface/resources/meshes/mannequin/lambert1_Base_Color.png new file mode 100644 index 0000000000..3553c39442 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Base_Color.png differ diff --git a/interface/resources/meshes/mannequin/lambert1_Base_Color.texmeta.json b/interface/resources/meshes/mannequin/lambert1_Base_Color.texmeta.json new file mode 100644 index 0000000000..e7d39041b9 --- /dev/null +++ b/interface/resources/meshes/mannequin/lambert1_Base_Color.texmeta.json @@ -0,0 +1,8 @@ +{ + "compressed": { + "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC": "../lambert1_Base_Color_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx", + "COMPRESSED_SRGB_S3TC_DXT1_EXT": "../lambert1_Base_Color_COMPRESSED_SRGB_S3TC_DXT1_EXT.ktx" + }, + "original": "../lambert1_Base_Color.png", + "uncompressed": "" +} diff --git a/interface/resources/meshes/mannequin/lambert1_Base_Color_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx b/interface/resources/meshes/mannequin/lambert1_Base_Color_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx new file mode 100644 index 0000000000..88761cfc54 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Base_Color_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Base_Color_COMPRESSED_SRGB_S3TC_DXT1_EXT.ktx b/interface/resources/meshes/mannequin/lambert1_Base_Color_COMPRESSED_SRGB_S3TC_DXT1_EXT.ktx new file mode 100644 index 0000000000..d2bc8e1659 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Base_Color_COMPRESSED_SRGB_S3TC_DXT1_EXT.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.png b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.png new file mode 100644 index 0000000000..10fd7d4da6 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.png differ diff --git a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.texmeta.json b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.texmeta.json new file mode 100644 index 0000000000..9f101d15f2 --- /dev/null +++ b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.texmeta.json @@ -0,0 +1,8 @@ +{ + "compressed": { + "COMPRESSED_RG11_EAC": "../lambert1_Normal_OpenGL_COMPRESSED_RG11_EAC.ktx", + "COMPRESSED_RG_RGTC2": "../lambert1_Normal_OpenGL_COMPRESSED_RG_RGTC2.ktx" + }, + "original": "../lambert1_Normal_OpenGL.png", + "uncompressed": "" +} diff --git a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL_COMPRESSED_RG11_EAC.ktx b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL_COMPRESSED_RG11_EAC.ktx new file mode 100644 index 0000000000..4026edb51c Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL_COMPRESSED_RG11_EAC.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL_COMPRESSED_RG_RGTC2.ktx b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL_COMPRESSED_RG_RGTC2.ktx new file mode 100644 index 0000000000..576895ae69 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL_COMPRESSED_RG_RGTC2.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness.png b/interface/resources/meshes/mannequin/lambert1_Roughness.png index 9efa372541..aa8073623e 100644 Binary files a/interface/resources/meshes/mannequin/lambert1_Roughness.png and b/interface/resources/meshes/mannequin/lambert1_Roughness.png differ diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness.texmeta.json b/interface/resources/meshes/mannequin/lambert1_Roughness.texmeta.json new file mode 100644 index 0000000000..ce0a3d9aa0 --- /dev/null +++ b/interface/resources/meshes/mannequin/lambert1_Roughness.texmeta.json @@ -0,0 +1,8 @@ +{ + "compressed": { + "COMPRESSED_R11_EAC": "../lambert1_Roughness_COMPRESSED_R11_EAC.ktx", + "COMPRESSED_RED_RGTC1": "../lambert1_Roughness_COMPRESSED_RED_RGTC1.ktx" + }, + "original": "../lambert1_Roughness.png", + "uncompressed": "" +} diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness_COMPRESSED_R11_EAC.ktx b/interface/resources/meshes/mannequin/lambert1_Roughness_COMPRESSED_R11_EAC.ktx new file mode 100644 index 0000000000..c5f8d8e7d9 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Roughness_COMPRESSED_R11_EAC.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness_COMPRESSED_RED_RGTC1.ktx b/interface/resources/meshes/mannequin/lambert1_Roughness_COMPRESSED_RED_RGTC1.ktx new file mode 100644 index 0000000000..8cced84f77 Binary files /dev/null and b/interface/resources/meshes/mannequin/lambert1_Roughness_COMPRESSED_RED_RGTC1.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness_bcn.ktx b/interface/resources/meshes/mannequin/lambert1_Roughness_bcn.ktx deleted file mode 100644 index 628e6ce99b..0000000000 Binary files a/interface/resources/meshes/mannequin/lambert1_Roughness_bcn.ktx and /dev/null differ diff --git a/interface/resources/meshes/mannequin/mannequin.baked.fbx b/interface/resources/meshes/mannequin/mannequin.baked.fbx index 63611b0b6c..6151caa89e 100644 Binary files a/interface/resources/meshes/mannequin/mannequin.baked.fbx and b/interface/resources/meshes/mannequin/mannequin.baked.fbx differ diff --git a/interface/resources/qml/+android/Stats.qml b/interface/resources/qml/+android/Stats.qml index 0dcb07e730..e9a2aa47eb 100644 --- a/interface/resources/qml/+android/Stats.qml +++ b/interface/resources/qml/+android/Stats.qml @@ -10,6 +10,7 @@ Item { property int modality: Qt.NonModal implicitHeight: row.height implicitWidth: row.width + visible: false Component.onCompleted: { stats.parentChanged.connect(fill); diff --git a/interface/resources/qml/AudioScopeUI.qml b/interface/resources/qml/AudioScopeUI.qml index aa181dbf8d..91908807e2 100644 --- a/interface/resources/qml/AudioScopeUI.qml +++ b/interface/resources/qml/AudioScopeUI.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "styles-uit" -import "controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit Item { id: root diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 4474cfb2cd..01de7a36f9 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -2,9 +2,9 @@ import QtQuick 2.5 import QtWebChannel 1.0 import QtWebEngine 1.5 -import "controls-uit" +import controlsUit 1.0 import "styles" as HifiStyles -import "styles-uit" +import stylesUit 1.0 import "windows" ScrollingWindow { diff --git a/interface/resources/qml/CurrentAPI.qml b/interface/resources/qml/CurrentAPI.qml index 96bfb5c36b..4ea45041c3 100644 --- a/interface/resources/qml/CurrentAPI.qml +++ b/interface/resources/qml/CurrentAPI.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "styles-uit" -import "controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { id: root diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index f18969fb2f..8c5900b4c3 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import Hifi 1.0 as Hifi -import "controls-uit" +import controlsUit 1.0 import "windows" as Windows Windows.ScrollingWindow { diff --git a/interface/resources/qml/InteractiveWindow.qml b/interface/resources/qml/InteractiveWindow.qml index e8ddbf823d..c217238e93 100644 --- a/interface/resources/qml/InteractiveWindow.qml +++ b/interface/resources/qml/InteractiveWindow.qml @@ -12,9 +12,9 @@ import QtQuick 2.3 import "windows" as Windows import "controls" -import "controls-uit" as Controls +import controlsUit 1.0 as Controls import "styles" -import "styles-uit" +import stylesUit 1.0 Windows.Window { id: root; diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 336858502d..12117aaba4 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.4 -import "controls-uit" -import "styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "windows" import "LoginDialog" diff --git a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml index 96b638c911..a40110b1e9 100644 --- a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml @@ -13,8 +13,8 @@ import QtQuick 2.4 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: linkAccountBody diff --git a/interface/resources/qml/LoginDialog/+android/SignUpBody.qml b/interface/resources/qml/LoginDialog/+android/SignUpBody.qml index 3a44a8d741..10909e4c85 100644 --- a/interface/resources/qml/LoginDialog/+android/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/+android/SignUpBody.qml @@ -13,8 +13,8 @@ import QtQuick 2.4 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: signupBody diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index fe4c511f1d..3a57061de4 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: completeProfileBody diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 48cf124127..103761236d 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -13,8 +13,9 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 + Item { id: linkAccountBody clip: true @@ -96,7 +97,7 @@ Item { topMargin: hifi.dimensions.contentSpacing.y } - text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!") + text: qsTr("Sign in to High Fidelity to make friends, get HFC, and get interesting things on the Marketplace!") width: parent.width wrapMode: Text.WordWrap lineHeight: 1 @@ -136,7 +137,7 @@ Item { TextField { id: usernameField - text: Settings.getValue("wallet/savedUsername", ""); + text: Settings.getValue("keepMeLoggedIn/savedUsername", ""); width: parent.width focus: true placeholderText: "Username or Email" @@ -165,7 +166,7 @@ Item { root.text = ""; } Component.onCompleted: { - var savedUsername = Settings.getValue("wallet/savedUsername", ""); + var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", ""); usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername; } } @@ -239,7 +240,10 @@ Item { } - Keys.onReturnPressed: linkAccountBody.login() + Keys.onReturnPressed: { + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); + linkAccountBody.login(); + } } InfoItem { @@ -263,21 +267,21 @@ Item { CheckBox { id: autoLogoutCheckbox - checked: !Settings.getValue("wallet/autoLogout", true) - text: "Keep me signed in" + checked: Settings.getValue("keepMeLoggedIn", false) + text: "Keep me logged in" boxSize: 20; labelFontSize: 15 color: hifi.colors.black onCheckedChanged: { - Settings.setValue("wallet/autoLogout", !checked); + Settings.setValue("keepMeLoggedIn", checked); if (checked) { - Settings.setValue("wallet/savedUsername", Account.username); + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); } else { - Settings.setValue("wallet/savedUsername", ""); + Settings.setValue("keepMeLoggedIn/savedUsername", ""); } } Component.onDestruction: { - Settings.setValue("wallet/autoLogout", !checked); + Settings.setValue("keepMeLoggedIn", checked); } } @@ -289,7 +293,10 @@ Item { text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Log in") color: hifi.buttons.blue - onClicked: linkAccountBody.login() + onClicked: { + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); + linkAccountBody.login(); + } } } @@ -403,6 +410,7 @@ Item { case Qt.Key_Enter: case Qt.Key_Return: event.accepted = true + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); linkAccountBody.login() break } diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 9cb1add704..7fe29e13f6 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: signInBody diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index bb30696e4c..d3c898d76f 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls 1.4 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: signupBody diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index bf05a36ce1..2a41353534 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.4 import QtQuick.Controls 1.4 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: usernameCollisionBody diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml index 551ec263b7..020e6db002 100644 --- a/interface/resources/qml/LoginDialog/WelcomeBody.qml +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.4 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: welcomeBody diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 8c4d6145ec..322535641d 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -13,8 +13,8 @@ import QtWebEngine 1.1 import QtWebChannel 1.0 import "windows" as Windows -import "controls-uit" as Controls -import "styles-uit" +import controlsUit 1.0 as Controls +import stylesUit 1.0 Windows.ScrollingWindow { id: root diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index bef6423e25..53e6bcc37d 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -2,9 +2,9 @@ import QtQuick 2.3 import "windows" as Windows import "controls" -import "controls-uit" as Controls +import controlsUit 1.0 as Controls import "styles" -import "styles-uit" +import stylesUit 1.0 Windows.Window { id: root diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml index 141c1f25a7..720a904231 100644 --- a/interface/resources/qml/TabletBrowser.qml +++ b/interface/resources/qml/TabletBrowser.qml @@ -3,9 +3,9 @@ import QtWebChannel 1.0 import QtWebEngine 1.5 import "controls" -import "controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "styles" as HifiStyles -import "styles-uit" +import stylesUit 1.0 import "windows" Item { diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 5e05601ce4..9c22d0b65b 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -4,9 +4,9 @@ import QtQuick.Controls 1.3 import QtQuick.Controls.Styles 1.3 import QtGraphicalEffects 1.0 -import "controls-uit" +import controlsUit 1.0 import "styles" as HifiStyles -import "styles-uit" +import stylesUit 1.0 import "windows" ScrollingWindow { diff --git a/interface/resources/qml/controls-uit/AttachmentsTable.qml b/interface/resources/qml/controls-uit/AttachmentsTable.qml index 8ee9909ab8..e7fe874610 100644 --- a/interface/resources/qml/controls-uit/AttachmentsTable.qml +++ b/interface/resources/qml/controls-uit/AttachmentsTable.qml @@ -1,170 +1,4 @@ -// -// AttachmentsTable.qml -// -// Created by David Rowe on 18 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.XmlListModel 2.0 - -import "../styles-uit" -import "../controls-uit" as HifiControls -import "../windows" -import "../hifi/models" - -TableView { - id: tableView - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - - model: S3Model{} - - Rectangle { - anchors.fill: parent - visible: tableView.model.status !== XmlListModel.Ready - color: hifi.colors.darkGray0 - BusyIndicator { - anchors.centerIn: parent - width: 48; height: 48 - running: true - } - } - - headerDelegate: Rectangle { - height: hifi.dimensions.tableHeaderHeight - color: hifi.colors.darkGray - border.width: 0.5 - border.color: hifi.colors.baseGrayHighlight - - RalewayRegular { - id: textHeader - size: hifi.fontSizes.tableHeading - color: hifi.colors.lightGrayText - text: styleData.value - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - } - } - - // Use rectangle to draw border with rounded corners. - Rectangle { - color: "#00000000" - anchors { fill: parent; margins: -2 } - radius: hifi.dimensions.borderRadius - border.color: hifi.colors.baseGrayHighlight - border.width: 3 - } - anchors.margins: 2 // Shrink TableView to lie within border. - backgroundVisible: true - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAsNeeded - - style: TableViewStyle { - // Needed in order for rows to keep displaying rows after end of table entries. - backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven - alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd - - handle: Item { - id: scrollbarHandle - implicitWidth: 6 - Rectangle { - anchors { - fill: parent - leftMargin: 2 // Move it right - rightMargin: -2 // "" - topMargin: 3 // Shrink vertically - bottomMargin: 3 // "" - } - radius: 3 - color: hifi.colors.tableScrollHandleDark - } - } - - scrollBarBackground: Item { - implicitWidth: 10 - Rectangle { - anchors { - fill: parent - margins: -1 // Expand - } - color: hifi.colors.baseGrayHighlight - } - - Rectangle { - anchors { - fill: parent - margins: 1 // Shrink - } - radius: 4 - color: hifi.colors.tableScrollBackgroundDark - } - } - - incrementControl: Item { - visible: false - } - - decrementControl: Item { - visible: false - } - } - - rowDelegate: Rectangle { - height: hifi.dimensions.tableRowHeight - color: styleData.selected - ? hifi.colors.primaryHighlight - : tableView.isLightColorScheme - ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) - : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) - } - - itemDelegate: Item { - anchors { - left: parent ? parent.left : undefined - leftMargin: hifi.dimensions.tablePadding - right: parent ? parent.right : undefined - rightMargin: hifi.dimensions.tablePadding - } - FiraSansSemiBold { - id: textItem - text: styleData.value - size: hifi.fontSizes.tableText - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - } - } - - TableViewColumn { - role: "name" - title: "NAME" - width: parent.width *0.3 - horizontalAlignment: Text.AlignHCenter - } - TableViewColumn { - role: "size" - title: "SIZE" - width: parent.width *0.2 - horizontalAlignment: Text.AlignHCenter - } - TableViewColumn { - role: "modified" - title: "LAST MODIFIED" - width: parent.width *0.5 - horizontalAlignment: Text.AlignHCenter - } -} +AttachmentsTable { +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/BaseWebView.qml b/interface/resources/qml/controls-uit/BaseWebView.qml index fdd9c12220..61f428e9f7 100644 --- a/interface/resources/qml/controls-uit/BaseWebView.qml +++ b/interface/resources/qml/controls-uit/BaseWebView.qml @@ -1,38 +1,4 @@ -// -// WebView.qml -// -// Created by Bradley Austin Davis on 12 Jan 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.7 -import QtWebEngine 1.5 - -WebEngineView { - id: root - - Component.onCompleted: { - console.log("Connecting JS messaging to Hifi Logging") - // Ensure the JS from the web-engine makes it to our logging - root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); - }); - } - - onLoadingChanged: { - // Required to support clicking on "hifi://" links - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var url = loadRequest.url.toString(); - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - root.stop(); - } - } - } - } - - WebSpinner { } -} +BaseWebView { +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index f1a6e4bb4a..1d31f02777 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -1,122 +1,4 @@ -// -// Button.qml -// -// Created by David Rowe on 16 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.7 -import QtQuick.Controls 2.3 as Original -import TabletScriptingInterface 1.0 - -import "../styles-uit" - -Original.Button { - id: control; - - property int color: 0 - property int colorScheme: hifi.colorSchemes.light - property int fontSize: hifi.fontSizes.buttonLabel - property int radius: hifi.buttons.radius - property alias implicitTextWidth: buttonText.implicitWidth - property string buttonGlyph: ""; - property int fontCapitalization: Font.AllUppercase - - width: hifi.dimensions.buttonWidth - height: hifi.dimensions.controlLineHeight - - HifiConstants { id: hifi } - - onHoveredChanged: { - if (hovered) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onFocusChanged: { - if (focus) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - } - - background: Rectangle { - radius: control.radius - - border.width: (control.color === hifi.buttons.none || - (control.color === hifi.buttons.noneBorderless && control.hovered) || - (control.color === hifi.buttons.noneBorderlessWhite && control.hovered) || - (control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0; - border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight : - (control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white); - - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorStart[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorStart[control.color] - } - } - } - GradientStop { - position: 1.0 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorFinish[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else { - hifi.buttons.colorFinish[control.color] - } - } - } - } - } - - contentItem: Item { - HiFiGlyphs { - id: buttonGlyph; - visible: control.buttonGlyph !== ""; - text: control.buttonGlyph === "" ? hifi.glyphs.question : control.buttonGlyph; - // Size - size: 34; - // Anchors - anchors.right: buttonText.left; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - // Style - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme]; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - } - RalewayBold { - id: buttonText; - anchors.centerIn: parent; - font.capitalization: control.fontCapitalization - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - size: control.fontSize - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.text - } - } +Button { } - diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 6e4a3df010..c10bea08c4 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -1,121 +1,4 @@ -// -// CheckBox.qml -// -// Created by David Rowe on 26 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.2 -import QtQuick.Controls 2.2 as Original - -import "../styles-uit" - -import TabletScriptingInterface 1.0 - -Original.CheckBox { - id: checkBox - - property int colorScheme: hifi.colorSchemes.light - property string color: hifi.colors.lightGrayText - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - property bool isRedCheck: false - property int boxSize: 14 - property int boxRadius: 3 - property bool wrap: true; - readonly property int checkSize: Math.max(boxSize - 8, 10) - readonly property int checkRadius: 2 - property string labelFontFamily: "Raleway" - property int labelFontSize: 14; - property int labelFontWeight: Font.DemiBold; - focusPolicy: Qt.ClickFocus - hoverEnabled: true - - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - } - - onHoveredChanged: { - if (hovered) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - - indicator: Rectangle { - id: box - implicitWidth: boxSize - implicitHeight: boxSize - radius: boxRadius - y: parent.height / 2 - height / 2 - border.width: 1 - border.color: pressed || hovered - ? hifi.colors.checkboxCheckedBorder - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - - gradient: Gradient { - GradientStop { - position: 0.2 - color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - } - } - - Rectangle { - visible: pressed || hovered - anchors.centerIn: parent - id: innerBox - width: checkSize - 4 - height: width - radius: checkRadius - color: hifi.colors.checkboxCheckedBorder - } - - Rectangle { - id: check - width: checkSize - height: checkSize - radius: checkRadius - anchors.centerIn: parent - color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked - border.width: 2 - border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder - visible: checked && !pressed || !checked && pressed - } - - Rectangle { - id: disabledOverlay - visible: !enabled - width: boxSize - height: boxSize - radius: boxRadius - border.width: 1 - border.color: hifi.colors.baseGrayHighlight - color: hifi.colors.baseGrayHighlight - opacity: 0.5 - } - } - - contentItem: Label { - text: checkBox.text - color: checkBox.color - font.family: checkBox.labelFontFamily; - font.pixelSize: checkBox.labelFontSize; - font.weight: checkBox.labelFontWeight; - x: 2 - verticalAlignment: Text.AlignVCenter - wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap - elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight - enabled: checkBox.enabled - leftPadding: checkBox.indicator.width + checkBox.spacing - } +CheckBox { } diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml index 8a9686ff5e..af758ee707 100644 --- a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml +++ b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml @@ -1,125 +1,4 @@ -// -// CheckBox2.qml -// -// Created by Vlad Stelmahovsky on 10 Aug 2017 -// Copyright 2017 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 -// - -import QtQuick 2.7 -import QtQuick.Controls 2.2 - -import "../styles-uit" -import "../controls-uit" as HiFiControls -import TabletScriptingInterface 1.0 - -CheckBox { - id: checkBox - - HifiConstants { id: hifi; } - - padding: 0 - leftPadding: 0 - property int colorScheme: hifi.colorSchemes.light - property string color: hifi.colors.lightGrayText - readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light - property bool isRedCheck: false - property bool isRound: false - property int boxSize: 14 - property int boxRadius: isRound ? boxSize : 3 - property bool wrap: true; - readonly property int checkSize: Math.max(boxSize - 8, 10) - readonly property int checkRadius: isRound ? checkSize / 2 : 2 - focusPolicy: Qt.ClickFocus - hoverEnabled: true - - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - } - - onHoveredChanged: { - if (hovered) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - indicator: Rectangle { - id: box - implicitWidth: boxSize - implicitHeight: boxSize - radius: boxRadius - x: checkBox.leftPadding - y: parent.height / 2 - height / 2 - border.width: 1 - border.color: pressed || hovered - ? hifi.colors.checkboxCheckedBorder - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - - gradient: Gradient { - GradientStop { - position: 0.2 - color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) - : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - } - } - - Rectangle { - visible: pressed || hovered - anchors.centerIn: parent - id: innerBox - width: checkSize - 4 - height: width - radius: checkRadius - color: hifi.colors.checkboxCheckedBorder - } - - Rectangle { - id: check - width: checkSize - height: checkSize - radius: checkRadius - anchors.centerIn: parent - color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked - border.width: 2 - border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder - visible: checked && !pressed || !checked && pressed - } - - Rectangle { - id: disabledOverlay - visible: !enabled - width: boxSize - height: boxSize - radius: boxRadius - border.width: 1 - border.color: hifi.colors.baseGrayHighlight - color: hifi.colors.baseGrayHighlight - opacity: 0.5 - } - } - - contentItem: Text { - id: root - font.pixelSize: hifi.fontSizes.inputLabel - font.family: "Raleway" - font.weight: Font.DemiBold - text: checkBox.text - color: checkBox.color - x: 2 - wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap - elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight - enabled: checkBox.enabled - verticalAlignment: Text.AlignVCenter - leftPadding: checkBox.indicator.width + checkBox.spacing - } -} +import controlsUit 1.0 +CheckBoxQQC2 { +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index 245b565a62..8ac92909b6 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -1,191 +1,4 @@ -// -// ComboBox.qml -// -// Created by Bradley Austin David on 27 Jan 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.7 -import QtQuick.Controls 2.2 - -import "../styles-uit" -import "../controls-uit" as HifiControls - -FocusScope { - id: root - HifiConstants { id: hifi } - - property alias model: comboBox.model; - property alias editable: comboBox.editable - property alias comboBox: comboBox - readonly property alias currentText: comboBox.currentText; - property alias currentIndex: comboBox.currentIndex; - property int currentHighLightedIndex: comboBox.currentIndex; - - property int dropdownHeight: 480 - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - property string label: "" - property real controlHeight: height + (comboBoxLabel.visible ? comboBoxLabel.height + comboBoxLabel.anchors.bottomMargin : 0) - - readonly property ComboBox control: comboBox - - property bool isDesktop: true - - signal accepted(); - - implicitHeight: comboBox.height; - focus: true - - ComboBox { - id: comboBox - anchors.fill: parent - hoverEnabled: true - visible: true - height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. - - function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; } - function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; } - function selectCurrentItem() { root.currentIndex = root.currentHighLightedIndex; close(); /*hideList();*/ } - function selectSpecificItem(index) { root.currentIndex = index; close();/*hideList();*/ } - - Keys.onUpPressed: previousItem(); - Keys.onDownPressed: nextItem(); - Keys.onSpacePressed: selectCurrentItem(); - Keys.onRightPressed: selectCurrentItem(); - Keys.onReturnPressed: selectCurrentItem(); - - background: Rectangle { - gradient: Gradient { - GradientStop { - position: 0.2 - color: comboBox.popup.visible - ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) - : (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart) - } - GradientStop { - position: 1.0 - color: comboBox.popup.visible - ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) - : (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish) - } - } - } - - indicator: Item { - id: dropIcon - anchors { right: parent.right; verticalCenter: parent.verticalCenter } - height: root.height - width: height - Rectangle { - width: 1 - height: parent.height - anchors.top: parent.top - anchors.left: parent.left - color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray - } - HiFiGlyphs { - anchors { top: parent.top; topMargin: -11; horizontalCenter: parent.horizontalCenter } - size: hifi.dimensions.spinnerSize - text: hifi.glyphs.caratDn - color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText) - } - } - - contentItem: FiraSansSemiBold { - id: textField - anchors { - left: parent.left - leftMargin: hifi.dimensions.textPadding - verticalCenter: parent.verticalCenter - } - size: hifi.fontSizes.textFieldInput - text: comboBox.displayText ? comboBox.displayText : comboBox.currentText - elide: Text.ElideRight - color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) - } - - delegate: ItemDelegate { - id: itemDelegate - hoverEnabled: true - width: root.width + 4 - height: popupText.implicitHeight * 1.4 - highlighted: root.currentHighLightedIndex == index - - onHoveredChanged: { - if (hovered) { - root.currentHighLightedIndex = index - } - } - - background: Rectangle { - color: itemDelegate.highlighted ? hifi.colors.primaryHighlight - : (isLightColorScheme ? hifi.colors.dropDownPressedLight - : hifi.colors.dropDownPressedDark) - } - - contentItem: FiraSansSemiBold { - id: popupText - anchors.left: parent.left - anchors.leftMargin: hifi.dimensions.textPadding - anchors.verticalCenter: parent.verticalCenter - text: comboBox.model[index] ? comboBox.model[index] - : (comboBox.model.get && comboBox.model.get(index).text ? - comboBox.model.get(index).text : "") - size: hifi.fontSizes.textFieldInput - color: hifi.colors.baseGray - } - } - popup: Popup { - y: comboBox.height - 1 - width: comboBox.width - implicitHeight: listView.contentHeight > dropdownHeight ? dropdownHeight - : listView.contentHeight - padding: 0 - topPadding: 1 - - onClosed: { - root.accepted() - } - - contentItem: ListView { - id: listView - clip: true - model: comboBox.popup.visible ? comboBox.delegateModel : null - currentIndex: root.currentHighLightedIndex - delegate: comboBox.delegate - ScrollBar.vertical: HifiControls.ScrollBar { - id: scrollbar - parent: listView - policy: ScrollBar.AsNeeded - visible: size < 1.0 - } - } - - background: Rectangle { - color: hifi.colors.baseGray - } - } - } - - function textAt(index) { - return comboBox.textAt(index); - } - - HifiControls.Label { - id: comboBoxLabel - text: root.label - colorScheme: root.colorScheme - anchors.left: parent.left - anchors.bottom: parent.top - anchors.bottomMargin: 4 - visible: label != "" - } - - Component.onCompleted: { - isDesktop = (typeof desktop !== "undefined"); - } +ComboBox { } diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controls-uit/ContentSection.qml index 47a13e9262..d41b5ad8e6 100644 --- a/interface/resources/qml/controls-uit/ContentSection.qml +++ b/interface/resources/qml/controls-uit/ContentSection.qml @@ -1,138 +1,4 @@ -// -// ContentSection.qml -// -// Created by David Rowe on 16 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "../styles-uit" - -Column { - property string name: "Content Section" - property bool isFirst: false - property bool isCollapsible: false // Set at creation. - property bool isCollapsed: false - - spacing: 0 // Defer spacing decisions to individual controls. - - anchors { - left: parent.left - leftMargin: hifi.dimensions.contentMargin.x - right: parent.right - rightMargin: hifi.dimensions.contentMargin.x - } - - function toggleCollapsed() { - if (isCollapsible) { - isCollapsed = !isCollapsed; - for (var i = 1; i < children.length; i++) { - children[i].visible = !isCollapsed; - } - } - } - - Item { - id: sectionName - anchors.left: parent.left - anchors.right: parent.right - height: leadingSpace.height + topBar.height + heading.height + bottomBar.height - - Item { - id: leadingSpace - width: 1 - height: isFirst ? 7 : 0 - anchors.top: parent.top - } - - Item { - id: topBar - visible: !isFirst - height: visible ? 2 : 0 - anchors.top: leadingSpace.bottom - - Rectangle { - id: shadow - width: frame.width - height: 1 - color: hifi.colors.baseGrayShadow - x: -hifi.dimensions.contentMargin.x - } - - Rectangle { - width: frame.width - height: 1 - color: hifi.colors.baseGrayHighlight - x: -hifi.dimensions.contentMargin.x - anchors.top: shadow.bottom - } - } - - Item { - id: heading - anchors { - left: parent.left - right: parent.right - top: topBar.bottom - } - height: isCollapsible ? 36 : 28 - - RalewayRegular { - id: title - anchors { - left: parent.left - top: parent.top - topMargin: 12 - } - size: hifi.fontSizes.sectionName - font.capitalization: Font.AllUppercase - text: name - color: hifi.colors.lightGrayText - } - - HiFiGlyphs { - anchors { - top: title.top - topMargin: -9 - right: parent.right - rightMargin: -4 - } - size: hifi.fontSizes.disclosureButton - text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse - color: hifi.colors.lightGrayText - visible: isCollapsible - } - - MouseArea { - // Events are propogated so that any active control is defocused. - anchors.fill: parent - propagateComposedEvents: true - onPressed: { - toggleCollapsed(); - mouse.accepted = false; - } - } - } - - LinearGradient { - id: bottomBar - visible: desktop.gradientsSupported && isCollapsible - width: frame.width - height: visible ? 4 : 0 - x: -hifi.dimensions.contentMargin.x - anchors.top: heading.bottom - start: Qt.point(0, 0) - end: Qt.point(0, 4) - gradient: Gradient { - GradientStop { position: 0.0; color: hifi.colors.darkGray } - GradientStop { position: 1.0; color: hifi.colors.baseGray } // Equivalent of darkGray0 over baseGray background. - } - cached: true - } - } +ContentSection { } diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml index ecae790b22..dede6d2ded 100644 --- a/interface/resources/qml/controls-uit/FilterBar.qml +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -1,321 +1,4 @@ -// -// FilterBar.qml -// -// Created by Zach Fox on 17 Feb 2018-03-12 -// 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 -// +import controlsUit 1.0 -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtGraphicalEffects 1.0 - -import "../styles-uit" -import "../controls-uit" as HifiControls - -Item { - id: root; - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray - property bool error: false; - property alias textFieldHeight: textField.height; - property string placeholderText; - property alias dropdownHeight: dropdownContainer.height; - property alias text: textField.text; - property alias primaryFilterChoices: filterBarModel; - property int primaryFilter_index: -1; - property string primaryFilter_filterName: ""; - property string primaryFilter_displayName: ""; - signal accepted; - - onPrimaryFilter_indexChanged: { - if (primaryFilter_index === -1) { - primaryFilter_filterName = ""; - primaryFilter_displayName = ""; - } else { - primaryFilter_filterName = filterBarModel.get(primaryFilter_index).filterName; - primaryFilter_displayName = filterBarModel.get(primaryFilter_index).displayName; - } - } - - TextField { - id: textField; - - anchors.top: parent.top; - anchors.right: parent.right; - anchors.left: parent.left; - - font.family: "Fira Sans" - font.pixelSize: hifi.fontSizes.textFieldInput; - - placeholderText: root.primaryFilter_index === -1 ? root.placeholderText : ""; - - TextMetrics { - id: primaryFilterTextMetrics; - font.family: "FiraSans Regular"; - font.pixelSize: hifi.fontSizes.textFieldInput; - font.capitalization: Font.AllUppercase; - text: root.primaryFilter_displayName; - } - - // workaround for https://bugreports.qt.io/browse/QTBUG-49297 - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Return: - case Qt.Key_Enter: - event.accepted = true; - - // emit accepted signal manually - if (acceptableInput) { - root.accepted(); - root.forceActiveFocus(); - } - break; - case Qt.Key_Backspace: - if (textField.text === "") { - primaryFilter_index = -1; - } - break; - } - } - - onAccepted: { - root.forceActiveFocus(); - } - - onActiveFocusChanged: { - if (!activeFocus) { - dropdownContainer.visible = false; - } - } - - color: { - if (isLightColorScheme) { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.lightGray - } - } else if (isFaintGrayColorScheme) { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.lightGray - } - } else { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.lightGrayText - } - } - } - - background: Rectangle { - id: mainFilterBarRectangle; - - color: { - if (isLightColorScheme) { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.textFieldLightBackground - } - } else if (isFaintGrayColorScheme) { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.faintGray50 - } - } else { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.baseGrayShadow - } - } - } - - border.color: textField.error ? hifi.colors.redHighlight : - (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) - border.width: 1 - radius: 4 - - Item { - id: searchButtonContainer; - anchors.left: parent.left; - anchors.verticalCenter: parent.verticalCenter; - height: parent.height; - width: 42; - - // Search icon - HiFiGlyphs { - id: searchIcon; - text: hifi.glyphs.search - color: textField.color - size: 40; - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - width: paintedWidth; - } - - // Carat - HiFiGlyphs { - text: hifi.glyphs.caratDn; - color: textField.color; - size: 40; - anchors.left: parent.left; - anchors.leftMargin: 15; - width: paintedWidth; - } - - MouseArea { - anchors.fill: parent; - onClicked: { - textField.forceActiveFocus(); - dropdownContainer.visible = !dropdownContainer.visible; - } - } - } - - Rectangle { - z: 999; - id: primaryFilterContainer; - color: textField.activeFocus ? hifi.colors.faintGray : hifi.colors.white; - width: primaryFilterTextMetrics.tightBoundingRect.width + 14; - height: parent.height - 8; - anchors.verticalCenter: parent.verticalCenter; - anchors.left: searchButtonContainer.right; - anchors.leftMargin: 4; - visible: primaryFilterText.text !== ""; - radius: height/2; - - FiraSansRegular { - id: primaryFilterText; - text: root.primaryFilter_displayName; - anchors.fill: parent; - color: textField.activeFocus ? hifi.colors.black : hifi.colors.lightGray; - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - size: hifi.fontSizes.textFieldInput; - font.capitalization: Font.AllUppercase; - } - - MouseArea { - anchors.fill: parent; - onClicked: { - textField.forceActiveFocus(); - } - } - } - - // "Clear" button - HiFiGlyphs { - text: hifi.glyphs.error - color: textField.color - size: 40 - anchors.right: parent.right - anchors.rightMargin: hifi.dimensions.textPadding - 2 - anchors.verticalCenter: parent.verticalCenter - visible: root.text !== "" || root.primaryFilter_index !== -1; - - MouseArea { - anchors.fill: parent; - onClicked: { - root.text = ""; - root.primaryFilter_index = -1; - dropdownContainer.visible = false; - textField.forceActiveFocus(); - } - } - } - } - - selectedTextColor: hifi.colors.black - selectionColor: hifi.colors.primaryHighlight - leftPadding: 44 + (root.primaryFilter_index === -1 ? 0 : primaryFilterTextMetrics.tightBoundingRect.width + 20); - rightPadding: 44; - } - - Rectangle { - id: dropdownContainer; - visible: false; - height: 50 * filterBarModel.count; - width: parent.width; - anchors.top: textField.bottom; - anchors.left: parent.left; - anchors.right: parent.right; - color: hifi.colors.white; - - ListModel { - id: filterBarModel; - } - - ListView { - id: dropdownListView; - interactive: false; - anchors.fill: parent; - model: filterBarModel; - delegate: Rectangle { - id: dropDownButton; - color: hifi.colors.white; - width: parent.width; - height: 50; - - RalewaySemiBold { - id: dropDownButtonText; - text: model.displayName; - anchors.fill: parent; - anchors.leftMargin: 12; - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - size: 18; - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - propagateComposedEvents: false; - onEntered: { - dropDownButton.color = hifi.colors.blueHighlight; - } - onExited: { - dropDownButton.color = hifi.colors.white; - } - onClicked: { - textField.forceActiveFocus(); - root.primaryFilter_index = index; - dropdownContainer.visible = false; - } - } - } - } - } - - DropShadow { - anchors.fill: dropdownContainer; - horizontalOffset: 0; - verticalOffset: 4; - radius: 4.0; - samples: 9 - color: Qt.rgba(0, 0, 0, 0.25); - source: dropdownContainer; - visible: dropdownContainer.visible; - } - - function changeFilterByDisplayName(name) { - for (var i = 0; i < filterBarModel.count; i++) { - if (filterBarModel.get(i).displayName === name) { - root.primaryFilter_index = i; - return; - } - } - - console.log("Passed displayName not found in filterBarModel! primaryFilter unchanged."); - } +FilterBar { } diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index 9129486720..3a0f8631a8 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -1,91 +1,4 @@ -// -// GlyphButton.qml -// -// Created by Clement on 3/7/16 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.7 -import QtQuick.Controls 2.2 as Original -import TabletScriptingInterface 1.0 - -import "../styles-uit" - -Original.Button { - id: control - property int color: 0 - property int colorScheme: hifi.colorSchemes.light - property string glyph: "" - property int size: 32 - - width: 120 - height: 28 - - onHoveredChanged: { - if (hovered) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onFocusChanged: { - if (focus) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - } - - background: Rectangle { - radius: hifi.buttons.radius - - gradient: Gradient { - GradientStop { - position: 0.2 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorStart[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else if (!control.hovered && control.focus) { - hifi.buttons.focusedColor[control.color] - } else { - hifi.buttons.colorStart[control.color] - } - } - } - GradientStop { - position: 1.0 - color: { - if (!control.enabled) { - hifi.buttons.disabledColorFinish[control.colorScheme] - } else if (control.pressed) { - hifi.buttons.pressedColor[control.color] - } else if (control.hovered) { - hifi.buttons.hoveredColor[control.color] - } else if (!control.hovered && control.focus) { - hifi.buttons.focusedColor[control.color] - } else { - hifi.buttons.colorFinish[control.color] - } - } - } - } - } - - contentItem: HiFiGlyphs { - color: enabled ? hifi.buttons.textColor[control.color] - : hifi.buttons.disabledTextColor[control.colorScheme] - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.glyph - size: control.size - } +GlyphButton { } - diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml index 0609cc451d..7fc2269649 100644 --- a/interface/resources/qml/controls-uit/HorizontalRule.qml +++ b/interface/resources/qml/controls-uit/HorizontalRule.qml @@ -1,18 +1,4 @@ -// -// HorizontalRule.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 - -Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: 1 - color: hifi.colors.lightGray +HorizontalRule { } diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml index 545154ab44..b4f372545c 100644 --- a/interface/resources/qml/controls-uit/HorizontalSpacer.qml +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -1,21 +1,4 @@ -// -// HorizontalSpacer.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 - -import "../styles-uit" - -Item { - id: root - property alias size: root.width - - width: hifi.dimensions.controlInterlineHeight - height: 1 // Must be non-zero +HorizontalSpacer { } diff --git a/interface/resources/qml/controls-uit/ImageMessageBox.qml b/interface/resources/qml/controls-uit/ImageMessageBox.qml index 74313f7ffe..484a17dd7c 100644 --- a/interface/resources/qml/controls-uit/ImageMessageBox.qml +++ b/interface/resources/qml/controls-uit/ImageMessageBox.qml @@ -1,63 +1,4 @@ -// -// ImageMessageBox.qml -// -// Created by Dante Ruiz on 7/5/2017 -// Copyright 2017 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 -// - -import QtQuick 2.5 -import "../styles-uit" - -Item { - id: imageBox - visible: false - anchors.fill: parent - property alias source: image.source - property alias imageWidth: image.width - property alias imageHeight: image.height - - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.3 - } - - Image { - id: image - anchors.centerIn: parent - - HiFiGlyphs { - id: closeGlyphButton - text: hifi.glyphs.close - size: 25 - - anchors { - top: parent.top - topMargin: 15 - right: parent.right - rightMargin: 15 - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - - onEntered: { - parent.text = hifi.glyphs.closeInverted; - } - - onExited: { - parent.text = hifi.glyphs.close; - } - - onClicked: { - imageBox.visible = false; - } - } - } - } +import controlsUit 1.0 +ImageMessageBox { } diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index dd77fc92dc..c031c2f660 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -1,185 +1,4 @@ -import QtQuick 2.0 -import TabletScriptingInterface 1.0 +import controlsUit 1.0 -Item { - id: keyItem - width: 45 - height: 50 - - property int contentPadding: 4 - property string glyph: "a" - property bool toggle: false // does this button have the toggle behaivor? - property bool toggled: false // is this button currently toggled? - property alias mouseArea: mouseArea1 - property alias fontFamily: letter.font.family; - property alias fontPixelSize: letter.font.pixelSize - property alias verticalAlignment: letter.verticalAlignment - property alias letterAnchors: letter.anchors - - function resetToggledMode(mode) { - toggled = mode; - if (toggled) { - state = "mouseDepressed"; - } else { - state = ""; - } - } - - MouseArea { - id: mouseArea1 - width: 36 - anchors.fill: parent - hoverEnabled: true - - onCanceled: { - if (toggled) { - keyItem.state = "mouseDepressed"; - } else { - keyItem.state = ""; - } - } - - onContainsMouseChanged: { - if (containsMouse) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - onDoubleClicked: { - mouse.accepted = true; - } - - property var _HAPTIC_STRENGTH: 0.1; - property var _HAPTIC_DURATION: 3.0; - property var leftHand: 0; - property var rightHand: 1; - - onEntered: { - keyItem.state = "mouseOver"; - - var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY); - var pointerID = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y); - - if (Pointers.isLeftHand(pointerID)) { - Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, leftHand); - } else if (Pointers.isRightHand(pointerID)) { - Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, rightHand); - } - } - - onExited: { - if (toggled) { - keyItem.state = "mouseDepressed"; - } else { - keyItem.state = ""; - } - } - - onPressed: { - keyItem.state = "mouseClicked"; - mouse.accepted = true; - } - - onReleased: { - if (containsMouse) { - Tablet.playSound(TabletEnums.ButtonClick); - - webEntity.synthesizeKeyPress(glyph); - webEntity.synthesizeKeyPress(glyph, mirrorText); - - if (toggle) { - toggled = !toggled; - } - keyItem.state = "mouseOver"; - } else { - if (toggled) { - keyItem.state = "mouseDepressed"; - } else { - keyItem.state = ""; - } - } - mouse.accepted = true; - } - } - - Rectangle { - id: roundedRect - width: 30 - color: "#121212" - radius: 2 - border.color: "#00000000" - anchors.fill: parent - anchors.margins: contentPadding - } - - Text { - id: letter - y: 6 - width: 50 - color: "#ffffff" - text: glyph - style: Text.Normal - font.family: "Tahoma" - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.topMargin: 8 - horizontalAlignment: Text.AlignHCenter - font.pixelSize: 28 - } - - states: [ - State { - name: "mouseOver" - - PropertyChanges { - target: roundedRect - color: "#121212" - radius: 3 - border.width: 2 - border.color: "#00b4ef" - } - - PropertyChanges { - target: letter - color: "#00b4ef" - style: Text.Normal - } - }, - State { - name: "mouseClicked" - PropertyChanges { - target: roundedRect - color: "#1080b8" - border.width: 2 - border.color: "#00b4ef" - } - - PropertyChanges { - target: letter - color: "#121212" - styleColor: "#00000000" - style: Text.Normal - } - }, - State { - name: "mouseDepressed" - PropertyChanges { - target: roundedRect - color: "#0578b1" - border.width: 0 - } - - PropertyChanges { - target: letter - color: "#121212" - styleColor: "#00000000" - style: Text.Normal - } - } - ] +Key { } diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 9d4fd33022..2bdf682b9a 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -1,354 +1,4 @@ -// -// FileDialog.qml -// -// Created by Anthony Thibault on 31 Oct 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.7 -import QtGraphicalEffects 1.0 -import "." - -Rectangle { - id: keyboardBase - objectName: "keyboard" - - anchors.left: parent.left - anchors.right: parent.right - - color: "#252525" - - property bool raised: false - property bool numeric: false - - readonly property int keyboardRowHeight: 50 - readonly property int keyboardWidth: 480 - readonly property int keyboardHeight: 200 - - readonly property int mirrorTextHeight: keyboardRowHeight - - property bool password: false - property alias mirroredText: mirrorText.text - property bool showMirrorText: true - - readonly property int raisedHeight: keyboardHeight + (showMirrorText ? keyboardRowHeight : 0) - - height: enabled && raised ? raisedHeight : 0 - visible: enabled && raised - - property bool shiftMode: false - property bool numericShiftMode: false - - onRaisedChanged: { - mirroredText = ""; - } - - function resetShiftMode(mode) { - shiftMode = mode; - shiftKey.resetToggledMode(mode); - } - - function toUpper(str) { - if (str === ",") { - return "<"; - } else if (str === ".") { - return ">"; - } else if (str === "/") { - return "?"; - } else if (str === "-") { - return "_"; - } else { - return str.toUpperCase(str); - } - } - - function toLower(str) { - if (str === "<") { - return ","; - } else if (str === ">") { - return "."; - } else if (str === "?") { - return "/"; - } else if (str === "_") { - return "-"; - } else { - return str.toLowerCase(str); - } - } - - function forEachKey(func) { - var i, j; - for (i = 0; i < columnAlpha.children.length; i++) { - var row = columnAlpha.children[i]; - for (j = 0; j < row.children.length; j++) { - var key = row.children[j]; - func(key); - } - } - } - - onShiftModeChanged: { - forEachKey(function (key) { - if (/[a-z-_]/i.test(key.glyph)) { - if (shiftMode) { - key.glyph = keyboardBase.toUpper(key.glyph); - } else { - key.glyph = keyboardBase.toLower(key.glyph); - } - } - }); - } - - function alphaKeyClickedHandler(mouseArea) { - // reset shift mode to false after first keypress - if (shiftMode) { - resetShiftMode(false); - } - } - - Component.onCompleted: { - // hook up callbacks to every ascii key - forEachKey(function (key) { - if (/^[a-z]+$/i.test(key.glyph)) { - key.mouseArea.onClicked.connect(alphaKeyClickedHandler); - } - }); - } - - Rectangle { - height: showMirrorText ? mirrorTextHeight : 0 - width: keyboardWidth - color: "#252525" - anchors.horizontalCenter: parent.horizontalCenter - - TextInput { - id: mirrorText - visible: showMirrorText - font.family: "Fira Sans" - font.pixelSize: 20 - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - color: "#00B4EF"; - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - - wrapMode: Text.WordWrap - readOnly: false // we need this to allow control to accept QKeyEvent - selectByMouse: false - echoMode: password ? TextInput.Password : TextInput.Normal - - Keys.onPressed: { - if (event.key == Qt.Key_Return || event.key == Qt.Key_Space) { - mirrorText.text = ""; - event.accepted = true; - } - } - - MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus - anchors.fill: parent - } - } - } - - Rectangle { - id: keyboardRect - y: showMirrorText ? mirrorTextHeight : 0 - width: keyboardWidth - height: keyboardHeight - color: "#252525" - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - - Column { - id: columnAlpha - width: keyboardWidth - height: keyboardHeight - visible: !numeric - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { width: 43; glyph: "q"; } - Key { width: 43; glyph: "w"; } - Key { width: 43; glyph: "e"; } - Key { width: 43; glyph: "r"; } - Key { width: 43; glyph: "t"; } - Key { width: 43; glyph: "y"; } - Key { width: 43; glyph: "u"; } - Key { width: 43; glyph: "i"; } - Key { width: 43; glyph: "o"; } - Key { width: 43; glyph: "p"; } - Key { width: 43; glyph: "←"; } - } - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 20 - - Key { width: 43; glyph: "a"; } - Key { width: 43; glyph: "s"; } - Key { width: 43; glyph: "d"; } - Key { width: 43; glyph: "f"; } - Key { width: 43; glyph: "g"; } - Key { width: 43; glyph: "h"; } - Key { width: 43; glyph: "j"; } - Key { width: 43; glyph: "k"; } - Key { width: 43; glyph: "l"; } - Key { width: 70; glyph: "⏎"; } - } - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { - id: shiftKey - width: 43 - glyph: "⇪" - toggle: true - onToggledChanged: shiftMode = toggled - } - Key { width: 43; glyph: "z"; } - Key { width: 43; glyph: "x"; } - Key { width: 43; glyph: "c"; } - Key { width: 43; glyph: "v"; } - Key { width: 43; glyph: "b"; } - Key { width: 43; glyph: "n"; } - Key { width: 43; glyph: "m"; } - Key { width: 43; glyph: "-"; } - Key { width: 43; glyph: "/"; } - Key { width: 43; glyph: "?"; } - } - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { - width: 70 - glyph: "123" - mouseArea.onClicked: keyboardBase.parent.punctuationMode = true - } - Key { width: 231; glyph: " "; } - Key { width: 43; glyph: ","; } - Key { width: 43; glyph: "."; } - Key { - fontFamily: "hifi-glyphs"; - fontPixelSize: 48; - letterAnchors.topMargin: -4; - verticalAlignment: Text.AlignVCenter; - width: 86; glyph: "\ue02b"; - } - } - } - - Column { - id: columnNumeric - width: keyboardWidth - height: keyboardHeight - visible: numeric - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { width: 43; glyph: "1"; } - Key { width: 43; glyph: "2"; } - Key { width: 43; glyph: "3"; } - Key { width: 43; glyph: "4"; } - Key { width: 43; glyph: "5"; } - Key { width: 43; glyph: "6"; } - Key { width: 43; glyph: "7"; } - Key { width: 43; glyph: "8"; } - Key { width: 43; glyph: "9"; } - Key { width: 43; glyph: "0"; } - Key { width: 43; glyph: "←"; } - } - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { width: 43; glyph: "!"; } - Key { width: 43; glyph: "@"; } - Key { width: 43; glyph: "#"; } - Key { width: 43; glyph: "$"; } - Key { width: 43; glyph: "%"; } - Key { width: 43; glyph: "^"; } - Key { width: 43; glyph: "&"; } - Key { width: 43; glyph: "*"; } - Key { width: 43; glyph: "("; } - Key { width: 43; glyph: ")"; } - Key { width: 43; glyph: "⏎"; } - } - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { - id: numericShiftKey - width: 43 - glyph: "\u21E8" - toggle: true - onToggledChanged: numericShiftMode = toggled - } - Key { width: 43; glyph: numericShiftMode ? "`" : "+"; } - Key { width: 43; glyph: numericShiftMode ? "~" : "-"; } - Key { width: 43; glyph: numericShiftMode ? "\u00A3" : "="; } - Key { width: 43; glyph: numericShiftMode ? "\u20AC" : ";"; } - Key { width: 43; glyph: numericShiftMode ? "\u00A5" : ":"; } - Key { width: 43; glyph: numericShiftMode ? "<" : "'"; } - Key { width: 43; glyph: numericShiftMode ? ">" : "\""; } - Key { width: 43; glyph: numericShiftMode ? "[" : "{"; } - Key { width: 43; glyph: numericShiftMode ? "]" : "}"; } - Key { width: 43; glyph: numericShiftMode ? "\\" : "|"; } - } - - Row { - width: keyboardWidth - height: keyboardRowHeight - anchors.left: parent.left - anchors.leftMargin: 4 - - Key { - width: 70 - glyph: "abc" - mouseArea.onClicked: keyboardBase.parent.punctuationMode = false - } - Key { width: 231; glyph: " "; } - Key { width: 43; glyph: ","; } - Key { width: 43; glyph: "."; } - Key { - fontFamily: "hifi-glyphs"; - fontPixelSize: 48; - letterAnchors.topMargin: -4; - verticalAlignment: Text.AlignVCenter; - width: 86; glyph: "\ue02b"; - } - } - } - } +Keyboard { } diff --git a/interface/resources/qml/controls-uit/Label.qml b/interface/resources/qml/controls-uit/Label.qml index 4c7051b495..032367208b 100644 --- a/interface/resources/qml/controls-uit/Label.qml +++ b/interface/resources/qml/controls-uit/Label.qml @@ -1,35 +1,4 @@ -// -// Label.qml -// -// Created by David Rowe on 26 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.7 - -import "../styles-uit" - -RalewaySemiBold { - HifiConstants { id: hifi } - property int colorScheme: hifi.colorSchemes.light - - size: hifi.fontSizes.inputLabel - color: { - if (colorScheme === hifi.colorSchemes.dark) { - if (enabled) { - hifi.colors.lightGrayText - } else { - hifi.colors.baseGrayHighlight - } - } else { - if (enabled) { - hifi.colors.lightGray - } else { - hifi.colors.lightGrayText - } - } - } +Label { } diff --git a/interface/resources/qml/controls-uit/QueuedButton.qml b/interface/resources/qml/controls-uit/QueuedButton.qml index 6612d582df..54d366a663 100644 --- a/interface/resources/qml/controls-uit/QueuedButton.qml +++ b/interface/resources/qml/controls-uit/QueuedButton.qml @@ -1,41 +1,4 @@ -// -// QueuedButton.qml -// -- original Button.qml + signal timer workaround --ht -// Created by David Rowe on 16 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 - -import "../styles-uit" -import "." as HifiControls - -HifiControls.Button { - // FIXME: THIS WORKAROUND MIGRATED/CONSOLIDATED FROM RUNNINGSCRIPTS.QML - - // For some reason trigginer an API that enters - // an internal event loop directly from the button clicked - // trigger below causes the appliction to behave oddly. - // Most likely because the button onClicked handling is never - // completed until the function returns. - // FIXME find a better way of handling the input dialogs that - // doesn't trigger this. - - // NOTE: dialogs that need to use this workaround can connect via - // onQueuedClicked: ... - // instead of: - // onClicked: ... - - signal clickedQueued() - Timer { - id: fromTimer - interval: 5 - repeat: false - running: false - onTriggered: clickedQueued() - } - onClicked: fromTimer.running = true +QueuedButton { } diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index 56324c55d7..46eb76e7ce 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -1,93 +1,4 @@ -// -// RadioButton.qml -// -// Created by Cain Kilgore on 20th July 2017 -// Copyright 2017 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtQuick.Controls 2.2 as Original - -import "../styles-uit" -import "../controls-uit" as HifiControls - -import TabletScriptingInterface 1.0 - -Original.RadioButton { - id: radioButton - HifiConstants { id: hifi } - - hoverEnabled: true - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - - property real letterSpacing: 1 - property int fontSize: hifi.fontSizes.inputLabel - property int boxSize: defaultBoxSize - property real scaleFactor: boxSize / defaultBoxSize - - readonly property int defaultBoxSize: 14 - readonly property int boxRadius: 3 * scaleFactor - readonly property int checkSize: 10 * scaleFactor - readonly property int checkRadius: 2 * scaleFactor - readonly property int indicatorRadius: 7 * scaleFactor - - onClicked: { - Tablet.playSound(TabletEnums.ButtonClick); - } - - onHoveredChanged: { - if (hovered) { - Tablet.playSound(TabletEnums.ButtonHover); - } - } - - indicator: Rectangle { - id: box - width: boxSize - height: boxSize - radius: indicatorRadius - x: radioButton.leftPadding - y: parent.height / 2 - height / 2 - gradient: Gradient { - GradientStop { - position: 0.2 - color: pressed || hovered - ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart) - : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish) - : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) - } - } - - Rectangle { - id: check - width: checkSize - height: checkSize - radius: indicatorRadius - anchors.centerIn: parent - color: "#00B4EF" - border.width: 1 - border.color: "#36CDFF" - visible: checked && !pressed || !checked && pressed - } - } - - contentItem: RalewaySemiBold { - text: radioButton.text - size: radioButton.fontSize - font.letterSpacing: letterSpacing - color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - leftPadding: radioButton.indicator.width + radioButton.spacing - } +RadioButton { } diff --git a/interface/resources/qml/controls-uit/ScrollBar.qml b/interface/resources/qml/controls-uit/ScrollBar.qml index 125e84e585..1b21509819 100644 --- a/interface/resources/qml/controls-uit/ScrollBar.qml +++ b/interface/resources/qml/controls-uit/ScrollBar.qml @@ -1,41 +1,4 @@ -// -// ScrollBar.qml -// -// Created by Vlad Stelmahovsky on 27 Nov 2017 -// Copyright 2016 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 -// - -import QtQuick 2.7 -import QtQuick.Controls 2.2 - -import "../styles-uit" +import controlsUit 1.0 ScrollBar { - visible: size < 1.0 - - HifiConstants { id: hifi } - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - - background: Item { - implicitWidth: hifi.dimensions.scrollbarBackgroundWidth - Rectangle { - anchors { fill: parent; topMargin: 3; bottomMargin: 3 } - radius: hifi.dimensions.scrollbarHandleWidth/2 - color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight - : hifi.colors.tableScrollBackgroundDark - } - } - - contentItem: Item { - implicitWidth: hifi.dimensions.scrollbarHandleWidth - Rectangle { - anchors { fill: parent; topMargin: 1; bottomMargin: 1 } - radius: hifi.dimensions.scrollbarHandleWidth/2 - color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark - } - } } diff --git a/interface/resources/qml/controls-uit/Separator.qml b/interface/resources/qml/controls-uit/Separator.qml index 3350764ae9..baeefc7895 100644 --- a/interface/resources/qml/controls-uit/Separator.qml +++ b/interface/resources/qml/controls-uit/Separator.qml @@ -1,44 +1,4 @@ -// -// Separator.qml -// -// Created by Zach Fox on 2017-06-06 -// Copyright 2017 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import "../styles-uit" - -Item { - property int colorScheme: 0; - - readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray, "#89858C" ]; - readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray, "#89858C" ]; - - // Size - height: colorScheme === 0 ? 2 : 1; - Rectangle { - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.bottom: parent.bottom; - // Style - color: topColor[colorScheme]; - } - Rectangle { - visible: colorScheme === 0; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.bottom: parent.bottom; - anchors.bottomMargin: -height; - // Style - color: bottomColor[colorScheme]; - } +Separator { } diff --git a/interface/resources/qml/controls-uit/Slider.qml b/interface/resources/qml/controls-uit/Slider.qml index 2a5d4c137d..1bcdbc39b7 100644 --- a/interface/resources/qml/controls-uit/Slider.qml +++ b/interface/resources/qml/controls-uit/Slider.qml @@ -1,98 +1,4 @@ -// -// Slider.qml -// -// Created by David Rowe on 27 Feb 2016 -// Copyright 2016 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 -// - -import QtQuick 2.7 -import QtQuick.Controls 2.2 - -import "../styles-uit" -import "../controls-uit" as HifiControls +import controlsUit 1.0 Slider { - id: slider - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - property string label: "" - property real controlHeight: height + (sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0) - - property alias minimumValue: slider.from - property alias maximumValue: slider.to - property alias step: slider.stepSize - property bool tickmarksEnabled: false - - height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control. - y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0 - - background: Rectangle { - x: slider.leftPadding - y: slider.topPadding + slider.availableHeight / 2 - height / 2 - - implicitWidth: 50 - implicitHeight: hifi.dimensions.sliderGrooveHeight - width: slider.availableWidth - height: implicitHeight - radius: height / 2 - color: isLightColorScheme ? hifi.colors.sliderGutterLight : hifi.colors.sliderGutterDark - - Rectangle { - width: slider.visualPosition * parent.width - height: parent.height - radius: height / 2 - gradient: Gradient { - GradientStop { position: 0.0; color: hifi.colors.blueAccent } - GradientStop { position: 1.0; color: hifi.colors.primaryHighlight } - } - } - } - - handle: Rectangle { - x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) - y: slider.topPadding + slider.availableHeight / 2 - height / 2 - implicitWidth: hifi.dimensions.sliderHandleSize - implicitHeight: hifi.dimensions.sliderHandleSize - radius: height / 2 - border.width: 1 - border.color: isLightColorScheme ? hifi.colors.sliderBorderLight : hifi.colors.sliderBorderDark - gradient: Gradient { - GradientStop { - position: 0.0 - color: pressed || hovered - ? (isLightColorScheme ? hifi.colors.sliderDarkStart : hifi.colors.sliderLightStart ) - : (isLightColorScheme ? hifi.colors.sliderLightStart : hifi.colors.sliderDarkStart ) - } - GradientStop { - position: 1.0 - color: pressed || hovered - ? (isLightColorScheme ? hifi.colors.sliderDarkFinish : hifi.colors.sliderLightFinish ) - : (isLightColorScheme ? hifi.colors.sliderLightFinish : hifi.colors.sliderDarkFinish ) - } - } - - Rectangle { - height: parent.height - 2 - width: height - radius: height / 2 - anchors.centerIn: parent - color: hifi.colors.transparent - border.width: 1 - border.color: hifi.colors.black - } - } - - HifiControls.Label { - id: sliderLabel - text: slider.label - colorScheme: slider.colorScheme - anchors.left: parent.left - anchors.bottom: parent.top - anchors.bottomMargin: 2 - visible: label != "" - } -} +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index 3d3ea7a75e..c202a3997c 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -1,185 +1,4 @@ -// -// SpinBox.qml -// -// Created by David Rowe on 26 Feb 2016 -// Copyright 2016 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 -// - -import QtQuick 2.7 -import QtQuick.Controls 2.2 - -import "../styles-uit" -import "../controls-uit" as HifiControls +import controlsUit 1.0 SpinBox { - id: spinBox - - HifiConstants { - id: hifi - } - - inputMethodHints: Qt.ImhFormattedNumbersOnly - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light - property string label: "" - property string suffix: "" - property string labelInside: "" - property color colorLabelInside: hifi.colors.white - property color backgroundColor: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray) - : (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow) - property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0) - property int decimals: 2; - property real factor: Math.pow(10, decimals) - - property real minimumValue: 0.0 - property real maximumValue: 0.0 - - property real realValue: 0.0 - property real realFrom: minimumValue - property real realTo: maximumValue - property real realStepSize: 1.0 - - signal editingFinished() - - implicitHeight: height - implicitWidth: width - editable: true - - padding: 0 - leftPadding: 0 - rightPadding: padding + (up.indicator ? up.indicator.width : 0) - topPadding: 0 - bottomPadding: 0 - - locale: Qt.locale("en_US") - - onValueModified: realValue = value/factor - onValueChanged: realValue = value/factor - onRealValueChanged: { - var newValue = Math.round(realValue*factor); - if(value != newValue) { - value = newValue; - } - } - - stepSize: realStepSize*factor - to : realTo*factor - from : realFrom*factor - - font.family: "Fira Sans SemiBold" - font.pixelSize: hifi.fontSizes.textFieldInput - height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. - - y: spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0 - - background: Rectangle { - color: backgroundColor - border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight - border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0 - } - - validator: DoubleValidator { - bottom: Math.min(spinBox.from, spinBox.to) - top: Math.max(spinBox.from, spinBox.to) - } - - textFromValue: function(value, locale) { - return parseFloat(value/factor).toFixed(decimals); - } - - valueFromText: function(text, locale) { - return Number.fromLocaleString(locale, text)*factor; - } - - - contentItem: TextInput { - z: 2 - color: isLightColorScheme - ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) - : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) - selectedTextColor: hifi.colors.black - selectionColor: hifi.colors.primaryHighlight - text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix - inputMethodHints: spinBox.inputMethodHints - validator: spinBox.validator - verticalAlignment: Qt.AlignVCenter - leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding - //rightPadding: hifi.dimensions.spinnerSize - width: spinBox.width - hifi.dimensions.spinnerSize - onEditingFinished: spinBox.editingFinished() - } - - up.indicator: Item { - x: spinBox.width - implicitWidth - 5 - y: 1 - clip: true - implicitHeight: spinBox.implicitHeight/2 - implicitWidth: spinBox.implicitHeight/2 - HiFiGlyphs { - anchors.centerIn: parent - text: hifi.glyphs.caratUp - size: hifi.dimensions.spinnerSize - color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray - } - } - up.onPressedChanged: { - if(value) { - spinBox.forceActiveFocus(); - } - } - - down.indicator: Item { - x: spinBox.width - implicitWidth - 5 - y: spinBox.implicitHeight/2 - clip: true - implicitHeight: spinBox.implicitHeight/2 - implicitWidth: spinBox.implicitHeight/2 - HiFiGlyphs { - anchors.centerIn: parent - text: hifi.glyphs.caratDn - size: hifi.dimensions.spinnerSize - color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray - } - } - down.onPressedChanged: { - if(value) { - spinBox.forceActiveFocus(); - } - } - - HifiControls.Label { - id: spinBoxLabel - text: spinBox.label - colorScheme: spinBox.colorScheme - anchors.left: parent.left - anchors.bottom: parent.top - anchors.bottomMargin: 4 - visible: label != "" - } - - HifiControls.Label { - id: spinBoxLabelInside - text: spinBox.labelInside - anchors.left: parent.left - anchors.leftMargin: 10 - font.bold: true - anchors.verticalCenter: parent.verticalCenter - color: spinBox.colorLabelInside - visible: spinBox.labelInside != "" - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton - onWheel: { - if (wheel.angleDelta.y > 0) - value += stepSize - else - value -= stepSize - } - } } diff --git a/interface/resources/qml/controls-uit/Switch.qml b/interface/resources/qml/controls-uit/Switch.qml index bfe86b1420..96ff7f8898 100644 --- a/interface/resources/qml/controls-uit/Switch.qml +++ b/interface/resources/qml/controls-uit/Switch.qml @@ -1,160 +1,4 @@ -// -// Switch.qml -// -// Created by Zach Fox on 2017-06-06 -// Copyright 2017 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 -// +import controlsUit 1.0 -import QtQuick 2.7 -import QtQuick.Controls 2.2 as Original - -import "../styles-uit" - -Item { - id: rootSwitch; - - property int colorScheme: hifi.colorSchemes.light; - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light; - property int switchWidth: 70; - readonly property int switchRadius: height/2; - property string labelTextOff: ""; - property string labelGlyphOffText: ""; - property int labelGlyphOffSize: 32; - property string labelTextOn: ""; - property string labelGlyphOnText: ""; - property int labelGlyphOnSize: 32; - property alias checked: originalSwitch.checked; - signal onCheckedChanged; - signal clicked; - - Original.Switch { - id: originalSwitch; - focusPolicy: Qt.ClickFocus - anchors.top: rootSwitch.top; - anchors.left: rootSwitch.left; - anchors.leftMargin: rootSwitch.width/2 - rootSwitch.switchWidth/2; - onCheckedChanged: rootSwitch.onCheckedChanged(); - onClicked: rootSwitch.clicked(); - hoverEnabled: true - - topPadding: 3; - leftPadding: 3; - rightPadding: 3; - bottomPadding: 3; - - onHoveredChanged: { - if (hovered) { - switchHandle.color = hifi.colors.blueHighlight; - } else { - switchHandle.color = hifi.colors.lightGray; - } - } - - background: Rectangle { - color: "#252525"; - implicitWidth: rootSwitch.switchWidth; - implicitHeight: rootSwitch.height; - radius: rootSwitch.switchRadius; - } - - indicator: Rectangle { - id: switchHandle; - implicitWidth: rootSwitch.height - originalSwitch.topPadding - originalSwitch.bottomPadding; - implicitHeight: implicitWidth; - radius: implicitWidth/2; - border.color: hifi.colors.lightGrayText; - color: hifi.colors.lightGray; - //x: originalSwitch.leftPadding - x: Math.max(0, Math.min(parent.width - width, originalSwitch.visualPosition * parent.width - (width / 2))) - y: parent.height / 2 - height / 2 - Behavior on x { - enabled: !originalSwitch.down - SmoothedAnimation { velocity: 200 } - } - - } - } - - // OFF Label - Item { - anchors.right: originalSwitch.left; - anchors.rightMargin: 10; - anchors.top: rootSwitch.top; - height: rootSwitch.height; - - RalewaySemiBold { - id: labelOff; - text: labelTextOff; - size: hifi.fontSizes.inputLabel; - color: originalSwitch.checked ? hifi.colors.lightGrayText : "#FFFFFF"; - anchors.top: parent.top; - anchors.right: parent.right; - width: paintedWidth; - height: parent.height; - verticalAlignment: Text.AlignVCenter; - } - - HiFiGlyphs { - id: labelGlyphOff; - text: labelGlyphOffText; - size: labelGlyphOffSize; - color: labelOff.color; - anchors.top: parent.top; - anchors.topMargin: 2; - anchors.right: labelOff.left; - anchors.rightMargin: 4; - } - - MouseArea { - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: labelGlyphOff.left; - anchors.right: labelOff.right; - onClicked: { - originalSwitch.checked = false; - } - } - } - - // ON Label - Item { - anchors.left: originalSwitch.right; - anchors.leftMargin: 10; - anchors.top: rootSwitch.top; - height: rootSwitch.height; - - RalewaySemiBold { - id: labelOn; - text: labelTextOn; - size: hifi.fontSizes.inputLabel; - color: originalSwitch.checked ? "#FFFFFF" : hifi.colors.lightGrayText; - anchors.top: parent.top; - anchors.left: parent.left; - width: paintedWidth; - height: parent.height; - verticalAlignment: Text.AlignVCenter; - } - - HiFiGlyphs { - id: labelGlyphOn; - text: labelGlyphOnText; - size: labelGlyphOnSize; - color: labelOn.color; - anchors.top: parent.top; - anchors.left: labelOn.right; - } - - MouseArea { - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: labelOn.left; - anchors.right: labelGlyphOn.right; - onClicked: { - originalSwitch.checked = true; - } - } - } +Switch { } diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index ce4e1c376a..3a4932302f 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -1,165 +1,4 @@ -// -// Table.qml -// -// Created by David Rowe on 18 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Controls 2.3 as QQC2 - -import "../styles-uit" - -TableView { - id: tableView - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - property bool expandSelectedRow: false - property bool centerHeaderText: false - readonly property real headerSpacing: 3 //spacing between sort indicator and table header title - property var titlePaintedPos: [] // storing extra data position behind painted - // title text and sort indicatorin table's header - signal titlePaintedPosSignal(int column) //signal that extradata position gets changed - - model: ListModel { } - - Component.onCompleted: { - if (flickableItem !== null && flickableItem !== undefined) { - tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar - } - } - - QQC2.ScrollBar { - id: scrollbar - parent: tableView.flickableItem - policy: QQC2.ScrollBar.AsNeeded - orientation: Qt.Vertical - visible: size < 1.0 - topPadding: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1 - anchors.top: tableView.top - anchors.left: tableView.right - anchors.bottom: tableView.bottom - - background: Item { - implicitWidth: hifi.dimensions.scrollbarBackgroundWidth - Rectangle { - anchors { - fill: parent; - topMargin: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight : 0 - } - color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight - : hifi.colors.tableScrollBackgroundDark - } - } - - contentItem: Item { - implicitWidth: hifi.dimensions.scrollbarHandleWidth - Rectangle { - anchors.fill: parent - radius: (width - 4)/2 - color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark - } - } - } - - headerVisible: false - headerDelegate: Rectangle { - height: hifi.dimensions.tableHeaderHeight - color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - - - RalewayRegular { - id: titleText - x: centerHeaderText ? (parent.width - paintedWidth - - ((sortIndicatorVisible && - sortIndicatorColumn === styleData.column) ? - (titleSort.paintedWidth / 5 + tableView.headerSpacing) : 0)) / 2 : - hifi.dimensions.tablePadding - text: styleData.value - size: hifi.fontSizes.tableHeading - font.capitalization: Font.AllUppercase - color: hifi.colors.baseGrayHighlight - horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) - anchors.verticalCenter: parent.verticalCenter - } - - //actual image of sort indicator in glyph font only 20% of real font size - //i.e. if the charachter size set to 60 pixels, actual image is 12 pixels - HiFiGlyphs { - id: titleSort - text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn - color: hifi.colors.darkGray - opacity: 0.6; - size: hifi.fontSizes.tableHeadingIcon - anchors.verticalCenter: titleText.verticalCenter - anchors.left: titleText.right - anchors.leftMargin: -(hifi.fontSizes.tableHeadingIcon / 2.5) + tableView.headerSpacing - visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column - onXChanged: { - titlePaintedPos[styleData.column] = titleText.x + titleText.paintedWidth + - paintedWidth / 5 + tableView.headerSpacing*2 - titlePaintedPosSignal(styleData.column) - } - } - - Rectangle { - width: 1 - anchors { - left: parent.left - top: parent.top - topMargin: 1 - bottom: parent.bottom - bottomMargin: 2 - } - color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - visible: styleData.column > 0 - } - - Rectangle { - height: 1 - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - } - } - - // Use rectangle to draw border with rounded corners. - frameVisible: false - Rectangle { - color: "#00000000" - anchors { fill: parent; margins: -2 } - border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - border.width: 2 - } - anchors.margins: 2 // Shrink TableView to lie within border. - - backgroundVisible: true - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - style: TableViewStyle { - // Needed in order for rows to keep displaying rows after end of table entries. - backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd - padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 - } - - rowDelegate: Rectangle { - height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight - color: styleData.selected - ? hifi.colors.primaryHighlight - : tableView.isLightColorScheme - ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) - : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) - } +Table { } diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controls-uit/TabletContentSection.qml index c34f4afdd6..70075d3858 100644 --- a/interface/resources/qml/controls-uit/TabletContentSection.qml +++ b/interface/resources/qml/controls-uit/TabletContentSection.qml @@ -1,138 +1,4 @@ -// -// ContentSection.qml -// -// Created by Dante Ruiz on 13 Feb 2017 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtGraphicalEffects 1.0 - -import "../styles-uit" - -Column { - property string name: "Content Section" - property bool isFirst: false - property bool isCollapsible: false // Set at creation. - property bool isCollapsed: false - - spacing: 0 // Defer spacing decisions to individual controls. - - anchors { - left: parent.left - leftMargin: hifi.dimensions.contentMargin.x - right: parent.right - rightMargin: hifi.dimensions.contentMargin.x - } - - function toggleCollapsed() { - if (isCollapsible) { - isCollapsed = !isCollapsed; - for (var i = 1; i < children.length; i++) { - children[i].visible = !isCollapsed; - } - } - } - - Item { - id: sectionName - anchors.left: parent.left - anchors.right: parent.right - height: leadingSpace.height + topBar.height + heading.height + bottomBar.height - - Item { - id: leadingSpace - width: 1 - height: isFirst ? 7 : 0 - anchors.top: parent.top - } - - Item { - id: topBar - visible: !isFirst - height: visible ? 2 : 0 - anchors.top: leadingSpace.bottom - - Rectangle { - id: shadow - width: 480 - height: 1 - color: hifi.colors.baseGrayShadow - x: -hifi.dimensions.contentMargin.x - } - - Rectangle { - width: 480 - height: 1 - color: hifi.colors.baseGrayHighlight - x: -hifi.dimensions.contentMargin.x - anchors.top: shadow.bottom - } - } - - Item { - id: heading - anchors { - left: parent.left - right: parent.right - top: topBar.bottom - } - height: isCollapsible ? 36 : 28 - - RalewayRegular { - id: title - anchors { - left: parent.left - top: parent.top - topMargin: 12 - } - size: hifi.fontSizes.sectionName - font.capitalization: Font.AllUppercase - text: name - color: hifi.colors.lightGrayText - } - - HiFiGlyphs { - anchors { - top: title.top - topMargin: -9 - right: parent.right - rightMargin: -4 - } - size: hifi.fontSizes.disclosureButton - text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse - color: hifi.colors.lightGrayText - visible: isCollapsible - } - - MouseArea { - // Events are propogated so that any active control is defocused. - anchors.fill: parent - propagateComposedEvents: true - onPressed: { - toggleCollapsed(); - mouse.accepted = false; - } - } - } - - LinearGradient { - id: bottomBar - visible: false - width: 480 - height: visible ? 4 : 0 - x: -hifi.dimensions.contentMargin.x - anchors.top: heading.bottom - start: Qt.point(0, 0) - end: Qt.point(0, 4) - gradient: Gradient { - GradientStop { position: 0.0; color: hifi.colors.darkGray } - GradientStop { position: 1.0; color: hifi.colors.baseGray } // Equivalent of darkGray0 over baseGray background. - } - cached: true - } - } +TabletContentSection { } diff --git a/interface/resources/qml/controls-uit/TabletHeader.qml b/interface/resources/qml/controls-uit/TabletHeader.qml index 56203de286..53e660d708 100644 --- a/interface/resources/qml/controls-uit/TabletHeader.qml +++ b/interface/resources/qml/controls-uit/TabletHeader.qml @@ -1,34 +1,4 @@ -// -// TabletHeader.qml -// -// Created by David Rowe on 11 Mar 2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// +import controlsUit 1.0 -import QtQuick 2.5 - -import "../styles-uit" - -Rectangle { - - property string title: "" - - HifiConstants { id: hifi } - - height: hifi.dimensions.tabletMenuHeader - z: 100 - - color: hifi.colors.darkGray - - RalewayBold { - text: title - size: 26 - color: hifi.colors.white - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: hifi.dimensions.contentMargin.x - } +TabletHeader { } diff --git a/interface/resources/qml/controls-uit/TextAction.qml b/interface/resources/qml/controls-uit/TextAction.qml index 1745a6c273..ce5a973776 100644 --- a/interface/resources/qml/controls-uit/TextAction.qml +++ b/interface/resources/qml/controls-uit/TextAction.qml @@ -1,65 +1,4 @@ -// -// TextField.qml -// -// Created by David Rowe on 21 Apr 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 - -import "../styles-uit" -import "../controls-uit" as HifiControls - -Item { - property string icon: "" - property int iconSize: 30 - property string text: "" - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - - signal clicked() - - height: Math.max(glyph.visible ? glyph.height - 4 : 0, string.visible ? string.height : 0) - width: glyph.width + string.anchors.leftMargin + string.width - - HiFiGlyphs { - id: glyph - anchors.left: parent.left - anchors.top: parent.top - anchors.topMargin: -2 - text: parent.icon - size: parent.iconSize - color: isLightColorScheme - ? (mouseArea.containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.lightGray) - : (mouseArea.containsMouse ? hifi.colors.faintGray : hifi.colors.lightGrayText) - visible: text !== "" - width: visible ? implicitWidth : 0 - } - - RalewaySemiBold { - id: string - anchors { - left: glyph.visible ? glyph.right : parent.left - leftMargin: visible && glyph.visible ? hifi.dimensions.contentSpacing.x : 0 - verticalCenter: glyph.visible ? glyph.verticalCenter : undefined - } - text: parent.text - size: hifi.fontSizes.inputLabel - color: isLightColorScheme - ? (mouseArea.containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.lightGray) - : (mouseArea.containsMouse ? hifi.colors.faintGray : hifi.colors.lightGrayText) - font.underline: true; - visible: text !== "" - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: parent.clicked() - } +TextAction { } diff --git a/interface/resources/qml/controls-uit/TextEdit.qml b/interface/resources/qml/controls-uit/TextEdit.qml index a72a3b13d8..783950bd1e 100644 --- a/interface/resources/qml/controls-uit/TextEdit.qml +++ b/interface/resources/qml/controls-uit/TextEdit.qml @@ -1,23 +1,4 @@ -// -// TextEdit.qml -// -// Created by Bradley Austin Davis on 24 Apr 2015 -// Copyright 2015 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 -// - -import QtQuick 2.5 -import "../styles-uit" +import controlsUit 1.0 TextEdit { - - property real size: 32 - - font.family: "Raleway" - font.weight: Font.DemiBold - font.pointSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft } diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index 917068ac01..261a4162ef 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -1,180 +1,4 @@ -// -// TextField.qml -// -// Created by David Rowe on 17 Feb 2016 -// Copyright 2016 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 -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import "../styles-uit" -import "../controls-uit" as HifiControls +import controlsUit 1.0 TextField { - id: textField - - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray - property bool isSearchField: false - property string label: "" - property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0) - property bool hasDefocusedBorder: true; - property bool hasRoundedBorder: false - property int roundedBorderRadius: 4 - property bool error: false; - property bool hasClearButton: false; - property string leftPermanentGlyph: ""; - property string centerPlaceholderGlyph: ""; - - placeholderText: textField.placeholderText - - font.family: "Fira Sans" - font.pixelSize: hifi.fontSizes.textFieldInput - height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered. - property alias textFieldLabel: textFieldLabel - - y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0 - - // workaround for https://bugreports.qt.io/browse/QTBUG-49297 - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Return: - case Qt.Key_Enter: - event.accepted = true; - - // emit accepted signal manually - if (acceptableInput) { - accepted(); - } - } - } - - style: TextFieldStyle { - id: style; - textColor: { - if (isLightColorScheme) { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.lightGray - } - } else if (isFaintGrayColorScheme) { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.lightGray - } - } else { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.lightGrayText - } - } - } - background: Rectangle { - color: { - if (isLightColorScheme) { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.textFieldLightBackground - } - } else if (isFaintGrayColorScheme) { - if (textField.activeFocus) { - hifi.colors.white - } else { - hifi.colors.faintGray50 - } - } else { - if (textField.activeFocus) { - hifi.colors.black - } else { - hifi.colors.baseGrayShadow - } - } - } - border.color: textField.error ? hifi.colors.redHighlight : - (textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color)) - border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 - radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0) - - HiFiGlyphs { - text: textField.leftPermanentGlyph; - color: textColor; - size: hifi.fontSizes.textFieldSearchIcon; - anchors.left: parent.left; - anchors.verticalCenter: parent.verticalCenter; - anchors.leftMargin: hifi.dimensions.textPadding - 2; - visible: text; - } - - HiFiGlyphs { - text: textField.centerPlaceholderGlyph; - color: textColor; - size: parent.height; - anchors.horizontalCenter: parent.horizontalCenter; - anchors.verticalCenter: parent.verticalCenter; - visible: text && !textField.focus && textField.text === ""; - } - - HiFiGlyphs { - text: hifi.glyphs.search - color: textColor - size: hifi.fontSizes.textFieldSearchIcon - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: hifi.dimensions.textPadding - 2 - visible: isSearchField - } - - HiFiGlyphs { - text: hifi.glyphs.error - color: textColor - size: 40 - anchors.right: parent.right - anchors.rightMargin: hifi.dimensions.textPadding - 2 - anchors.verticalCenter: parent.verticalCenter - visible: hasClearButton && textField.text !== ""; - - MouseArea { - anchors.fill: parent; - onClicked: { - textField.text = ""; - } - } - } - } - placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray - selectedTextColor: hifi.colors.black - selectionColor: hifi.colors.primaryHighlight - padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding - padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding - } - - HifiControls.Label { - id: textFieldLabel - text: textField.label - colorScheme: textField.colorScheme - anchors.left: parent.left - - Binding on anchors.right { - when: textField.right - value: textField.right - } - Binding on wrapMode { - when: textField.right - value: Text.WordWrap - } - - anchors.bottom: parent.top - anchors.bottomMargin: 3 - visible: label != "" - } } diff --git a/interface/resources/qml/controls-uit/ToolTip.qml b/interface/resources/qml/controls-uit/ToolTip.qml index 4fe36adcd5..a5f24c7974 100644 --- a/interface/resources/qml/controls-uit/ToolTip.qml +++ b/interface/resources/qml/controls-uit/ToolTip.qml @@ -1,51 +1,4 @@ -// -// ToolTip.qml -// -// Created by Clement on 9/12/17 -// Copyright 2017 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 -// +import controlsUit 1.0 -import QtQuick 2.5 - -Item { - property string toolTip - property bool showToolTip: false - - Rectangle { - id: toolTipRectangle - anchors.right: parent.right - - width: toolTipText.width + 4 - height: toolTipText.height + 4 - opacity: (toolTip != "" && showToolTip) ? 1 : 0 - color: "#ffffaa" - border.color: "#0a0a0a" - Text { - id: toolTipText - text: toolTip - color: "black" - anchors.centerIn: parent - } - Behavior on opacity { - PropertyAnimation { - easing.type: Easing.InOutQuad - duration: 250 - } - } - } - MouseArea { - id: mouseArea - anchors.fill: parent - onEntered: showTimer.start() - onExited: { showToolTip = false; showTimer.stop(); } - hoverEnabled: true - } - Timer { - id: showTimer - interval: 250 - onTriggered: { showToolTip = true; } - } -} \ No newline at end of file +ToolTip { +} diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 5199a10a27..a21cdc6d47 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -1,205 +1,4 @@ -// -// Tree.qml -// -// Created by David Rowe on 17 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQml.Models 2.2 -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Controls 2.2 as QQC2 - - -import "../styles-uit" - -TreeView { - id: treeView - - property var treeModel: ListModel { } - property bool centerHeaderText: false - property int colorScheme: hifi.colorSchemes.light - readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - - property var modifyEl: function(index, data) { return false; } - - model: treeModel - selection: ItemSelectionModel { - id: selectionModel - model: treeModel - } - - anchors { left: parent.left; right: parent.right } - - headerVisible: false - - Component.onCompleted: { - if (flickableItem !== null && flickableItem !== undefined) { - treeView.flickableItem.QQC2.ScrollBar.vertical = scrollbar - } - } - - QQC2.ScrollBar { - id: scrollbar - parent: treeView.flickableItem - policy: QQC2.ScrollBar.AsNeeded - orientation: Qt.Vertical - visible: size < 1.0 - topPadding: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1 - anchors.top: treeView.top - anchors.left: treeView.right - anchors.bottom: treeView.bottom - - background: Item { - implicitWidth: hifi.dimensions.scrollbarBackgroundWidth - Rectangle { - anchors { - fill: parent; - topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight: 0 - } - color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight - : hifi.colors.tableScrollBackgroundDark - } - } - - contentItem: Item { - implicitWidth: hifi.dimensions.scrollbarHandleWidth - Rectangle { - anchors.fill: parent - radius: (width - 4)/2 - color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark - } - } - } - - // Use rectangle to draw border with rounded corners. - frameVisible: false - Rectangle { - color: "#00000000" - anchors.fill: parent - radius: hifi.dimensions.borderRadius - border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - border.width: 2 - anchors.margins: -2 - } - anchors.margins: 2 // Shrink TreeView to lie within border. - - backgroundVisible: true - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - style: TreeViewStyle { - // Needed in order for rows to keep displaying rows after end of table entries. - backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven - alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd - - headerDelegate: Rectangle { - height: hifi.dimensions.tableHeaderHeight - color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - - RalewayRegular { - id: titleText - text: styleData.value - size: hifi.fontSizes.tableHeading - font.capitalization: Font.AllUppercase - color: hifi.colors.baseGrayHighlight - horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) - elide: Text.ElideRight - anchors { - left: parent.left - leftMargin: hifi.dimensions.tablePadding - right: sortIndicatorVisible && sortIndicatorColumn === styleData.column ? titleSort.left : parent.right - rightMargin: hifi.dimensions.tablePadding - verticalCenter: parent.verticalCenter - } - } - - HiFiGlyphs { - id: titleSort - text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn - color: isLightColorScheme ? hifi.colors.darkGray : hifi.colors.baseGrayHighlight - opacity: 0.6; - size: hifi.fontSizes.tableHeadingIcon - anchors { - right: parent.right - verticalCenter: titleText.verticalCenter - } - visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column - } - - Rectangle { - width: 1 - anchors { - left: parent.left - top: parent.top - topMargin: 1 - bottom: parent.bottom - bottomMargin: 2 - } - color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - visible: styleData.column > 0 - } - - Rectangle { - height: 1 - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight - } - } - - branchDelegate: HiFiGlyphs { - text: styleData.isExpanded ? hifi.glyphs.caratDn : hifi.glyphs.caratR - size: hifi.fontSizes.carat - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - anchors { - left: parent ? parent.left : undefined - leftMargin: hifi.dimensions.tablePadding / 2 - } - } - } - - rowDelegate: Rectangle { - height: hifi.dimensions.tableRowHeight - color: styleData.selected - ? hifi.colors.primaryHighlight - : treeView.isLightColorScheme - ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) - : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) - } - - itemDelegate: FiraSansSemiBold { - anchors { - left: parent ? parent.left : undefined - leftMargin: (2 + styleData.depth) * hifi.dimensions.tablePadding - right: parent ? parent.right : undefined - rightMargin: hifi.dimensions.tablePadding - verticalCenter: parent ? parent.verticalCenter : undefined - } - - text: styleData.value - size: hifi.fontSizes.tableText - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - - elide: Text.ElideRight - } - - Item { - id: unfocusHelper - visible: false - } - - onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index) +Tree { } diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml index 2df65f1002..42b48b003e 100644 --- a/interface/resources/qml/controls-uit/VerticalSpacer.qml +++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml @@ -1,21 +1,4 @@ -// -// VerticalSpacer.qml -// -// Created by David Rowe on 16 Feb 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 - -import "../styles-uit" - -Item { - id: root - property alias size: root.height - - width: 1 // Must be non-zero - height: hifi.dimensions.controlInterlineHeight +VerticalSpacer { } diff --git a/interface/resources/qml/controls-uit/WebGlyphButton.qml b/interface/resources/qml/controls-uit/WebGlyphButton.qml index fd7cd001b2..1377b937a2 100644 --- a/interface/resources/qml/controls-uit/WebGlyphButton.qml +++ b/interface/resources/qml/controls-uit/WebGlyphButton.qml @@ -1,40 +1,4 @@ -// -// GlyphButton.qml -// -// Created by Vlad Stelmahovsky on 2017-06-21 -// Copyright 2017 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtQuick.Controls 2.2 as Original - -import "../styles-uit" - -Original.Button { - id: control - - property int colorScheme: hifi.colorSchemes.light - property string glyph: "" - property int size: 32 - //colors - readonly property color normalColor: "#AFAFAF" - readonly property color hoverColor: "#00B4EF" - readonly property color clickedColor: "#FFFFFF" - readonly property color disabledColor: "#575757" - - background: Item {} - - contentItem: HiFiGlyphs { - color: control.enabled ? (control.pressed ? control.clickedColor : - (control.hovered ? control.hoverColor : control.normalColor)) : - control.disabledColor - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.glyph - size: control.size - } +WebGlyphButton { } - diff --git a/interface/resources/qml/controls-uit/WebSpinner.qml b/interface/resources/qml/controls-uit/WebSpinner.qml index e8e01c4865..a20597d8dc 100644 --- a/interface/resources/qml/controls-uit/WebSpinner.qml +++ b/interface/resources/qml/controls-uit/WebSpinner.qml @@ -1,24 +1,4 @@ -// -// WebSpinner.qml -// -// Created by David Rowe on 23 May 2017 -// Copyright 2017 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import QtWebEngine 1.5 - -AnimatedImage { - property WebEngineView webview: parent - source: "../../icons/loader-snake-64-w.gif" - visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString()) - playing: visible - z: 10000 - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } +WebSpinner { } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 2895f36944..b11dee7f3b 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -1,21 +1,4 @@ -// -// WebView.qml -// -// Created by Bradley Austin Davis on 12 Jan 2016 -// Copyright 2016 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 -// +import controlsUit 1.0 -import QtQuick 2.5 -import "." - -BaseWebView { - onNewViewRequested: { - // Load dialog via OffscreenUi so that JavaScript EventBridge is available. - var browser = OffscreenUi.load("Browser.qml"); - request.openIn(browser.webView); - browser.webView.forceActiveFocus(); - } +WebView { } diff --git a/interface/resources/qml/controls-uit/readme.txt b/interface/resources/qml/controls-uit/readme.txt new file mode 100644 index 0000000000..8aa3714ff9 --- /dev/null +++ b/interface/resources/qml/controls-uit/readme.txt @@ -0,0 +1 @@ +this folder exists purely for compatibility reasons and might be deleted in future! please consider using 'import controlsUit 1.0' instead of including this folder \ No newline at end of file diff --git a/interface/resources/qml/controls/Button.qml b/interface/resources/qml/controls/Button.qml index 6cbdec5644..b677822c0e 100644 --- a/interface/resources/qml/controls/Button.qml +++ b/interface/resources/qml/controls/Button.qml @@ -3,7 +3,6 @@ import QtQuick.Controls 2.2 as Original import "." import "../styles" -import "../controls-uit" Original.Button { id: control diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 943f15e1de..cce32c137a 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -4,7 +4,7 @@ import QtWebChannel 1.0 import QtQuick.Controls 2.2 -import "../styles-uit" as StylesUIt +import stylesUit 1.0 as StylesUIt Item { id: flick diff --git a/interface/resources/qml/controls/TabletWebButton.qml b/interface/resources/qml/controls/TabletWebButton.qml index d016f71f2d..140461d817 100644 --- a/interface/resources/qml/controls/TabletWebButton.qml +++ b/interface/resources/qml/controls/TabletWebButton.qml @@ -10,7 +10,7 @@ import Hifi 1.0 import QtQuick 2.4 -import "../styles-uit" +import stylesUit 1.0 Rectangle { property alias text: label.text diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index bb037ad478..be11f16498 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -1,5 +1,5 @@ import QtQuick 2.7 -import "../controls-uit" as HiFiControls +import controlsUit 1.0 as HiFiControls Item { id: root diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index db695dbfb2..94f4c7978c 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -1,8 +1,8 @@ import QtQuick 2.7 import QtWebEngine 1.5 -import "../controls-uit" as HiFiControls +import controlsUit 1.0 as HiFiControls import "../styles" as HifiStyles -import "../styles-uit" +import stylesUit 1.0 Item { id: root @@ -195,6 +195,10 @@ Item { keyboardEnabled = HMD.active; } + Component.onDestruction: { + keyboardRaised = false; + } + Keys.onPressed: { switch(event.key) { case Qt.Key_L: diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 71bf69fdc8..375bcd50e0 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -1,5 +1,5 @@ import QtQuick 2.7 -import "../controls-uit" as HiFiControls +import controlsUit 1.0 as HiFiControls Item { width: parent !== null ? parent.width : undefined diff --git a/interface/resources/qml/controls-uit/+android/ImageButton.qml b/interface/resources/qml/controlsUit/+android/ImageButton.qml similarity index 96% rename from interface/resources/qml/controls-uit/+android/ImageButton.qml rename to interface/resources/qml/controlsUit/+android/ImageButton.qml index 5ebf7cd3e9..88eaf95d76 100644 --- a/interface/resources/qml/controls-uit/+android/ImageButton.qml +++ b/interface/resources/qml/controlsUit/+android/ImageButton.qml @@ -1,6 +1,6 @@ // // ImageButton.qml -// interface/resources/qml/controls-uit +// interface/resources/qml/controlsUit // // Created by Gabriel Calero & Cristian Duarte on 12 Oct 2017 // Copyright 2017 High Fidelity, Inc. @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtQuick.Layouts 1.3 -import "../styles-uit" as HifiStyles +import "../stylesUit" as HifiStyles Item { id: button @@ -79,4 +79,4 @@ Item { } } ] -} \ No newline at end of file +} diff --git a/interface/resources/qml/controlsUit/AttachmentsTable.qml b/interface/resources/qml/controlsUit/AttachmentsTable.qml new file mode 100644 index 0000000000..a2677962da --- /dev/null +++ b/interface/resources/qml/controlsUit/AttachmentsTable.qml @@ -0,0 +1,170 @@ +// +// AttachmentsTable.qml +// +// Created by David Rowe on 18 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.XmlListModel 2.0 + +import "../stylesUit" +import "." as HifiControls +import "../windows" +import "../hifi/models" + +TableView { + id: tableView + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + model: S3Model{} + + Rectangle { + anchors.fill: parent + visible: tableView.model.status !== XmlListModel.Ready + color: hifi.colors.darkGray0 + BusyIndicator { + anchors.centerIn: parent + width: 48; height: 48 + running: true + } + } + + headerDelegate: Rectangle { + height: hifi.dimensions.tableHeaderHeight + color: hifi.colors.darkGray + border.width: 0.5 + border.color: hifi.colors.baseGrayHighlight + + RalewayRegular { + id: textHeader + size: hifi.fontSizes.tableHeading + color: hifi.colors.lightGrayText + text: styleData.value + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + } + } + + // Use rectangle to draw border with rounded corners. + Rectangle { + color: "#00000000" + anchors { fill: parent; margins: -2 } + radius: hifi.dimensions.borderRadius + border.color: hifi.colors.baseGrayHighlight + border.width: 3 + } + anchors.margins: 2 // Shrink TableView to lie within border. + backgroundVisible: true + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + + style: TableViewStyle { + // Needed in order for rows to keep displaying rows after end of table entries. + backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven + alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + handle: Item { + id: scrollbarHandle + implicitWidth: 6 + Rectangle { + anchors { + fill: parent + leftMargin: 2 // Move it right + rightMargin: -2 // "" + topMargin: 3 // Shrink vertically + bottomMargin: 3 // "" + } + radius: 3 + color: hifi.colors.tableScrollHandleDark + } + } + + scrollBarBackground: Item { + implicitWidth: 10 + Rectangle { + anchors { + fill: parent + margins: -1 // Expand + } + color: hifi.colors.baseGrayHighlight + } + + Rectangle { + anchors { + fill: parent + margins: 1 // Shrink + } + radius: 4 + color: hifi.colors.tableScrollBackgroundDark + } + } + + incrementControl: Item { + visible: false + } + + decrementControl: Item { + visible: false + } + } + + rowDelegate: Rectangle { + height: hifi.dimensions.tableRowHeight + color: styleData.selected + ? hifi.colors.primaryHighlight + : tableView.isLightColorScheme + ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) + : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) + } + + itemDelegate: Item { + anchors { + left: parent ? parent.left : undefined + leftMargin: hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + } + FiraSansSemiBold { + id: textItem + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + } + } + + TableViewColumn { + role: "name" + title: "NAME" + width: parent.width *0.3 + horizontalAlignment: Text.AlignHCenter + } + TableViewColumn { + role: "size" + title: "SIZE" + width: parent.width *0.2 + horizontalAlignment: Text.AlignHCenter + } + TableViewColumn { + role: "modified" + title: "LAST MODIFIED" + width: parent.width *0.5 + horizontalAlignment: Text.AlignHCenter + } +} diff --git a/interface/resources/qml/controlsUit/BaseWebView.qml b/interface/resources/qml/controlsUit/BaseWebView.qml new file mode 100644 index 0000000000..fdd9c12220 --- /dev/null +++ b/interface/resources/qml/controlsUit/BaseWebView.qml @@ -0,0 +1,38 @@ +// +// WebView.qml +// +// Created by Bradley Austin Davis on 12 Jan 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtWebEngine 1.5 + +WebEngineView { + id: root + + Component.onCompleted: { + console.log("Connecting JS messaging to Hifi Logging") + // Ensure the JS from the web-engine makes it to our logging + root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); + }); + } + + onLoadingChanged: { + // Required to support clicking on "hifi://" links + if (WebEngineView.LoadStartedStatus == loadRequest.status) { + var url = loadRequest.url.toString(); + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + root.stop(); + } + } + } + } + + WebSpinner { } +} diff --git a/interface/resources/qml/controlsUit/Button.qml b/interface/resources/qml/controlsUit/Button.qml new file mode 100644 index 0000000000..6ea7ce4b4c --- /dev/null +++ b/interface/resources/qml/controlsUit/Button.qml @@ -0,0 +1,122 @@ +// +// Button.qml +// +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.3 as Original +import TabletScriptingInterface 1.0 + +import "../stylesUit" + +Original.Button { + id: control; + + property int color: 0 + property int colorScheme: hifi.colorSchemes.light + property int fontSize: hifi.fontSizes.buttonLabel + property int radius: hifi.buttons.radius + property alias implicitTextWidth: buttonText.implicitWidth + property string buttonGlyph: ""; + property int fontCapitalization: Font.AllUppercase + + width: hifi.dimensions.buttonWidth + height: hifi.dimensions.controlLineHeight + + HifiConstants { id: hifi } + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + } + + background: Rectangle { + radius: control.radius + + border.width: (control.color === hifi.buttons.none || + (control.color === hifi.buttons.noneBorderless && control.hovered) || + (control.color === hifi.buttons.noneBorderlessWhite && control.hovered) || + (control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0; + border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight : + (control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white); + + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorStart[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorStart[control.color] + } + } + } + GradientStop { + position: 1.0 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorFinish[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorFinish[control.color] + } + } + } + } + } + + contentItem: Item { + HiFiGlyphs { + id: buttonGlyph; + visible: control.buttonGlyph !== ""; + text: control.buttonGlyph === "" ? hifi.glyphs.question : control.buttonGlyph; + // Size + size: 34; + // Anchors + anchors.right: buttonText.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + // Style + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme]; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + RalewayBold { + id: buttonText; + anchors.centerIn: parent; + font.capitalization: control.fontCapitalization + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + size: control.fontSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.text + } + } +} + diff --git a/interface/resources/qml/controlsUit/CheckBox.qml b/interface/resources/qml/controlsUit/CheckBox.qml new file mode 100644 index 0000000000..abf08908fb --- /dev/null +++ b/interface/resources/qml/controlsUit/CheckBox.qml @@ -0,0 +1,121 @@ +// +// CheckBox.qml +// +// Created by David Rowe on 26 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.2 +import QtQuick.Controls 2.2 as Original + +import "../stylesUit" + +import TabletScriptingInterface 1.0 + +Original.CheckBox { + id: checkBox + + property int colorScheme: hifi.colorSchemes.light + property string color: hifi.colors.lightGrayText + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property bool isRedCheck: false + property int boxSize: 14 + property int boxRadius: 3 + property bool wrap: true; + readonly property int checkSize: Math.max(boxSize - 8, 10) + readonly property int checkRadius: 2 + property string labelFontFamily: "Raleway" + property int labelFontSize: 14; + property int labelFontWeight: Font.DemiBold; + focusPolicy: Qt.ClickFocus + hoverEnabled: true + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + } + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + + indicator: Rectangle { + id: box + implicitWidth: boxSize + implicitHeight: boxSize + radius: boxRadius + y: parent.height / 2 - height / 2 + border.width: 1 + border.color: pressed || hovered + ? hifi.colors.checkboxCheckedBorder + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + visible: pressed || hovered + anchors.centerIn: parent + id: innerBox + width: checkSize - 4 + height: width + radius: checkRadius + color: hifi.colors.checkboxCheckedBorder + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: checkRadius + anchors.centerIn: parent + color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked + border.width: 2 + border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder + visible: checked && !pressed || !checked && pressed + } + + Rectangle { + id: disabledOverlay + visible: !enabled + width: boxSize + height: boxSize + radius: boxRadius + border.width: 1 + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 + } + } + + contentItem: Label { + text: checkBox.text + color: checkBox.color + font.family: checkBox.labelFontFamily; + font.pixelSize: checkBox.labelFontSize; + font.weight: checkBox.labelFontWeight; + x: 2 + verticalAlignment: Text.AlignVCenter + wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap + elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight + enabled: checkBox.enabled + leftPadding: checkBox.indicator.width + checkBox.spacing + } +} diff --git a/interface/resources/qml/controlsUit/CheckBoxQQC2.qml b/interface/resources/qml/controlsUit/CheckBoxQQC2.qml new file mode 100644 index 0000000000..91d35ecd58 --- /dev/null +++ b/interface/resources/qml/controlsUit/CheckBoxQQC2.qml @@ -0,0 +1,125 @@ +// +// CheckBox2.qml +// +// Created by Vlad Stelmahovsky on 10 Aug 2017 +// Copyright 2017 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../stylesUit" +import "." as HiFiControls +import TabletScriptingInterface 1.0 + +CheckBox { + id: checkBox + + HifiConstants { id: hifi; } + + padding: 0 + leftPadding: 0 + property int colorScheme: hifi.colorSchemes.light + property string color: hifi.colors.lightGrayText + readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light + property bool isRedCheck: false + property bool isRound: false + property int boxSize: 14 + property int boxRadius: isRound ? boxSize : 3 + property bool wrap: true; + readonly property int checkSize: Math.max(boxSize - 8, 10) + readonly property int checkRadius: isRound ? checkSize / 2 : 2 + focusPolicy: Qt.ClickFocus + hoverEnabled: true + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + } + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + indicator: Rectangle { + id: box + implicitWidth: boxSize + implicitHeight: boxSize + radius: boxRadius + x: checkBox.leftPadding + y: parent.height / 2 - height / 2 + border.width: 1 + border.color: pressed || hovered + ? hifi.colors.checkboxCheckedBorder + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + visible: pressed || hovered + anchors.centerIn: parent + id: innerBox + width: checkSize - 4 + height: width + radius: checkRadius + color: hifi.colors.checkboxCheckedBorder + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: checkRadius + anchors.centerIn: parent + color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked + border.width: 2 + border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder + visible: checked && !pressed || !checked && pressed + } + + Rectangle { + id: disabledOverlay + visible: !enabled + width: boxSize + height: boxSize + radius: boxRadius + border.width: 1 + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 + } + } + + contentItem: Text { + id: root + font.pixelSize: hifi.fontSizes.inputLabel + font.family: "Raleway" + font.weight: Font.DemiBold + text: checkBox.text + color: checkBox.color + x: 2 + wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap + elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight + enabled: checkBox.enabled + verticalAlignment: Text.AlignVCenter + leftPadding: checkBox.indicator.width + checkBox.spacing + } +} + diff --git a/interface/resources/qml/controlsUit/ComboBox.qml b/interface/resources/qml/controlsUit/ComboBox.qml new file mode 100644 index 0000000000..8d1d7a5262 --- /dev/null +++ b/interface/resources/qml/controlsUit/ComboBox.qml @@ -0,0 +1,191 @@ +// +// ComboBox.qml +// +// Created by Bradley Austin David on 27 Jan 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../stylesUit" +import "." as HifiControls + +FocusScope { + id: root + HifiConstants { id: hifi } + + property alias model: comboBox.model; + property alias editable: comboBox.editable + property alias comboBox: comboBox + readonly property alias currentText: comboBox.currentText; + property alias currentIndex: comboBox.currentIndex; + property int currentHighLightedIndex: comboBox.currentIndex; + + property int dropdownHeight: 480 + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property string label: "" + property real controlHeight: height + (comboBoxLabel.visible ? comboBoxLabel.height + comboBoxLabel.anchors.bottomMargin : 0) + + readonly property ComboBox control: comboBox + + property bool isDesktop: true + + signal accepted(); + + implicitHeight: comboBox.height; + focus: true + + ComboBox { + id: comboBox + anchors.fill: parent + hoverEnabled: true + visible: true + height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. + + function previousItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count - 1) % comboBox.count; } + function nextItem() { root.currentHighLightedIndex = (root.currentHighLightedIndex + comboBox.count + 1) % comboBox.count; } + function selectCurrentItem() { root.currentIndex = root.currentHighLightedIndex; close(); /*hideList();*/ } + function selectSpecificItem(index) { root.currentIndex = index; close();/*hideList();*/ } + + Keys.onUpPressed: previousItem(); + Keys.onDownPressed: nextItem(); + Keys.onSpacePressed: selectCurrentItem(); + Keys.onRightPressed: selectCurrentItem(); + Keys.onReturnPressed: selectCurrentItem(); + + background: Rectangle { + gradient: Gradient { + GradientStop { + position: 0.2 + color: comboBox.popup.visible + ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + : (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart) + } + GradientStop { + position: 1.0 + color: comboBox.popup.visible + ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + : (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish) + } + } + } + + indicator: Item { + id: dropIcon + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + height: root.height + width: height + Rectangle { + width: 1 + height: parent.height + anchors.top: parent.top + anchors.left: parent.left + color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray + } + HiFiGlyphs { + anchors { top: parent.top; topMargin: -11; horizontalCenter: parent.horizontalCenter } + size: hifi.dimensions.spinnerSize + text: hifi.glyphs.caratDn + color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText) + } + } + + contentItem: FiraSansSemiBold { + id: textField + anchors { + left: parent.left + leftMargin: hifi.dimensions.textPadding + verticalCenter: parent.verticalCenter + } + size: hifi.fontSizes.textFieldInput + text: comboBox.displayText ? comboBox.displayText : comboBox.currentText + elide: Text.ElideRight + color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) + } + + delegate: ItemDelegate { + id: itemDelegate + hoverEnabled: true + width: root.width + 4 + height: popupText.implicitHeight * 1.4 + highlighted: root.currentHighLightedIndex == index + + onHoveredChanged: { + if (hovered) { + root.currentHighLightedIndex = index + } + } + + background: Rectangle { + color: itemDelegate.highlighted ? hifi.colors.primaryHighlight + : (isLightColorScheme ? hifi.colors.dropDownPressedLight + : hifi.colors.dropDownPressedDark) + } + + contentItem: FiraSansSemiBold { + id: popupText + anchors.left: parent.left + anchors.leftMargin: hifi.dimensions.textPadding + anchors.verticalCenter: parent.verticalCenter + text: comboBox.model[index] ? comboBox.model[index] + : (comboBox.model.get && comboBox.model.get(index).text ? + comboBox.model.get(index).text : "") + size: hifi.fontSizes.textFieldInput + color: hifi.colors.baseGray + } + } + popup: Popup { + y: comboBox.height - 1 + width: comboBox.width + implicitHeight: listView.contentHeight > dropdownHeight ? dropdownHeight + : listView.contentHeight + padding: 0 + topPadding: 1 + + onClosed: { + root.accepted() + } + + contentItem: ListView { + id: listView + clip: true + model: comboBox.popup.visible ? comboBox.delegateModel : null + currentIndex: root.currentHighLightedIndex + delegate: comboBox.delegate + ScrollBar.vertical: HifiControls.ScrollBar { + id: scrollbar + parent: listView + policy: ScrollBar.AsNeeded + visible: size < 1.0 + } + } + + background: Rectangle { + color: hifi.colors.baseGray + } + } + } + + function textAt(index) { + return comboBox.textAt(index); + } + + HifiControls.Label { + id: comboBoxLabel + text: root.label + colorScheme: root.colorScheme + anchors.left: parent.left + anchors.bottom: parent.top + anchors.bottomMargin: 4 + visible: label != "" + } + + Component.onCompleted: { + isDesktop = (typeof desktop !== "undefined"); + } +} diff --git a/interface/resources/qml/controlsUit/ContentSection.qml b/interface/resources/qml/controlsUit/ContentSection.qml new file mode 100644 index 0000000000..262c29220f --- /dev/null +++ b/interface/resources/qml/controlsUit/ContentSection.qml @@ -0,0 +1,138 @@ +// +// ContentSection.qml +// +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import QtGraphicalEffects 1.0 + +import "../stylesUit" + +Column { + property string name: "Content Section" + property bool isFirst: false + property bool isCollapsible: false // Set at creation. + property bool isCollapsed: false + + spacing: 0 // Defer spacing decisions to individual controls. + + anchors { + left: parent.left + leftMargin: hifi.dimensions.contentMargin.x + right: parent.right + rightMargin: hifi.dimensions.contentMargin.x + } + + function toggleCollapsed() { + if (isCollapsible) { + isCollapsed = !isCollapsed; + for (var i = 1; i < children.length; i++) { + children[i].visible = !isCollapsed; + } + } + } + + Item { + id: sectionName + anchors.left: parent.left + anchors.right: parent.right + height: leadingSpace.height + topBar.height + heading.height + bottomBar.height + + Item { + id: leadingSpace + width: 1 + height: isFirst ? 7 : 0 + anchors.top: parent.top + } + + Item { + id: topBar + visible: !isFirst + height: visible ? 2 : 0 + anchors.top: leadingSpace.bottom + + Rectangle { + id: shadow + width: frame.width + height: 1 + color: hifi.colors.baseGrayShadow + x: -hifi.dimensions.contentMargin.x + } + + Rectangle { + width: frame.width + height: 1 + color: hifi.colors.baseGrayHighlight + x: -hifi.dimensions.contentMargin.x + anchors.top: shadow.bottom + } + } + + Item { + id: heading + anchors { + left: parent.left + right: parent.right + top: topBar.bottom + } + height: isCollapsible ? 36 : 28 + + RalewayRegular { + id: title + anchors { + left: parent.left + top: parent.top + topMargin: 12 + } + size: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase + text: name + color: hifi.colors.lightGrayText + } + + HiFiGlyphs { + anchors { + top: title.top + topMargin: -9 + right: parent.right + rightMargin: -4 + } + size: hifi.fontSizes.disclosureButton + text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse + color: hifi.colors.lightGrayText + visible: isCollapsible + } + + MouseArea { + // Events are propogated so that any active control is defocused. + anchors.fill: parent + propagateComposedEvents: true + onPressed: { + toggleCollapsed(); + mouse.accepted = false; + } + } + } + + LinearGradient { + id: bottomBar + visible: desktop.gradientsSupported && isCollapsible + width: frame.width + height: visible ? 4 : 0 + x: -hifi.dimensions.contentMargin.x + anchors.top: heading.bottom + start: Qt.point(0, 0) + end: Qt.point(0, 4) + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.darkGray } + GradientStop { position: 1.0; color: hifi.colors.baseGray } // Equivalent of darkGray0 over baseGray background. + } + cached: true + } + } +} diff --git a/interface/resources/qml/controlsUit/FilterBar.qml b/interface/resources/qml/controlsUit/FilterBar.qml new file mode 100644 index 0000000000..0892018913 --- /dev/null +++ b/interface/resources/qml/controlsUit/FilterBar.qml @@ -0,0 +1,333 @@ +// +// FilterBar.qml +// +// Created by Zach Fox on 17 Feb 2018-03-12 +// 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 +// + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 + +import "../stylesUit" +import "." as HifiControls + +Item { + id: root; + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray + property bool error: false; + property alias textFieldHeight: textField.height; + property string placeholderText; + property alias dropdownHeight: dropdownContainer.height; + property alias text: textField.text; + property alias primaryFilterChoices: filterBarModel; + property int primaryFilter_index: -1; + property string primaryFilter_filterName: ""; + property string primaryFilter_displayName: ""; + signal accepted; + + onPrimaryFilter_indexChanged: { + if (primaryFilter_index === -1) { + primaryFilter_filterName = ""; + primaryFilter_displayName = ""; + } else { + primaryFilter_filterName = filterBarModel.get(primaryFilter_index).filterName; + primaryFilter_displayName = filterBarModel.get(primaryFilter_index).displayName; + } + } + + TextField { + id: textField; + + anchors.top: parent.top; + anchors.right: parent.right; + anchors.left: parent.left; + + font.family: "Fira Sans" + font.pixelSize: hifi.fontSizes.textFieldInput; + + placeholderText: root.primaryFilter_index === -1 ? root.placeholderText : ""; + + TextMetrics { + id: primaryFilterTextMetrics; + font.family: "FiraSans Regular"; + font.pixelSize: hifi.fontSizes.textFieldInput; + font.capitalization: Font.AllUppercase; + text: root.primaryFilter_displayName; + } + + // workaround for https://bugreports.qt.io/browse/QTBUG-49297 + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Return: + case Qt.Key_Enter: + event.accepted = true; + + // emit accepted signal manually + if (acceptableInput) { + root.accepted(); + root.forceActiveFocus(); + } + break; + case Qt.Key_Backspace: + if (textField.text === "") { + primaryFilter_index = -1; + } + break; + } + } + + onAccepted: { + root.forceActiveFocus(); + } + + onActiveFocusChanged: { + if (!activeFocus) { + dropdownContainer.visible = false; + } + } + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.lightGrayText + } + } + } + + background: Rectangle { + id: mainFilterBarRectangle; + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.textFieldLightBackground + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.faintGray50 + } + } else { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.baseGrayShadow + } + } + } + + border.color: textField.error ? hifi.colors.redHighlight : + (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + border.width: 1 + radius: 4 + + Item { + id: searchButtonContainer; + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height; + width: 42; + + // Search icon + HiFiGlyphs { + id: searchIcon; + text: hifi.glyphs.search + color: textField.color + size: 40; + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: paintedWidth; + } + + // Carat + HiFiGlyphs { + text: hifi.glyphs.caratDn; + color: textField.color; + size: 40; + anchors.left: parent.left; + anchors.leftMargin: 15; + width: paintedWidth; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.forceActiveFocus(); + dropdownContainer.visible = !dropdownContainer.visible; + } + } + } + + Rectangle { + z: 999; + id: primaryFilterContainer; + color: textField.activeFocus ? hifi.colors.faintGray : hifi.colors.white; + width: primaryFilterTextMetrics.tightBoundingRect.width + 14; + height: parent.height - 8; + anchors.verticalCenter: parent.verticalCenter; + anchors.left: searchButtonContainer.right; + anchors.leftMargin: 4; + visible: primaryFilterText.text !== ""; + radius: height/2; + + FiraSansRegular { + id: primaryFilterText; + text: root.primaryFilter_displayName; + anchors.fill: parent; + color: textField.activeFocus ? hifi.colors.black : hifi.colors.lightGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + size: hifi.fontSizes.textFieldInput; + font.capitalization: Font.AllUppercase; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.forceActiveFocus(); + } + } + } + + // "Clear" button + HiFiGlyphs { + text: hifi.glyphs.error + color: textField.color + size: 40 + anchors.right: parent.right + anchors.rightMargin: hifi.dimensions.textPadding - 2 + anchors.verticalCenter: parent.verticalCenter + visible: root.text !== "" || root.primaryFilter_index !== -1; + + MouseArea { + anchors.fill: parent; + onClicked: { + root.text = ""; + root.primaryFilter_index = -1; + dropdownContainer.visible = false; + textField.forceActiveFocus(); + } + } + } + } + + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + leftPadding: 44 + (root.primaryFilter_index === -1 ? 0 : primaryFilterTextMetrics.tightBoundingRect.width + 20); + rightPadding: 44; + } + + Rectangle { + id: dropdownContainer; + visible: false; + height: 50 * filterBarModel.count; + width: parent.width; + anchors.top: textField.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + color: hifi.colors.white; + + ListModel { + id: filterBarModel; + } + + ListView { + id: dropdownListView; + interactive: false; + anchors.fill: parent; + model: filterBarModel; + delegate: Item { + width: parent.width; + height: 50; + Rectangle { + id: dropDownButton; + color: hifi.colors.white; + width: parent.width; + height: 50; + visible: true; + + RalewaySemiBold { + id: dropDownButtonText; + text: model.displayName; + anchors.fill: parent; + anchors.topMargin: 2; + anchors.leftMargin: 12; + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 18; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + onEntered: { + dropDownButton.color = hifi.colors.blueHighlight; + } + onExited: { + dropDownButton.color = hifi.colors.white; + } + onClicked: { + textField.forceActiveFocus(); + root.primaryFilter_index = index; + dropdownContainer.visible = false; + } + } + } + Rectangle { + height: 2; + width: parent.width; + color: hifi.colors.lightGray; + visible: model.separator + } + } + } + } + + DropShadow { + anchors.fill: dropdownContainer; + horizontalOffset: 0; + verticalOffset: 4; + radius: 4.0; + samples: 9 + color: Qt.rgba(0, 0, 0, 0.25); + source: dropdownContainer; + visible: dropdownContainer.visible; + } + + function changeFilterByDisplayName(name) { + for (var i = 0; i < filterBarModel.count; i++) { + if (filterBarModel.get(i).displayName === name) { + root.primaryFilter_index = i; + return; + } + } + + console.log("Passed displayName not found in filterBarModel! primaryFilter unchanged."); + } +} diff --git a/interface/resources/qml/controlsUit/GlyphButton.qml b/interface/resources/qml/controlsUit/GlyphButton.qml new file mode 100644 index 0000000000..17f7fba2d6 --- /dev/null +++ b/interface/resources/qml/controlsUit/GlyphButton.qml @@ -0,0 +1,91 @@ +// +// GlyphButton.qml +// +// Created by Clement on 3/7/16 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Original +import TabletScriptingInterface 1.0 + +import "../stylesUit" + +Original.Button { + id: control + property int color: 0 + property int colorScheme: hifi.colorSchemes.light + property string glyph: "" + property int size: 32 + + width: 120 + height: 28 + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + } + + background: Rectangle { + radius: hifi.buttons.radius + + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorStart[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] + } else { + hifi.buttons.colorStart[control.color] + } + } + } + GradientStop { + position: 1.0 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorFinish[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else if (!control.hovered && control.focus) { + hifi.buttons.focusedColor[control.color] + } else { + hifi.buttons.colorFinish[control.color] + } + } + } + } + } + + contentItem: HiFiGlyphs { + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.glyph + size: control.size + } +} + diff --git a/interface/resources/qml/controlsUit/HorizontalRule.qml b/interface/resources/qml/controlsUit/HorizontalRule.qml new file mode 100644 index 0000000000..0609cc451d --- /dev/null +++ b/interface/resources/qml/controlsUit/HorizontalRule.qml @@ -0,0 +1,18 @@ +// +// HorizontalRule.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: hifi.colors.lightGray +} diff --git a/interface/resources/qml/controlsUit/HorizontalSpacer.qml b/interface/resources/qml/controlsUit/HorizontalSpacer.qml new file mode 100644 index 0000000000..efcabf2699 --- /dev/null +++ b/interface/resources/qml/controlsUit/HorizontalSpacer.qml @@ -0,0 +1,21 @@ +// +// HorizontalSpacer.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +import "../stylesUit" + +Item { + id: root + property alias size: root.width + + width: hifi.dimensions.controlInterlineHeight + height: 1 // Must be non-zero +} diff --git a/interface/resources/qml/controlsUit/ImageMessageBox.qml b/interface/resources/qml/controlsUit/ImageMessageBox.qml new file mode 100644 index 0000000000..46d93383a4 --- /dev/null +++ b/interface/resources/qml/controlsUit/ImageMessageBox.qml @@ -0,0 +1,63 @@ +// +// ImageMessageBox.qml +// +// Created by Dante Ruiz on 7/5/2017 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import "../stylesUit" + +Item { + id: imageBox + visible: false + anchors.fill: parent + property alias source: image.source + property alias imageWidth: image.width + property alias imageHeight: image.height + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.3 + } + + Image { + id: image + anchors.centerIn: parent + + HiFiGlyphs { + id: closeGlyphButton + text: hifi.glyphs.close + size: 25 + + anchors { + top: parent.top + topMargin: 15 + right: parent.right + rightMargin: 15 + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + + onExited: { + parent.text = hifi.glyphs.close; + } + + onClicked: { + imageBox.visible = false; + } + } + } + } + +} diff --git a/interface/resources/qml/controlsUit/Key.qml b/interface/resources/qml/controlsUit/Key.qml new file mode 100644 index 0000000000..dd77fc92dc --- /dev/null +++ b/interface/resources/qml/controlsUit/Key.qml @@ -0,0 +1,185 @@ +import QtQuick 2.0 +import TabletScriptingInterface 1.0 + +Item { + id: keyItem + width: 45 + height: 50 + + property int contentPadding: 4 + property string glyph: "a" + property bool toggle: false // does this button have the toggle behaivor? + property bool toggled: false // is this button currently toggled? + property alias mouseArea: mouseArea1 + property alias fontFamily: letter.font.family; + property alias fontPixelSize: letter.font.pixelSize + property alias verticalAlignment: letter.verticalAlignment + property alias letterAnchors: letter.anchors + + function resetToggledMode(mode) { + toggled = mode; + if (toggled) { + state = "mouseDepressed"; + } else { + state = ""; + } + } + + MouseArea { + id: mouseArea1 + width: 36 + anchors.fill: parent + hoverEnabled: true + + onCanceled: { + if (toggled) { + keyItem.state = "mouseDepressed"; + } else { + keyItem.state = ""; + } + } + + onContainsMouseChanged: { + if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onDoubleClicked: { + mouse.accepted = true; + } + + property var _HAPTIC_STRENGTH: 0.1; + property var _HAPTIC_DURATION: 3.0; + property var leftHand: 0; + property var rightHand: 1; + + onEntered: { + keyItem.state = "mouseOver"; + + var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY); + var pointerID = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y); + + if (Pointers.isLeftHand(pointerID)) { + Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, leftHand); + } else if (Pointers.isRightHand(pointerID)) { + Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, rightHand); + } + } + + onExited: { + if (toggled) { + keyItem.state = "mouseDepressed"; + } else { + keyItem.state = ""; + } + } + + onPressed: { + keyItem.state = "mouseClicked"; + mouse.accepted = true; + } + + onReleased: { + if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonClick); + + webEntity.synthesizeKeyPress(glyph); + webEntity.synthesizeKeyPress(glyph, mirrorText); + + if (toggle) { + toggled = !toggled; + } + keyItem.state = "mouseOver"; + } else { + if (toggled) { + keyItem.state = "mouseDepressed"; + } else { + keyItem.state = ""; + } + } + mouse.accepted = true; + } + } + + Rectangle { + id: roundedRect + width: 30 + color: "#121212" + radius: 2 + border.color: "#00000000" + anchors.fill: parent + anchors.margins: contentPadding + } + + Text { + id: letter + y: 6 + width: 50 + color: "#ffffff" + text: glyph + style: Text.Normal + font.family: "Tahoma" + anchors.right: parent.right + anchors.rightMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + anchors.top: parent.top + anchors.topMargin: 8 + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 28 + } + + states: [ + State { + name: "mouseOver" + + PropertyChanges { + target: roundedRect + color: "#121212" + radius: 3 + border.width: 2 + border.color: "#00b4ef" + } + + PropertyChanges { + target: letter + color: "#00b4ef" + style: Text.Normal + } + }, + State { + name: "mouseClicked" + PropertyChanges { + target: roundedRect + color: "#1080b8" + border.width: 2 + border.color: "#00b4ef" + } + + PropertyChanges { + target: letter + color: "#121212" + styleColor: "#00000000" + style: Text.Normal + } + }, + State { + name: "mouseDepressed" + PropertyChanges { + target: roundedRect + color: "#0578b1" + border.width: 0 + } + + PropertyChanges { + target: letter + color: "#121212" + styleColor: "#00000000" + style: Text.Normal + } + } + ] +} diff --git a/interface/resources/qml/controlsUit/Keyboard.qml b/interface/resources/qml/controlsUit/Keyboard.qml new file mode 100644 index 0000000000..c38631ff79 --- /dev/null +++ b/interface/resources/qml/controlsUit/Keyboard.qml @@ -0,0 +1,370 @@ +// +// FileDialog.qml +// +// Created by Anthony Thibault on 31 Oct 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtGraphicalEffects 1.0 +import "." + +Rectangle { + id: keyboardBase + objectName: "keyboard" + + anchors.left: parent.left + anchors.right: parent.right + + color: "#252525" + + property bool raised: false + property bool numeric: false + + readonly property int keyboardRowHeight: 50 + readonly property int keyboardWidth: 480 + readonly property int keyboardHeight: 200 + + readonly property int mirrorTextHeight: keyboardRowHeight + + property bool password: false + property alias mirroredText: mirrorText.text + property bool showMirrorText: true + + readonly property int raisedHeight: keyboardHeight + (showMirrorText ? keyboardRowHeight : 0) + + height: 0 + visible: false + + property bool shiftMode: false + property bool numericShiftMode: false + + + onPasswordChanged: { + var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard"); + if (use3DKeyboard) { + KeyboardScriptingInterface.password = password; + } + } + + onRaisedChanged: { + var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard"); + if (!use3DKeyboard) { + keyboardBase.height = raised ? raisedHeight : 0; + keyboardBase.visible = raised; + } else { + KeyboardScriptingInterface.raised = raised; + KeyboardScriptingInterface.password = raised ? password : false; + } + mirroredText = ""; + } + + function resetShiftMode(mode) { + shiftMode = mode; + shiftKey.resetToggledMode(mode); + } + + function toUpper(str) { + if (str === ",") { + return "<"; + } else if (str === ".") { + return ">"; + } else if (str === "/") { + return "?"; + } else if (str === "-") { + return "_"; + } else { + return str.toUpperCase(str); + } + } + + function toLower(str) { + if (str === "<") { + return ","; + } else if (str === ">") { + return "."; + } else if (str === "?") { + return "/"; + } else if (str === "_") { + return "-"; + } else { + return str.toLowerCase(str); + } + } + + function forEachKey(func) { + var i, j; + for (i = 0; i < columnAlpha.children.length; i++) { + var row = columnAlpha.children[i]; + for (j = 0; j < row.children.length; j++) { + var key = row.children[j]; + func(key); + } + } + } + + onShiftModeChanged: { + forEachKey(function (key) { + if (/[a-z-_]/i.test(key.glyph)) { + if (shiftMode) { + key.glyph = keyboardBase.toUpper(key.glyph); + } else { + key.glyph = keyboardBase.toLower(key.glyph); + } + } + }); + } + + function alphaKeyClickedHandler(mouseArea) { + // reset shift mode to false after first keypress + if (shiftMode) { + resetShiftMode(false); + } + } + + Component.onCompleted: { + // hook up callbacks to every ascii key + forEachKey(function (key) { + if (/^[a-z]+$/i.test(key.glyph)) { + key.mouseArea.onClicked.connect(alphaKeyClickedHandler); + } + }); + } + + Rectangle { + height: showMirrorText ? mirrorTextHeight : 0 + width: keyboardWidth + color: "#252525" + anchors.horizontalCenter: parent.horizontalCenter + + TextInput { + id: mirrorText + visible: showMirrorText + font.family: "Fira Sans" + font.pixelSize: 20 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + color: "#00B4EF"; + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + + wrapMode: Text.WordWrap + readOnly: false // we need this to allow control to accept QKeyEvent + selectByMouse: false + echoMode: password ? TextInput.Password : TextInput.Normal + + Keys.onPressed: { + if (event.key == Qt.Key_Return || event.key == Qt.Key_Space) { + mirrorText.text = ""; + event.accepted = true; + } + } + + MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus + anchors.fill: parent + } + } + } + + Rectangle { + id: keyboardRect + y: showMirrorText ? mirrorTextHeight : 0 + width: keyboardWidth + height: keyboardHeight + color: "#252525" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + + Column { + id: columnAlpha + width: keyboardWidth + height: keyboardHeight + visible: !numeric + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { width: 43; glyph: "q"; } + Key { width: 43; glyph: "w"; } + Key { width: 43; glyph: "e"; } + Key { width: 43; glyph: "r"; } + Key { width: 43; glyph: "t"; } + Key { width: 43; glyph: "y"; } + Key { width: 43; glyph: "u"; } + Key { width: 43; glyph: "i"; } + Key { width: 43; glyph: "o"; } + Key { width: 43; glyph: "p"; } + Key { width: 43; glyph: "←"; } + } + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 20 + + Key { width: 43; glyph: "a"; } + Key { width: 43; glyph: "s"; } + Key { width: 43; glyph: "d"; } + Key { width: 43; glyph: "f"; } + Key { width: 43; glyph: "g"; } + Key { width: 43; glyph: "h"; } + Key { width: 43; glyph: "j"; } + Key { width: 43; glyph: "k"; } + Key { width: 43; glyph: "l"; } + Key { width: 70; glyph: "⏎"; } + } + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { + id: shiftKey + width: 43 + glyph: "⇪" + toggle: true + onToggledChanged: shiftMode = toggled + } + Key { width: 43; glyph: "z"; } + Key { width: 43; glyph: "x"; } + Key { width: 43; glyph: "c"; } + Key { width: 43; glyph: "v"; } + Key { width: 43; glyph: "b"; } + Key { width: 43; glyph: "n"; } + Key { width: 43; glyph: "m"; } + Key { width: 43; glyph: "-"; } + Key { width: 43; glyph: "/"; } + Key { width: 43; glyph: "?"; } + } + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { + width: 70 + glyph: "123" + mouseArea.onClicked: keyboardBase.parent.punctuationMode = true + } + Key { width: 231; glyph: " "; } + Key { width: 43; glyph: ","; } + Key { width: 43; glyph: "."; } + Key { + fontFamily: "hifi-glyphs"; + fontPixelSize: 48; + letterAnchors.topMargin: -4; + verticalAlignment: Text.AlignVCenter; + width: 86; glyph: "\ue02b"; + } + } + } + + Column { + id: columnNumeric + width: keyboardWidth + height: keyboardHeight + visible: numeric + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { width: 43; glyph: "1"; } + Key { width: 43; glyph: "2"; } + Key { width: 43; glyph: "3"; } + Key { width: 43; glyph: "4"; } + Key { width: 43; glyph: "5"; } + Key { width: 43; glyph: "6"; } + Key { width: 43; glyph: "7"; } + Key { width: 43; glyph: "8"; } + Key { width: 43; glyph: "9"; } + Key { width: 43; glyph: "0"; } + Key { width: 43; glyph: "←"; } + } + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { width: 43; glyph: "!"; } + Key { width: 43; glyph: "@"; } + Key { width: 43; glyph: "#"; } + Key { width: 43; glyph: "$"; } + Key { width: 43; glyph: "%"; } + Key { width: 43; glyph: "^"; } + Key { width: 43; glyph: "&"; } + Key { width: 43; glyph: "*"; } + Key { width: 43; glyph: "("; } + Key { width: 43; glyph: ")"; } + Key { width: 43; glyph: "⏎"; } + } + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { + id: numericShiftKey + width: 43 + glyph: "\u21E8" + toggle: true + onToggledChanged: numericShiftMode = toggled + } + Key { width: 43; glyph: numericShiftMode ? "`" : "+"; } + Key { width: 43; glyph: numericShiftMode ? "~" : "-"; } + Key { width: 43; glyph: numericShiftMode ? "\u00A3" : "="; } + Key { width: 43; glyph: numericShiftMode ? "\u20AC" : ";"; } + Key { width: 43; glyph: numericShiftMode ? "\u00A5" : ":"; } + Key { width: 43; glyph: numericShiftMode ? "<" : "'"; } + Key { width: 43; glyph: numericShiftMode ? ">" : "\""; } + Key { width: 43; glyph: numericShiftMode ? "[" : "{"; } + Key { width: 43; glyph: numericShiftMode ? "]" : "}"; } + Key { width: 43; glyph: numericShiftMode ? "\\" : "|"; } + } + + Row { + width: keyboardWidth + height: keyboardRowHeight + anchors.left: parent.left + anchors.leftMargin: 4 + + Key { + width: 70 + glyph: "abc" + mouseArea.onClicked: keyboardBase.parent.punctuationMode = false + } + Key { width: 231; glyph: " "; } + Key { width: 43; glyph: ","; } + Key { width: 43; glyph: "."; } + Key { + fontFamily: "hifi-glyphs"; + fontPixelSize: 48; + letterAnchors.topMargin: -4; + verticalAlignment: Text.AlignVCenter; + width: 86; glyph: "\ue02b"; + } + } + } + } +} diff --git a/interface/resources/qml/controlsUit/Label.qml b/interface/resources/qml/controlsUit/Label.qml new file mode 100644 index 0000000000..7f208cde88 --- /dev/null +++ b/interface/resources/qml/controlsUit/Label.qml @@ -0,0 +1,35 @@ +// +// Label.qml +// +// Created by David Rowe on 26 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 + +import "../stylesUit" + +RalewaySemiBold { + HifiConstants { id: hifi } + property int colorScheme: hifi.colorSchemes.light + + size: hifi.fontSizes.inputLabel + color: { + if (colorScheme === hifi.colorSchemes.dark) { + if (enabled) { + hifi.colors.lightGrayText + } else { + hifi.colors.baseGrayHighlight + } + } else { + if (enabled) { + hifi.colors.lightGray + } else { + hifi.colors.lightGrayText + } + } + } +} diff --git a/interface/resources/qml/controlsUit/QueuedButton.qml b/interface/resources/qml/controlsUit/QueuedButton.qml new file mode 100644 index 0000000000..70ad9eb112 --- /dev/null +++ b/interface/resources/qml/controlsUit/QueuedButton.qml @@ -0,0 +1,41 @@ +// +// QueuedButton.qml +// -- original Button.qml + signal timer workaround --ht +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +import "../stylesUit" +import "." as HifiControls + +HifiControls.Button { + // FIXME: THIS WORKAROUND MIGRATED/CONSOLIDATED FROM RUNNINGSCRIPTS.QML + + // For some reason trigginer an API that enters + // an internal event loop directly from the button clicked + // trigger below causes the appliction to behave oddly. + // Most likely because the button onClicked handling is never + // completed until the function returns. + // FIXME find a better way of handling the input dialogs that + // doesn't trigger this. + + // NOTE: dialogs that need to use this workaround can connect via + // onQueuedClicked: ... + // instead of: + // onClicked: ... + + signal clickedQueued() + Timer { + id: fromTimer + interval: 5 + repeat: false + running: false + onTriggered: clickedQueued() + } + onClicked: fromTimer.running = true +} diff --git a/interface/resources/qml/controlsUit/RadioButton.qml b/interface/resources/qml/controlsUit/RadioButton.qml new file mode 100644 index 0000000000..ad62a77aa7 --- /dev/null +++ b/interface/resources/qml/controlsUit/RadioButton.qml @@ -0,0 +1,93 @@ +// +// RadioButton.qml +// +// Created by Cain Kilgore on 20th July 2017 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 2.2 as Original + +import "../stylesUit" +import "." as HifiControls + +import TabletScriptingInterface 1.0 + +Original.RadioButton { + id: radioButton + HifiConstants { id: hifi } + + hoverEnabled: true + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + property real letterSpacing: 1 + property int fontSize: hifi.fontSizes.inputLabel + property int boxSize: defaultBoxSize + property real scaleFactor: boxSize / defaultBoxSize + + readonly property int defaultBoxSize: 14 + readonly property int boxRadius: 3 * scaleFactor + readonly property int checkSize: 10 * scaleFactor + readonly property int checkRadius: 2 * scaleFactor + readonly property int indicatorRadius: 7 * scaleFactor + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + } + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + indicator: Rectangle { + id: box + width: boxSize + height: boxSize + radius: indicatorRadius + x: radioButton.leftPadding + y: parent.height / 2 - height / 2 + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart) + : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish) + : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: indicatorRadius + anchors.centerIn: parent + color: "#00B4EF" + border.width: 1 + border.color: "#36CDFF" + visible: checked && !pressed || !checked && pressed + } + } + + contentItem: RalewaySemiBold { + text: radioButton.text + size: radioButton.fontSize + font.letterSpacing: letterSpacing + color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + leftPadding: radioButton.indicator.width + radioButton.spacing + } +} diff --git a/interface/resources/qml/controlsUit/ScrollBar.qml b/interface/resources/qml/controlsUit/ScrollBar.qml new file mode 100644 index 0000000000..bcb1f62429 --- /dev/null +++ b/interface/resources/qml/controlsUit/ScrollBar.qml @@ -0,0 +1,41 @@ +// +// ScrollBar.qml +// +// Created by Vlad Stelmahovsky on 27 Nov 2017 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../stylesUit" + +ScrollBar { + visible: size < 1.0 + + HifiConstants { id: hifi } + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + background: Item { + implicitWidth: hifi.dimensions.scrollbarBackgroundWidth + Rectangle { + anchors { fill: parent; topMargin: 3; bottomMargin: 3 } + radius: hifi.dimensions.scrollbarHandleWidth/2 + color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight + : hifi.colors.tableScrollBackgroundDark + } + } + + contentItem: Item { + implicitWidth: hifi.dimensions.scrollbarHandleWidth + Rectangle { + anchors { fill: parent; topMargin: 1; bottomMargin: 1 } + radius: hifi.dimensions.scrollbarHandleWidth/2 + color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark + } + } +} diff --git a/interface/resources/qml/controlsUit/Separator.qml b/interface/resources/qml/controlsUit/Separator.qml new file mode 100644 index 0000000000..da6b9adf57 --- /dev/null +++ b/interface/resources/qml/controlsUit/Separator.qml @@ -0,0 +1,44 @@ +// +// Separator.qml +// +// Created by Zach Fox on 2017-06-06 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import "../stylesUit" + +Item { + property int colorScheme: 0; + + readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray, "#89858C" ]; + readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray, "#89858C" ]; + + // Size + height: colorScheme === 0 ? 2 : 1; + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + // Style + color: topColor[colorScheme]; + } + Rectangle { + visible: colorScheme === 0; + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: -height; + // Style + color: bottomColor[colorScheme]; + } +} diff --git a/interface/resources/qml/controlsUit/Slider.qml b/interface/resources/qml/controlsUit/Slider.qml new file mode 100644 index 0000000000..8cb08b69e2 --- /dev/null +++ b/interface/resources/qml/controlsUit/Slider.qml @@ -0,0 +1,98 @@ +// +// Slider.qml +// +// Created by David Rowe on 27 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../stylesUit" +import "." as HifiControls + +Slider { + id: slider + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property string label: "" + property real controlHeight: height + (sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0) + + property alias minimumValue: slider.from + property alias maximumValue: slider.to + property alias step: slider.stepSize + property bool tickmarksEnabled: false + + height: hifi.fontSizes.textFieldInput + 14 // Match height of TextField control. + y: sliderLabel.visible ? sliderLabel.height + sliderLabel.anchors.bottomMargin : 0 + + background: Rectangle { + x: slider.leftPadding + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + + implicitWidth: 50 + implicitHeight: hifi.dimensions.sliderGrooveHeight + width: slider.availableWidth + height: implicitHeight + radius: height / 2 + color: isLightColorScheme ? hifi.colors.sliderGutterLight : hifi.colors.sliderGutterDark + + Rectangle { + width: slider.visualPosition * parent.width + height: parent.height + radius: height / 2 + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.blueAccent } + GradientStop { position: 1.0; color: hifi.colors.primaryHighlight } + } + } + } + + handle: Rectangle { + x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + implicitWidth: hifi.dimensions.sliderHandleSize + implicitHeight: hifi.dimensions.sliderHandleSize + radius: height / 2 + border.width: 1 + border.color: isLightColorScheme ? hifi.colors.sliderBorderLight : hifi.colors.sliderBorderDark + gradient: Gradient { + GradientStop { + position: 0.0 + color: pressed || hovered + ? (isLightColorScheme ? hifi.colors.sliderDarkStart : hifi.colors.sliderLightStart ) + : (isLightColorScheme ? hifi.colors.sliderLightStart : hifi.colors.sliderDarkStart ) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (isLightColorScheme ? hifi.colors.sliderDarkFinish : hifi.colors.sliderLightFinish ) + : (isLightColorScheme ? hifi.colors.sliderLightFinish : hifi.colors.sliderDarkFinish ) + } + } + + Rectangle { + height: parent.height - 2 + width: height + radius: height / 2 + anchors.centerIn: parent + color: hifi.colors.transparent + border.width: 1 + border.color: hifi.colors.black + } + } + + HifiControls.Label { + id: sliderLabel + text: slider.label + colorScheme: slider.colorScheme + anchors.left: parent.left + anchors.bottom: parent.top + anchors.bottomMargin: 2 + visible: label != "" + } +} diff --git a/interface/resources/qml/controlsUit/SpinBox.qml b/interface/resources/qml/controlsUit/SpinBox.qml new file mode 100644 index 0000000000..d24c7c5e8c --- /dev/null +++ b/interface/resources/qml/controlsUit/SpinBox.qml @@ -0,0 +1,185 @@ +// +// SpinBox.qml +// +// Created by David Rowe on 26 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../stylesUit" +import "." as HifiControls + +SpinBox { + id: spinBox + + HifiConstants { + id: hifi + } + + inputMethodHints: Qt.ImhFormattedNumbersOnly + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light + property string label: "" + property string suffix: "" + property string labelInside: "" + property color colorLabelInside: hifi.colors.white + property color backgroundColor: isLightColorScheme + ? (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGray) + : (spinBox.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow) + property real controlHeight: height + (spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0) + property int decimals: 2; + property real factor: Math.pow(10, decimals) + + property real minimumValue: 0.0 + property real maximumValue: 0.0 + + property real realValue: 0.0 + property real realFrom: minimumValue + property real realTo: maximumValue + property real realStepSize: 1.0 + + signal editingFinished() + + implicitHeight: height + implicitWidth: width + editable: true + + padding: 0 + leftPadding: 0 + rightPadding: padding + (up.indicator ? up.indicator.width : 0) + topPadding: 0 + bottomPadding: 0 + + locale: Qt.locale("en_US") + + onValueModified: realValue = value/factor + onValueChanged: realValue = value/factor + onRealValueChanged: { + var newValue = Math.round(realValue*factor); + if(value != newValue) { + value = newValue; + } + } + + stepSize: realStepSize*factor + to : realTo*factor + from : realFrom*factor + + font.family: "Fira Sans SemiBold" + font.pixelSize: hifi.fontSizes.textFieldInput + height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. + + y: spinBoxLabel.visible ? spinBoxLabel.height + spinBoxLabel.anchors.bottomMargin : 0 + + background: Rectangle { + color: backgroundColor + border.color: spinBoxLabelInside.visible ? spinBoxLabelInside.color : hifi.colors.primaryHighlight + border.width: spinBox.activeFocus ? spinBoxLabelInside.visible ? 2 : 1 : 0 + } + + validator: DoubleValidator { + bottom: Math.min(spinBox.from, spinBox.to) + top: Math.max(spinBox.from, spinBox.to) + } + + textFromValue: function(value, locale) { + return parseFloat(value/factor).toFixed(decimals); + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text)*factor; + } + + + contentItem: TextInput { + z: 2 + color: isLightColorScheme + ? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray) + : (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText) + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix + inputMethodHints: spinBox.inputMethodHints + validator: spinBox.validator + verticalAlignment: Qt.AlignVCenter + leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding + //rightPadding: hifi.dimensions.spinnerSize + width: spinBox.width - hifi.dimensions.spinnerSize + onEditingFinished: spinBox.editingFinished() + } + + up.indicator: Item { + x: spinBox.width - implicitWidth - 5 + y: 1 + clip: true + implicitHeight: spinBox.implicitHeight/2 + implicitWidth: spinBox.implicitHeight/2 + HiFiGlyphs { + anchors.centerIn: parent + text: hifi.glyphs.caratUp + size: hifi.dimensions.spinnerSize + color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray + } + } + up.onPressedChanged: { + if(value) { + spinBox.forceActiveFocus(); + } + } + + down.indicator: Item { + x: spinBox.width - implicitWidth - 5 + y: spinBox.implicitHeight/2 + clip: true + implicitHeight: spinBox.implicitHeight/2 + implicitWidth: spinBox.implicitHeight/2 + HiFiGlyphs { + anchors.centerIn: parent + text: hifi.glyphs.caratDn + size: hifi.dimensions.spinnerSize + color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray + } + } + down.onPressedChanged: { + if(value) { + spinBox.forceActiveFocus(); + } + } + + HifiControls.Label { + id: spinBoxLabel + text: spinBox.label + colorScheme: spinBox.colorScheme + anchors.left: parent.left + anchors.bottom: parent.top + anchors.bottomMargin: 4 + visible: label != "" + } + + HifiControls.Label { + id: spinBoxLabelInside + text: spinBox.labelInside + anchors.left: parent.left + anchors.leftMargin: 10 + font.bold: true + anchors.verticalCenter: parent.verticalCenter + color: spinBox.colorLabelInside + visible: spinBox.labelInside != "" + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + onWheel: { + if (wheel.angleDelta.y > 0) + value += stepSize + else + value -= stepSize + } + } +} diff --git a/interface/resources/qml/controlsUit/Switch.qml b/interface/resources/qml/controlsUit/Switch.qml new file mode 100644 index 0000000000..0961ef2500 --- /dev/null +++ b/interface/resources/qml/controlsUit/Switch.qml @@ -0,0 +1,160 @@ +// +// Switch.qml +// +// Created by Zach Fox on 2017-06-06 +// Copyright 2017 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 as Original + +import "../stylesUit" + +Item { + id: rootSwitch; + + property int colorScheme: hifi.colorSchemes.light; + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light; + property int switchWidth: 70; + readonly property int switchRadius: height/2; + property string labelTextOff: ""; + property string labelGlyphOffText: ""; + property int labelGlyphOffSize: 32; + property string labelTextOn: ""; + property string labelGlyphOnText: ""; + property int labelGlyphOnSize: 32; + property alias checked: originalSwitch.checked; + signal onCheckedChanged; + signal clicked; + + Original.Switch { + id: originalSwitch; + focusPolicy: Qt.ClickFocus + anchors.top: rootSwitch.top; + anchors.left: rootSwitch.left; + anchors.leftMargin: rootSwitch.width/2 - rootSwitch.switchWidth/2; + onCheckedChanged: rootSwitch.onCheckedChanged(); + onClicked: rootSwitch.clicked(); + hoverEnabled: true + + topPadding: 3; + leftPadding: 3; + rightPadding: 3; + bottomPadding: 3; + + onHoveredChanged: { + if (hovered) { + switchHandle.color = hifi.colors.blueHighlight; + } else { + switchHandle.color = hifi.colors.lightGray; + } + } + + background: Rectangle { + color: "#252525"; + implicitWidth: rootSwitch.switchWidth; + implicitHeight: rootSwitch.height; + radius: rootSwitch.switchRadius; + } + + indicator: Rectangle { + id: switchHandle; + implicitWidth: rootSwitch.height - originalSwitch.topPadding - originalSwitch.bottomPadding; + implicitHeight: implicitWidth; + radius: implicitWidth/2; + border.color: hifi.colors.lightGrayText; + color: hifi.colors.lightGray; + //x: originalSwitch.leftPadding + x: Math.max(0, Math.min(parent.width - width, originalSwitch.visualPosition * parent.width - (width / 2))) + y: parent.height / 2 - height / 2 + Behavior on x { + enabled: !originalSwitch.down + SmoothedAnimation { velocity: 200 } + } + + } + } + + // OFF Label + Item { + anchors.right: originalSwitch.left; + anchors.rightMargin: 10; + anchors.top: rootSwitch.top; + height: rootSwitch.height; + + RalewaySemiBold { + id: labelOff; + text: labelTextOff; + size: hifi.fontSizes.inputLabel; + color: originalSwitch.checked ? hifi.colors.lightGrayText : "#FFFFFF"; + anchors.top: parent.top; + anchors.right: parent.right; + width: paintedWidth; + height: parent.height; + verticalAlignment: Text.AlignVCenter; + } + + HiFiGlyphs { + id: labelGlyphOff; + text: labelGlyphOffText; + size: labelGlyphOffSize; + color: labelOff.color; + anchors.top: parent.top; + anchors.topMargin: 2; + anchors.right: labelOff.left; + anchors.rightMargin: 4; + } + + MouseArea { + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: labelGlyphOff.left; + anchors.right: labelOff.right; + onClicked: { + originalSwitch.checked = false; + } + } + } + + // ON Label + Item { + anchors.left: originalSwitch.right; + anchors.leftMargin: 10; + anchors.top: rootSwitch.top; + height: rootSwitch.height; + + RalewaySemiBold { + id: labelOn; + text: labelTextOn; + size: hifi.fontSizes.inputLabel; + color: originalSwitch.checked ? "#FFFFFF" : hifi.colors.lightGrayText; + anchors.top: parent.top; + anchors.left: parent.left; + width: paintedWidth; + height: parent.height; + verticalAlignment: Text.AlignVCenter; + } + + HiFiGlyphs { + id: labelGlyphOn; + text: labelGlyphOnText; + size: labelGlyphOnSize; + color: labelOn.color; + anchors.top: parent.top; + anchors.left: labelOn.right; + } + + MouseArea { + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: labelOn.left; + anchors.right: labelGlyphOn.right; + onClicked: { + originalSwitch.checked = true; + } + } + } +} diff --git a/interface/resources/qml/controlsUit/Table.qml b/interface/resources/qml/controlsUit/Table.qml new file mode 100644 index 0000000000..ab74361046 --- /dev/null +++ b/interface/resources/qml/controlsUit/Table.qml @@ -0,0 +1,165 @@ +// +// Table.qml +// +// Created by David Rowe on 18 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 as QQC2 + +import "../stylesUit" + +TableView { + id: tableView + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property bool expandSelectedRow: false + property bool centerHeaderText: false + readonly property real headerSpacing: 3 //spacing between sort indicator and table header title + property var titlePaintedPos: [] // storing extra data position behind painted + // title text and sort indicatorin table's header + signal titlePaintedPosSignal(int column) //signal that extradata position gets changed + + model: ListModel { } + + Component.onCompleted: { + if (flickableItem !== null && flickableItem !== undefined) { + tableView.flickableItem.QQC2.ScrollBar.vertical = scrollbar + } + } + + QQC2.ScrollBar { + id: scrollbar + parent: tableView.flickableItem + policy: QQC2.ScrollBar.AsNeeded + orientation: Qt.Vertical + visible: size < 1.0 + topPadding: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1 + anchors.top: tableView.top + anchors.left: tableView.right + anchors.bottom: tableView.bottom + + background: Item { + implicitWidth: hifi.dimensions.scrollbarBackgroundWidth + Rectangle { + anchors { + fill: parent; + topMargin: tableView.headerVisible ? hifi.dimensions.tableHeaderHeight : 0 + } + color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight + : hifi.colors.tableScrollBackgroundDark + } + } + + contentItem: Item { + implicitWidth: hifi.dimensions.scrollbarHandleWidth + Rectangle { + anchors.fill: parent + radius: (width - 4)/2 + color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark + } + } + } + + headerVisible: false + headerDelegate: Rectangle { + height: hifi.dimensions.tableHeaderHeight + color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + + + RalewayRegular { + id: titleText + x: centerHeaderText ? (parent.width - paintedWidth - + ((sortIndicatorVisible && + sortIndicatorColumn === styleData.column) ? + (titleSort.paintedWidth / 5 + tableView.headerSpacing) : 0)) / 2 : + hifi.dimensions.tablePadding + text: styleData.value + size: hifi.fontSizes.tableHeading + font.capitalization: Font.AllUppercase + color: hifi.colors.baseGrayHighlight + horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) + anchors.verticalCenter: parent.verticalCenter + } + + //actual image of sort indicator in glyph font only 20% of real font size + //i.e. if the charachter size set to 60 pixels, actual image is 12 pixels + HiFiGlyphs { + id: titleSort + text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn + color: hifi.colors.darkGray + opacity: 0.6; + size: hifi.fontSizes.tableHeadingIcon + anchors.verticalCenter: titleText.verticalCenter + anchors.left: titleText.right + anchors.leftMargin: -(hifi.fontSizes.tableHeadingIcon / 2.5) + tableView.headerSpacing + visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column + onXChanged: { + titlePaintedPos[styleData.column] = titleText.x + titleText.paintedWidth + + paintedWidth / 5 + tableView.headerSpacing*2 + titlePaintedPosSignal(styleData.column) + } + } + + Rectangle { + width: 1 + anchors { + left: parent.left + top: parent.top + topMargin: 1 + bottom: parent.bottom + bottomMargin: 2 + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + visible: styleData.column > 0 + } + + Rectangle { + height: 1 + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + } + } + + // Use rectangle to draw border with rounded corners. + frameVisible: false + Rectangle { + color: "#00000000" + anchors { fill: parent; margins: -2 } + border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + border.width: 2 + } + anchors.margins: 2 // Shrink TableView to lie within border. + + backgroundVisible: true + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + style: TableViewStyle { + // Needed in order for rows to keep displaying rows after end of table entries. + backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 + } + + rowDelegate: Rectangle { + height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight + color: styleData.selected + ? hifi.colors.primaryHighlight + : tableView.isLightColorScheme + ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) + : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) + } +} diff --git a/interface/resources/qml/controlsUit/TabletContentSection.qml b/interface/resources/qml/controlsUit/TabletContentSection.qml new file mode 100644 index 0000000000..dccaf31bbe --- /dev/null +++ b/interface/resources/qml/controlsUit/TabletContentSection.qml @@ -0,0 +1,138 @@ +// +// ContentSection.qml +// +// Created by Dante Ruiz on 13 Feb 2017 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import QtGraphicalEffects 1.0 + +import "../stylesUit" + +Column { + property string name: "Content Section" + property bool isFirst: false + property bool isCollapsible: false // Set at creation. + property bool isCollapsed: false + + spacing: 0 // Defer spacing decisions to individual controls. + + anchors { + left: parent.left + leftMargin: hifi.dimensions.contentMargin.x + right: parent.right + rightMargin: hifi.dimensions.contentMargin.x + } + + function toggleCollapsed() { + if (isCollapsible) { + isCollapsed = !isCollapsed; + for (var i = 1; i < children.length; i++) { + children[i].visible = !isCollapsed; + } + } + } + + Item { + id: sectionName + anchors.left: parent.left + anchors.right: parent.right + height: leadingSpace.height + topBar.height + heading.height + bottomBar.height + + Item { + id: leadingSpace + width: 1 + height: isFirst ? 7 : 0 + anchors.top: parent.top + } + + Item { + id: topBar + visible: !isFirst + height: visible ? 2 : 0 + anchors.top: leadingSpace.bottom + + Rectangle { + id: shadow + width: 480 + height: 1 + color: hifi.colors.baseGrayShadow + x: -hifi.dimensions.contentMargin.x + } + + Rectangle { + width: 480 + height: 1 + color: hifi.colors.baseGrayHighlight + x: -hifi.dimensions.contentMargin.x + anchors.top: shadow.bottom + } + } + + Item { + id: heading + anchors { + left: parent.left + right: parent.right + top: topBar.bottom + } + height: isCollapsible ? 36 : 28 + + RalewayRegular { + id: title + anchors { + left: parent.left + top: parent.top + topMargin: 12 + } + size: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase + text: name + color: hifi.colors.lightGrayText + } + + HiFiGlyphs { + anchors { + top: title.top + topMargin: -9 + right: parent.right + rightMargin: -4 + } + size: hifi.fontSizes.disclosureButton + text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse + color: hifi.colors.lightGrayText + visible: isCollapsible + } + + MouseArea { + // Events are propogated so that any active control is defocused. + anchors.fill: parent + propagateComposedEvents: true + onPressed: { + toggleCollapsed(); + mouse.accepted = false; + } + } + } + + LinearGradient { + id: bottomBar + visible: false + width: 480 + height: visible ? 4 : 0 + x: -hifi.dimensions.contentMargin.x + anchors.top: heading.bottom + start: Qt.point(0, 0) + end: Qt.point(0, 4) + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.darkGray } + GradientStop { position: 1.0; color: hifi.colors.baseGray } // Equivalent of darkGray0 over baseGray background. + } + cached: true + } + } +} diff --git a/interface/resources/qml/controlsUit/TabletHeader.qml b/interface/resources/qml/controlsUit/TabletHeader.qml new file mode 100644 index 0000000000..f626700742 --- /dev/null +++ b/interface/resources/qml/controlsUit/TabletHeader.qml @@ -0,0 +1,34 @@ +// +// TabletHeader.qml +// +// Created by David Rowe on 11 Mar 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../stylesUit" + +Rectangle { + + property string title: "" + + HifiConstants { id: hifi } + + height: hifi.dimensions.tabletMenuHeader + z: 100 + + color: hifi.colors.darkGray + + RalewayBold { + text: title + size: 26 + color: hifi.colors.white + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: hifi.dimensions.contentMargin.x + } +} diff --git a/interface/resources/qml/controlsUit/TextAction.qml b/interface/resources/qml/controlsUit/TextAction.qml new file mode 100644 index 0000000000..a0a1bb7d07 --- /dev/null +++ b/interface/resources/qml/controlsUit/TextAction.qml @@ -0,0 +1,65 @@ +// +// TextField.qml +// +// Created by David Rowe on 21 Apr 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +import "../stylesUit" +import "." as HifiControls + +Item { + property string icon: "" + property int iconSize: 30 + property string text: "" + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + signal clicked() + + height: Math.max(glyph.visible ? glyph.height - 4 : 0, string.visible ? string.height : 0) + width: glyph.width + string.anchors.leftMargin + string.width + + HiFiGlyphs { + id: glyph + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: -2 + text: parent.icon + size: parent.iconSize + color: isLightColorScheme + ? (mouseArea.containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.lightGray) + : (mouseArea.containsMouse ? hifi.colors.faintGray : hifi.colors.lightGrayText) + visible: text !== "" + width: visible ? implicitWidth : 0 + } + + RalewaySemiBold { + id: string + anchors { + left: glyph.visible ? glyph.right : parent.left + leftMargin: visible && glyph.visible ? hifi.dimensions.contentSpacing.x : 0 + verticalCenter: glyph.visible ? glyph.verticalCenter : undefined + } + text: parent.text + size: hifi.fontSizes.inputLabel + color: isLightColorScheme + ? (mouseArea.containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.lightGray) + : (mouseArea.containsMouse ? hifi.colors.faintGray : hifi.colors.lightGrayText) + font.underline: true; + visible: text !== "" + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: parent.clicked() + } +} diff --git a/interface/resources/qml/controlsUit/TextEdit.qml b/interface/resources/qml/controlsUit/TextEdit.qml new file mode 100644 index 0000000000..7446c5040f --- /dev/null +++ b/interface/resources/qml/controlsUit/TextEdit.qml @@ -0,0 +1,23 @@ +// +// TextEdit.qml +// +// Created by Bradley Austin Davis on 24 Apr 2015 +// Copyright 2015 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 +// + +import QtQuick 2.5 +import "../stylesUit" + +TextEdit { + + property real size: 32 + + font.family: "Raleway" + font.weight: Font.DemiBold + font.pointSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft +} diff --git a/interface/resources/qml/controlsUit/TextField.qml b/interface/resources/qml/controlsUit/TextField.qml new file mode 100644 index 0000000000..d78f3a1340 --- /dev/null +++ b/interface/resources/qml/controlsUit/TextField.qml @@ -0,0 +1,180 @@ +// +// TextField.qml +// +// Created by David Rowe on 17 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../stylesUit" +import "." as HifiControls + +TextField { + id: textField + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray + property bool isSearchField: false + property string label: "" + property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0) + property bool hasDefocusedBorder: true; + property bool hasRoundedBorder: false + property int roundedBorderRadius: 4 + property bool error: false; + property bool hasClearButton: false; + property string leftPermanentGlyph: ""; + property string centerPlaceholderGlyph: ""; + + placeholderText: textField.placeholderText + + font.family: "Fira Sans" + font.pixelSize: hifi.fontSizes.textFieldInput + height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered. + property alias textFieldLabel: textFieldLabel + + y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0 + + // workaround for https://bugreports.qt.io/browse/QTBUG-49297 + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Return: + case Qt.Key_Enter: + event.accepted = true; + + // emit accepted signal manually + if (acceptableInput) { + accepted(); + } + } + } + + style: TextFieldStyle { + id: style; + textColor: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.lightGrayText + } + } + } + background: Rectangle { + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.textFieldLightBackground + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.faintGray50 + } + } else { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.baseGrayShadow + } + } + } + border.color: textField.error ? hifi.colors.redHighlight : + (textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color)) + border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 + radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0) + + HiFiGlyphs { + text: textField.leftPermanentGlyph; + color: textColor; + size: hifi.fontSizes.textFieldSearchIcon; + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + anchors.leftMargin: hifi.dimensions.textPadding - 2; + visible: text; + } + + HiFiGlyphs { + text: textField.centerPlaceholderGlyph; + color: textColor; + size: parent.height; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.verticalCenter: parent.verticalCenter; + visible: text && !textField.focus && textField.text === ""; + } + + HiFiGlyphs { + text: hifi.glyphs.search + color: textColor + size: hifi.fontSizes.textFieldSearchIcon + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: hifi.dimensions.textPadding - 2 + visible: isSearchField + } + + HiFiGlyphs { + text: hifi.glyphs.error + color: textColor + size: 40 + anchors.right: parent.right + anchors.rightMargin: hifi.dimensions.textPadding - 2 + anchors.verticalCenter: parent.verticalCenter + visible: hasClearButton && textField.text !== ""; + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.text = ""; + } + } + } + } + placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding + padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding + } + + HifiControls.Label { + id: textFieldLabel + text: textField.label + colorScheme: textField.colorScheme + anchors.left: parent.left + + Binding on anchors.right { + when: textField.right + value: textField.right + } + Binding on wrapMode { + when: textField.right + value: Text.WordWrap + } + + anchors.bottom: parent.top + anchors.bottomMargin: 3 + visible: label != "" + } +} diff --git a/interface/resources/qml/controlsUit/ToolTip.qml b/interface/resources/qml/controlsUit/ToolTip.qml new file mode 100644 index 0000000000..4fe36adcd5 --- /dev/null +++ b/interface/resources/qml/controlsUit/ToolTip.qml @@ -0,0 +1,51 @@ +// +// ToolTip.qml +// +// Created by Clement on 9/12/17 +// Copyright 2017 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 +// + +import QtQuick 2.5 + +Item { + property string toolTip + property bool showToolTip: false + + Rectangle { + id: toolTipRectangle + anchors.right: parent.right + + width: toolTipText.width + 4 + height: toolTipText.height + 4 + opacity: (toolTip != "" && showToolTip) ? 1 : 0 + color: "#ffffaa" + border.color: "#0a0a0a" + Text { + id: toolTipText + text: toolTip + color: "black" + anchors.centerIn: parent + } + Behavior on opacity { + PropertyAnimation { + easing.type: Easing.InOutQuad + duration: 250 + } + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + onEntered: showTimer.start() + onExited: { showToolTip = false; showTimer.stop(); } + hoverEnabled: true + } + Timer { + id: showTimer + interval: 250 + onTriggered: { showToolTip = true; } + } +} \ No newline at end of file diff --git a/interface/resources/qml/controlsUit/Tree.qml b/interface/resources/qml/controlsUit/Tree.qml new file mode 100644 index 0000000000..f2c49095b1 --- /dev/null +++ b/interface/resources/qml/controlsUit/Tree.qml @@ -0,0 +1,205 @@ +// +// Tree.qml +// +// Created by David Rowe on 17 Feb 2016 +// Copyright 2016 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 +// + +import QtQml.Models 2.2 +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.2 as QQC2 + + +import "../stylesUit" + +TreeView { + id: treeView + + property var treeModel: ListModel { } + property bool centerHeaderText: false + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + property var modifyEl: function(index, data) { return false; } + + model: treeModel + selection: ItemSelectionModel { + id: selectionModel + model: treeModel + } + + anchors { left: parent.left; right: parent.right } + + headerVisible: false + + Component.onCompleted: { + if (flickableItem !== null && flickableItem !== undefined) { + treeView.flickableItem.QQC2.ScrollBar.vertical = scrollbar + } + } + + QQC2.ScrollBar { + id: scrollbar + parent: treeView.flickableItem + policy: QQC2.ScrollBar.AsNeeded + orientation: Qt.Vertical + visible: size < 1.0 + topPadding: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight + 1 : 1 + anchors.top: treeView.top + anchors.left: treeView.right + anchors.bottom: treeView.bottom + + background: Item { + implicitWidth: hifi.dimensions.scrollbarBackgroundWidth + Rectangle { + anchors { + fill: parent; + topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight: 0 + } + color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight + : hifi.colors.tableScrollBackgroundDark + } + } + + contentItem: Item { + implicitWidth: hifi.dimensions.scrollbarHandleWidth + Rectangle { + anchors.fill: parent + radius: (width - 4)/2 + color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark + } + } + } + + // Use rectangle to draw border with rounded corners. + frameVisible: false + Rectangle { + color: "#00000000" + anchors.fill: parent + radius: hifi.dimensions.borderRadius + border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + border.width: 2 + anchors.margins: -2 + } + anchors.margins: 2 // Shrink TreeView to lie within border. + + backgroundVisible: true + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + style: TreeViewStyle { + // Needed in order for rows to keep displaying rows after end of table entries. + backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven + alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + headerDelegate: Rectangle { + height: hifi.dimensions.tableHeaderHeight + color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + + RalewayRegular { + id: titleText + text: styleData.value + size: hifi.fontSizes.tableHeading + font.capitalization: Font.AllUppercase + color: hifi.colors.baseGrayHighlight + horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) + elide: Text.ElideRight + anchors { + left: parent.left + leftMargin: hifi.dimensions.tablePadding + right: sortIndicatorVisible && sortIndicatorColumn === styleData.column ? titleSort.left : parent.right + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent.verticalCenter + } + } + + HiFiGlyphs { + id: titleSort + text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn + color: isLightColorScheme ? hifi.colors.darkGray : hifi.colors.baseGrayHighlight + opacity: 0.6; + size: hifi.fontSizes.tableHeadingIcon + anchors { + right: parent.right + verticalCenter: titleText.verticalCenter + } + visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column + } + + Rectangle { + width: 1 + anchors { + left: parent.left + top: parent.top + topMargin: 1 + bottom: parent.bottom + bottomMargin: 2 + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + visible: styleData.column > 0 + } + + Rectangle { + height: 1 + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + } + } + + branchDelegate: HiFiGlyphs { + text: styleData.isExpanded ? hifi.glyphs.caratDn : hifi.glyphs.caratR + size: hifi.fontSizes.carat + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + left: parent ? parent.left : undefined + leftMargin: hifi.dimensions.tablePadding / 2 + } + } + } + + rowDelegate: Rectangle { + height: hifi.dimensions.tableRowHeight + color: styleData.selected + ? hifi.colors.primaryHighlight + : treeView.isLightColorScheme + ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) + : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) + } + + itemDelegate: FiraSansSemiBold { + anchors { + left: parent ? parent.left : undefined + leftMargin: (2 + styleData.depth) * hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent ? parent.verticalCenter : undefined + } + + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight + } + + Item { + id: unfocusHelper + visible: false + } + + onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index) +} diff --git a/interface/resources/qml/controlsUit/VerticalSpacer.qml b/interface/resources/qml/controlsUit/VerticalSpacer.qml new file mode 100644 index 0000000000..4c93aa1801 --- /dev/null +++ b/interface/resources/qml/controlsUit/VerticalSpacer.qml @@ -0,0 +1,21 @@ +// +// VerticalSpacer.qml +// +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +import "../stylesUit" + +Item { + id: root + property alias size: root.height + + width: 1 // Must be non-zero + height: hifi.dimensions.controlInterlineHeight +} diff --git a/interface/resources/qml/controlsUit/WebGlyphButton.qml b/interface/resources/qml/controlsUit/WebGlyphButton.qml new file mode 100644 index 0000000000..7739ecd5e7 --- /dev/null +++ b/interface/resources/qml/controlsUit/WebGlyphButton.qml @@ -0,0 +1,40 @@ +// +// GlyphButton.qml +// +// Created by Vlad Stelmahovsky on 2017-06-21 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 2.2 as Original + +import "../stylesUit" + +Original.Button { + id: control + + property int colorScheme: hifi.colorSchemes.light + property string glyph: "" + property int size: 32 + //colors + readonly property color normalColor: "#AFAFAF" + readonly property color hoverColor: "#00B4EF" + readonly property color clickedColor: "#FFFFFF" + readonly property color disabledColor: "#575757" + + background: Item {} + + contentItem: HiFiGlyphs { + color: control.enabled ? (control.pressed ? control.clickedColor : + (control.hovered ? control.hoverColor : control.normalColor)) : + control.disabledColor + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.glyph + size: control.size + } +} + diff --git a/interface/resources/qml/controlsUit/WebSpinner.qml b/interface/resources/qml/controlsUit/WebSpinner.qml new file mode 100644 index 0000000000..e8e01c4865 --- /dev/null +++ b/interface/resources/qml/controlsUit/WebSpinner.qml @@ -0,0 +1,24 @@ +// +// WebSpinner.qml +// +// Created by David Rowe on 23 May 2017 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import QtWebEngine 1.5 + +AnimatedImage { + property WebEngineView webview: parent + source: "../../icons/loader-snake-64-w.gif" + visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString()) + playing: visible + z: 10000 + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } +} diff --git a/interface/resources/qml/controlsUit/WebView.qml b/interface/resources/qml/controlsUit/WebView.qml new file mode 100644 index 0000000000..2895f36944 --- /dev/null +++ b/interface/resources/qml/controlsUit/WebView.qml @@ -0,0 +1,21 @@ +// +// WebView.qml +// +// Created by Bradley Austin Davis on 12 Jan 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +BaseWebView { + onNewViewRequested: { + // Load dialog via OffscreenUi so that JavaScript EventBridge is available. + var browser = OffscreenUi.load("Browser.qml"); + request.openIn(browser.webView); + browser.webView.forceActiveFocus(); + } +} diff --git a/interface/resources/qml/controls-uit/qmldir b/interface/resources/qml/controlsUit/qmldir similarity index 94% rename from interface/resources/qml/controls-uit/qmldir rename to interface/resources/qml/controlsUit/qmldir index 989115b8d2..d0577f5575 100644 --- a/interface/resources/qml/controls-uit/qmldir +++ b/interface/resources/qml/controlsUit/qmldir @@ -6,6 +6,7 @@ CheckBox 1.0 CheckBox.qml CheckBoxQQC2 1.0 CheckBoxQQC2.qml ComboBox 1.0 ComboBox.qml ContentSection 1.0 ContentSection.qml +FilterBar 1.0 FilterBar.qml GlyphButton 1.0 GlyphButton.qml HorizontalRule 1.0 HorizontalRule.qml HorizontalSpacer 1.0 HorizontalSpacer.qml @@ -15,6 +16,7 @@ Keyboard 1.0 Keyboard.qml Label 1.0 Label.qml QueuedButton 1.0 QueuedButton.qml RadioButton 1.0 RadioButton.qml +ScrollBar 1.0 ScrollBar.qml Separator 1.0 Separator.qml Slider 1.0 Slider.qml SpinBox 1.0 SpinBox.qml diff --git a/interface/resources/qml/dialogs/AssetDialog.qml b/interface/resources/qml/dialogs/AssetDialog.qml index e8d28e9b37..b8eaab0b8d 100644 --- a/interface/resources/qml/dialogs/AssetDialog.qml +++ b/interface/resources/qml/dialogs/AssetDialog.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import Qt.labs.settings 1.0 -import "../styles-uit" +import stylesUit 1.0 import "../windows" import "assetDialog" diff --git a/interface/resources/qml/dialogs/CustomQueryDialog.qml b/interface/resources/qml/dialogs/CustomQueryDialog.qml index 0c86b93c4b..026068eee1 100644 --- a/interface/resources/qml/dialogs/CustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/CustomQueryDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.7; import QtQuick.Dialogs 1.2 as OriginalDialogs; import QtQuick.Controls 2.3 -import "../controls-uit"; -import "../styles-uit"; +import controlsUit 1.0 +import stylesUit 1.0 import "../windows"; ModalWindow { diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 6651af0db3..b7340575dd 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -16,8 +16,8 @@ import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.3 import ".." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "fileDialog" diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index b5ac6cab72..9428e3ab6e 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "messageDialog" diff --git a/interface/resources/qml/dialogs/PreferencesDialog.qml b/interface/resources/qml/dialogs/PreferencesDialog.qml index fffd0e2ed9..9df1d0b963 100644 --- a/interface/resources/qml/dialogs/PreferencesDialog.qml +++ b/interface/resources/qml/dialogs/PreferencesDialog.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../controls-uit" as HifiControls -import "../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 import "../windows" import "preferences" diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 41ee30e6d5..9cfb3011bd 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" ModalWindow { diff --git a/interface/resources/qml/dialogs/TabletAssetDialog.qml b/interface/resources/qml/dialogs/TabletAssetDialog.qml index 897378e40c..b3bd45f972 100644 --- a/interface/resources/qml/dialogs/TabletAssetDialog.qml +++ b/interface/resources/qml/dialogs/TabletAssetDialog.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 import "../windows" import "assetDialog" diff --git a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml index 81a2c5c1e0..c7772984ab 100644 --- a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Dialogs 1.2 as OriginalDialogs import QtQuick.Controls 2.3 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" TabletModalWindow { diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index 6848c230e3..3be6e30dd0 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -16,8 +16,8 @@ import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.3 import ".." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "fileDialog" diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index c85b2b2ba0..dad2bb91aa 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.5 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "../LoginDialog" @@ -95,6 +95,18 @@ TabletModalWindow { } } + Timer { + id: keyboardTimer + repeat: false + interval: 200 + + onTriggered: { + if (MenuInterface.isOptionChecked("Use 3D Keyboard")) { + KeyboardScriptingInterface.raised = true; + } + } + } + TabletModalFrame { id: mfRoot @@ -127,6 +139,14 @@ TabletModalWindow { } } + Component.onDestruction: { + loginKeyboard.raised = false; + } + + Component.onCompleted: { + keyboardTimer.start(); + } + Keyboard { id: loginKeyboard raised: root.keyboardEnabled && root.keyboardRaised diff --git a/interface/resources/qml/dialogs/TabletMessageBox.qml b/interface/resources/qml/dialogs/TabletMessageBox.qml index fabe0dd247..1e6f0734ad 100644 --- a/interface/resources/qml/dialogs/TabletMessageBox.qml +++ b/interface/resources/qml/dialogs/TabletMessageBox.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "messageDialog" diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index 5746a3d67c..8f63730b8e 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Dialogs 1.2 as OriginalDialogs import QtQuick.Controls 2.3 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" TabletModalWindow { diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml index c3e842bc2f..da976ef3e1 100644 --- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml +++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 import QtQuick.Controls 1.5 as QQC1 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../fileDialog" diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml index 50a10974b5..6c042b5598 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 ComboBox { id: root diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index 8411980db7..f5715fa2c2 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 -import "../../controls-uit" +import controlsUit 1.0 Button { property var dialog; diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index 0efc3776b3..9505e70530 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 import "../../hifi/tablet/tabletWindows/preferences" Preference { diff --git a/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml b/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml index 2cf50891c9..6059f8ff1c 100644 --- a/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml +++ b/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml index 454a9124ae..09c5b4329d 100644 --- a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import TabletScriptingInterface 1.0 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml index e2172d8eda..f6f840bbe8 100644 --- a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import TabletScriptingInterface 1.0 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml b/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml index 3b3efaf520..98cb397976 100644 --- a/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../controls-uit" as HiFiControls -import "../../styles-uit" +import controlsUit 1.0 as HiFiControls +import stylesUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/EditablePreference.qml b/interface/resources/qml/dialogs/preferences/EditablePreference.qml index 8acf8e1f76..e0c79ebba0 100644 --- a/interface/resources/qml/dialogs/preferences/EditablePreference.qml +++ b/interface/resources/qml/dialogs/preferences/EditablePreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index cfc2e94ed9..f963003c59 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml b/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml index 103904a666..0a09d8d609 100644 --- a/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml +++ b/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/Section.qml b/interface/resources/qml/dialogs/preferences/Section.qml index c2c6583b7e..a9b755ad83 100644 --- a/interface/resources/qml/dialogs/preferences/Section.qml +++ b/interface/resources/qml/dialogs/preferences/Section.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import Hifi 1.0 -import "../../controls-uit" as HiFiControls -import "../../styles-uit" +import controlsUit 1.0 as HiFiControls +import stylesUit 1.0 import "." Preference { diff --git a/interface/resources/qml/dialogs/preferences/SliderPreference.qml b/interface/resources/qml/dialogs/preferences/SliderPreference.qml index 2bdda09fc3..c8a2aae158 100644 --- a/interface/resources/qml/dialogs/preferences/SliderPreference.qml +++ b/interface/resources/qml/dialogs/preferences/SliderPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml b/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml index b2c334b674..1b080c2759 100644 --- a/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml b/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml index 126e62fc30..cbc804d9d7 100644 --- a/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml +++ b/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/hifi/+android/ActionBar.qml b/interface/resources/qml/hifi/+android/ActionBar.qml index d487901d6f..3c58156f30 100644 --- a/interface/resources/qml/hifi/+android/ActionBar.qml +++ b/interface/resources/qml/hifi/+android/ActionBar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/AudioBar.qml b/interface/resources/qml/hifi/+android/AudioBar.qml index 6cc17fccf7..912572fdf8 100644 --- a/interface/resources/qml/hifi/+android/AudioBar.qml +++ b/interface/resources/qml/hifi/+android/AudioBar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/AvatarOption.qml b/interface/resources/qml/hifi/+android/AvatarOption.qml index 85d7e52eb2..7eba3c2a67 100644 --- a/interface/resources/qml/hifi/+android/AvatarOption.qml +++ b/interface/resources/qml/hifi/+android/AvatarOption.qml @@ -11,7 +11,7 @@ import QtQuick.Layouts 1.3 import QtQuick 2.5 -import "../controls-uit" as HifiControlsUit +import controlsUit 1.0 as HifiControlsUit ColumnLayout { id: itemRoot diff --git a/interface/resources/qml/hifi/+android/StatsBar.qml b/interface/resources/qml/hifi/+android/StatsBar.qml index aee438b44f..64e93b4a08 100644 --- a/interface/resources/qml/hifi/+android/StatsBar.qml +++ b/interface/resources/qml/hifi/+android/StatsBar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/WindowHeader.qml b/interface/resources/qml/hifi/+android/WindowHeader.qml index 4ec0a0c6e6..5316fc4786 100644 --- a/interface/resources/qml/hifi/+android/WindowHeader.qml +++ b/interface/resources/qml/hifi/+android/WindowHeader.qml @@ -16,8 +16,8 @@ import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import "." import "../styles" as HifiStyles -import "../styles-uit" -import "../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/bottomHudOptions.qml b/interface/resources/qml/hifi/+android/bottomHudOptions.qml index 22beccf531..6b830d94c2 100644 --- a/interface/resources/qml/hifi/+android/bottomHudOptions.qml +++ b/interface/resources/qml/hifi/+android/bottomHudOptions.qml @@ -16,8 +16,8 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import "../../styles" as HifiStyles -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." import "." diff --git a/interface/resources/qml/hifi/+android/modesbar.qml b/interface/resources/qml/hifi/+android/modesbar.qml index 994bf1efe4..1bf04fb8d9 100644 --- a/interface/resources/qml/hifi/+android/modesbar.qml +++ b/interface/resources/qml/hifi/+android/modesbar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 1a7f5bac40..ad337a6361 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -14,8 +14,8 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../windows" as Windows import "../dialogs" diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index aea5931627..9635681c34 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtQml.Models 2.1 import QtGraphicalEffects 1.0 -import "../controls-uit" as HifiControls -import "../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 import "avatarapp" Rectangle { @@ -19,7 +19,7 @@ Rectangle { HifiControls.Keyboard { id: keyboard z: 1000 - raised: parent.keyboardEnabled && parent.keyboardRaised + raised: parent.keyboardEnabled && parent.keyboardRaised && HMD.active numeric: parent.punctuationMode anchors { left: parent.left @@ -204,7 +204,8 @@ Rectangle { property bool isInManageState: false - Component.onCompleted: { + Component.onDestruction: { + keyboard.raised = false; } AvatarAppStyle { @@ -235,6 +236,8 @@ Rectangle { avatarIconVisible: mainPageVisible settingsButtonVisible: mainPageVisible onSettingsClicked: { + displayNameInput.focus = false; + root.keyboardRaised = false; settings.open(currentAvatarSettings, currentAvatar.avatarScale); } } @@ -344,6 +347,10 @@ Rectangle { emitSendToScript({'method' : 'changeDisplayName', 'displayName' : text}) focus = false; } + + onFocusChanged: { + root.keyboardRaised = focus; + } } ShadowImage { diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 83bf1e2c54..7f29324416 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -17,7 +17,7 @@ import QtGraphicalEffects 1.0 import TabletScriptingInterface 1.0 import "toolbars" -import "../styles-uit" +import stylesUit 1.0 Item { id: root; diff --git a/interface/resources/qml/hifi/ComboDialog.qml b/interface/resources/qml/hifi/ComboDialog.qml index e5dc8a9c1a..74d9c1019b 100644 --- a/interface/resources/qml/hifi/ComboDialog.qml +++ b/interface/resources/qml/hifi/ComboDialog.qml @@ -10,8 +10,8 @@ // import QtQuick 2.5 -import "../styles-uit" -import "../controls-uit" +import stylesUit 1.0 +import controlsUit 1.0 Item { property var dialogTitleText : ""; diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 4d342fe775..511d9377e5 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -8,7 +8,7 @@ import "../desktop" as OriginalDesktop import ".." import "." import "./toolbars" -import "../controls-uit" +import controlsUit 1.0 OriginalDesktop.Desktop { id: desktop diff --git a/interface/resources/qml/hifi/DesktopLetterboxMessage.qml b/interface/resources/qml/hifi/DesktopLetterboxMessage.qml index 9e9dcc75b2..048add24e5 100644 --- a/interface/resources/qml/hifi/DesktopLetterboxMessage.qml +++ b/interface/resources/qml/hifi/DesktopLetterboxMessage.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 Item { property alias text: popupText.text diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index 346481fe1f..4cfd4804b3 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -15,7 +15,7 @@ import Hifi 1.0 import QtQuick 2.5 import QtGraphicalEffects 1.0 import "toolbars" -import "../styles-uit" +import stylesUit 1.0 import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. Column { diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 8a18d88842..68bebdd041 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 Item { property alias text: popupText.text diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index dfa6555150..242ca5ab57 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -13,8 +13,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "toolbars" // references Users, UserActivityLogger, MyAvatar, Vec3, Quat, AddressManager, Account from root context diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 1384cb8711..368beaab47 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -15,8 +15,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. diff --git a/interface/resources/qml/hifi/SkyboxChanger.qml b/interface/resources/qml/hifi/SkyboxChanger.qml index f0c97a11a3..a66fc38415 100644 --- a/interface/resources/qml/hifi/SkyboxChanger.qml +++ b/interface/resources/qml/hifi/SkyboxChanger.qml @@ -10,8 +10,8 @@ // import QtQuick 2.5 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import QtQuick.Controls 2.2 Item { diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 4bf80e410b..09b722b906 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 -import "../styles-uit" -import "../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../controls" as HifiControls // references HMD, XXX from root context diff --git a/interface/resources/qml/hifi/TabletTextButton.qml b/interface/resources/qml/hifi/TabletTextButton.qml index e5ff1d381d..6c9e0331df 100644 --- a/interface/resources/qml/hifi/TabletTextButton.qml +++ b/interface/resources/qml/hifi/TabletTextButton.qml @@ -10,7 +10,7 @@ import Hifi 1.0 import QtQuick 2.4 -import "../styles-uit" +import stylesUit 1.0 Rectangle { property alias text: label.text diff --git a/interface/resources/qml/hifi/TextButton.qml b/interface/resources/qml/hifi/TextButton.qml index 02e49d86e4..61588a9603 100644 --- a/interface/resources/qml/hifi/TextButton.qml +++ b/interface/resources/qml/hifi/TextButton.qml @@ -9,7 +9,7 @@ // import Hifi 1.0 import QtQuick 2.4 -import "../styles-uit" +import stylesUit 1.0 Rectangle { property alias text: label.text; diff --git a/interface/resources/qml/hifi/WebBrowser.qml b/interface/resources/qml/hifi/WebBrowser.qml index ab93752d92..c05de26471 100644 --- a/interface/resources/qml/hifi/WebBrowser.qml +++ b/interface/resources/qml/hifi/WebBrowser.qml @@ -18,8 +18,8 @@ import QtGraphicalEffects 1.0 import QtWebEngine 1.5 import QtWebChannel 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../windows" import "../controls" diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index f4a708567a..c8dd83cd62 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -15,8 +15,8 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "./" as AudioControls diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml index 3a3ed90f5e..32331ccb6e 100644 --- a/interface/resources/qml/hifi/audio/AudioTabButton.qml +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabButton { id: control diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 3a954d4004..5ab62a5091 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -11,7 +11,7 @@ import QtQuick 2.7 -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls HifiControls.CheckBoxQQC2 { color: "white" diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml index 2b9599a3cc..cfe55af9c4 100644 --- a/interface/resources/qml/hifi/audio/PlaySampleSound.qml +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -13,8 +13,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls RowLayout { property var sound: null; diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 5fff14e4a1..b707d41025 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -1,9 +1,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit -import "../../controls" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit Rectangle { id: root; @@ -49,16 +48,32 @@ Rectangle { refresh(avatar); } + function extractTitleFromUrl(url) { + for (var j = (url.length - 1); j >= 0; --j) { + if (url[j] === '/') { + return url.substring(j + 1); + } + } + return url; + } + function refresh(avatar) { wearablesCombobox.model.clear(); wearablesCombobox.currentIndex = -1; for (var i = 0; i < avatar.wearables.count; ++i) { var wearable = avatar.wearables.get(i).properties; - for (var j = (wearable.modelURL.length - 1); j >= 0; --j) { - if (wearable.modelURL[j] === '/') { - wearable.text = wearable.modelURL.substring(j + 1); - break; + if (wearable.modelURL) { + wearable.text = extractTitleFromUrl(wearable.modelURL); + } else if (wearable.materialURL) { + var materialUrlOrJson = ''; + if (!wearable.materialURL.startsWith('materialData')) { + materialUrlOrJson = extractTitleFromUrl(wearable.materialURL); + } else if (wearable.materialData) { + materialUrlOrJson = JSON.stringify(JSON.parse(wearable.materialData)) + } + if(materialUrlOrJson) { + wearable.text = 'Material: ' + materialUrlOrJson; } } wearablesCombobox.model.append(wearable); diff --git a/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml b/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml index 9d9db010fb..d3c9cd1d5f 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml @@ -1,6 +1,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" +import stylesUit 1.0 ShadowRectangle { id: header diff --git a/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml b/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml index f66c7121cb..36cb4b1080 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 import QtQuick.Window 2.2 -import "../../styles-uit" +import stylesUit 1.0 QtObject { readonly property QtObject colors: QtObject { diff --git a/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml b/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml index cb73e9fe71..8b28d4c66b 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml @@ -1,6 +1,6 @@ import QtQuick 2.9 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 ShadowRectangle { property int wearablesCount: 0 diff --git a/interface/resources/qml/hifi/avatarapp/BlueButton.qml b/interface/resources/qml/hifi/avatarapp/BlueButton.qml index e668951517..0cc84d5ba0 100644 --- a/interface/resources/qml/hifi/avatarapp/BlueButton.qml +++ b/interface/resources/qml/hifi/avatarapp/BlueButton.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit HifiControlsUit.Button { HifiConstants { diff --git a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml index 1387c0791a..780981a5a3 100644 --- a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml +++ b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml @@ -1,7 +1,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/InputField.qml b/interface/resources/qml/hifi/avatarapp/InputField.qml index 905518ef0f..2020d56c96 100644 --- a/interface/resources/qml/hifi/avatarapp/InputField.qml +++ b/interface/resources/qml/hifi/avatarapp/InputField.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit TextField { id: textField diff --git a/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml b/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml index 4b868b47ce..6c2101498c 100644 --- a/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml +++ b/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml @@ -1,5 +1,5 @@ -import "../../controls-uit" as HifiControlsUit -import "../../styles-uit" +import controlsUit 1.0 as HifiControlsUit +import stylesUit 1.0 import QtQuick 2.0 import QtQuick.Controls 2.2 diff --git a/interface/resources/qml/hifi/avatarapp/MessageBox.qml b/interface/resources/qml/hifi/avatarapp/MessageBox.qml index f111303214..eb28745b1a 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBox.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBox.qml @@ -1,7 +1,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index b7782c697d..89a8eff025 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -63,8 +63,8 @@ MessageBox { popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; popup.button1text = 'CANCEL' popup.titleText = 'Get Wearables' - popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearable from My Purchases.' + '
' + '
' + + popup.bodyText = 'Get wearables from Marketplace.' + '
' + + 'Wear wearable from Inventory.' + '
' + '
' + 'Visit “AvatarIsland” to get wearables' popup.imageSource = getWearablesUrl; @@ -89,7 +89,7 @@ MessageBox { function showDeleteFavorite(favoriteName, callback) { popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) - popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' + popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from Inventory.' popup.imageSource = null; popup.button1text = 'CANCEL' popup.button2text = 'DELETE' @@ -128,8 +128,8 @@ MessageBox { popup.button1text = 'CANCEL' popup.titleText = 'Get Avatars' - popup.bodyText = 'Buy avatars from Marketplace.' + '
' + - 'Wear avatars in My Purchases.' + '
' + '
' + + popup.bodyText = 'Get avatars from Marketplace.' + '
' + + 'Wear avatars in Inventory.' + '
' + '
' + 'Visit “BodyMart” to get free avatars.' popup.imageSource = getAvatarsUrl; diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index 71bfbb084d..cd892c17b1 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -2,8 +2,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Rectangle { @@ -14,6 +14,22 @@ Rectangle { signal scaleChanged(real scale); + property bool keyboardEnabled: true + property bool keyboardRaised: false + property bool punctuationMode: false + + HifiControlsUit.Keyboard { + id: keyboard + z: 1000 + raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + property alias onSaveClicked: dialogButtons.onYesClicked property alias onCancelClicked: dialogButtons.onNoClicked @@ -31,7 +47,7 @@ Rectangle { scaleSlider.notify = false; scaleSlider.value = Math.round(avatarScale * 10); - scaleSlider.notify = true;; + scaleSlider.notify = true; if (settings.dominantHand === 'left') { leftHandRadioButton.checked = true; @@ -314,6 +330,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right placeholderText: 'user\\file\\dir' + + onFocusChanged: { + keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus); + } } } @@ -340,6 +360,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-' + + onFocusChanged: { + keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus); + } } } diff --git a/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml b/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml index c2d84bb371..a2c84fad47 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml @@ -1,4 +1,4 @@ -import "../../styles-uit" +import stylesUit 1.0 import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/ShadowImage.qml b/interface/resources/qml/hifi/avatarapp/ShadowImage.qml index 3995446e49..51e1043702 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowImage.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowImage.qml @@ -1,4 +1,4 @@ -import "../../styles-uit" +import stylesUit 1.0 import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml b/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml index 741fce3d8d..3968fcb1ff 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml @@ -1,4 +1,4 @@ -import "../../styles-uit" +import stylesUit 1.0 import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/SquareLabel.qml b/interface/resources/qml/hifi/avatarapp/SquareLabel.qml index e2c456ec04..69aff47373 100644 --- a/interface/resources/qml/hifi/avatarapp/SquareLabel.qml +++ b/interface/resources/qml/hifi/avatarapp/SquareLabel.qml @@ -1,5 +1,5 @@ -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/Vector3.qml b/interface/resources/qml/hifi/avatarapp/Vector3.qml index d77665f992..698123104f 100644 --- a/interface/resources/qml/hifi/avatarapp/Vector3.qml +++ b/interface/resources/qml/hifi/avatarapp/Vector3.qml @@ -1,7 +1,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Row { diff --git a/interface/resources/qml/hifi/avatarapp/WhiteButton.qml b/interface/resources/qml/hifi/avatarapp/WhiteButton.qml index dc729ae097..d0a4a152db 100644 --- a/interface/resources/qml/hifi/avatarapp/WhiteButton.qml +++ b/interface/resources/qml/hifi/avatarapp/WhiteButton.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit HifiControlsUit.Button { HifiConstants { diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b13f23f17d..62c3675ba8 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet import "../common" as HifiCommerceCommon @@ -55,6 +55,7 @@ Rectangle { property bool isInstalled; property bool isUpdating; property string baseAppURL; + property int currentUpdatesPage: 1; // Style color: hifi.colors.white; Connections { @@ -156,8 +157,14 @@ Rectangle { break; } } - root.availableUpdatesReceived = true; - refreshBuyUI(); + + if (result.data.updates.length === 0 || root.isUpdating) { + root.availableUpdatesReceived = true; + refreshBuyUI(); + } else { + root.currentUpdatesPage++; + Commerce.getAvailableUpdates(root.itemId, currentUpdatesPage) + } } } @@ -176,6 +183,7 @@ Rectangle { root.ownershipStatusReceived = false; Commerce.alreadyOwned(root.itemId); root.availableUpdatesReceived = false; + root.currentUpdatesPage = 1; Commerce.getAvailableUpdates(root.itemId); itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg"; } @@ -240,11 +248,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - lightboxPopup.visible = false; - sendToScript({method: 'checkout_openWallet'}); - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -383,7 +386,7 @@ Rectangle { anchors.leftMargin: 16; width: paintedWidth; height: paintedHeight; - text: "Review Purchase:"; + text: "Review:"; color: hifi.colors.black; size: 28; } @@ -448,7 +451,7 @@ Rectangle { // "HFC" balance label HiFiGlyphs { id: itemPriceTextLabel; - visible: !(root.isUpdating && root.itemEdition > 0); + visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0); text: hifi.glyphs.hfc; // Size size: 30; @@ -464,7 +467,7 @@ Rectangle { } FiraSansSemiBold { id: itemPriceText; - text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : root.itemPrice); + text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE")); // Text size size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26; // Anchors @@ -559,7 +562,7 @@ Rectangle { } } - // "View in My Purchases" button + // "View in Inventory" button HifiControlsUit.Button { id: viewInMyPurchasesButton; visible: false; @@ -570,7 +573,7 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN MY PURCHASES"; + text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN YOUR INVENTORY"; onClicked: { if (root.isUpdating) { sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); @@ -594,7 +597,7 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? - ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item")); + ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : "--") : "Get Item")); onClicked: { if (root.isUpdating && root.itemEdition > 0) { // If we're updating an app, the existing app needs to be uninstalled. @@ -608,9 +611,9 @@ Rectangle { } else if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { - lightboxPopup.titleText = "Purchase Content Set"; + lightboxPopup.titleText = "Get Content Set"; lightboxPopup.bodyText = "You will not be able to replace this domain's content with " + root.itemName + - " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to purchase this content set?"; + " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to get this content set?"; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { lightboxPopup.visible = false; @@ -694,7 +697,7 @@ Rectangle { id: completeText2; text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + ' ' + root.itemName + '' + - " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; + " has been added to your Inventory."; // Text size size: 18; // Anchors @@ -833,7 +836,7 @@ Rectangle { } lightboxPopup.button2text = "OPEN GOTO"; lightboxPopup.button2method = function() { - sendToScript({method: 'purchases_openGoTo'}); + sendToScript({method: 'checkout_openGoTo'}); lightboxPopup.visible = false; }; lightboxPopup.visible = true; @@ -864,7 +867,7 @@ Rectangle { RalewaySemiBold { id: myPurchasesLink; - text: 'View this item in My Purchases'; + text: 'View this item in your Inventory'; // Text size size: 18; // Anchors @@ -886,7 +889,8 @@ Rectangle { RalewaySemiBold { id: walletLink; - text: 'View receipt in Wallet'; + visible: !WalletScriptingInterface.limitedCommerce; + text: 'View receipt in Recent Activity'; // Text size size: 18; // Anchors @@ -902,18 +906,18 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - sendToScript({method: 'purchases_openWallet'}); + sendToScript({method: 'checkout_openRecentActivity'}); } } RalewayRegular { id: pendingText; - text: 'Your item is marked "pending" while your purchase is being confirmed. ' + + text: 'Your item is marked "pending" while the transfer is being confirmed. ' + 'Learn More'; // Text size size: 18; // Anchors - anchors.top: walletLink.bottom; + anchors.top: walletLink.visible ? walletLink.bottom : myPurchasesLink.bottom; anchors.topMargin: 32; height: paintedHeight; anchors.left: parent.left; @@ -925,8 +929,8 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - lightboxPopup.titleText = "Purchase Confirmations"; - lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed.

' + + lightboxPopup.titleText = "Confirmations"; + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed.

' + 'Confirmations usually take about 90 seconds.'; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { @@ -936,9 +940,9 @@ Rectangle { } } - // "Continue Shopping" button + // "Continue" button HifiControlsUit.Button { - id: continueShoppingButton; + id: continueButton; color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; anchors.bottom: parent.bottom; @@ -946,9 +950,9 @@ Rectangle { anchors.right: parent.right; width: 193; height: 44; - text: "Continue Shopping"; + text: "Continue"; onClicked: { - sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + sendToScript({method: 'checkout_continue', itemId: itemId}); } } } @@ -971,7 +975,7 @@ Rectangle { RalewayRegular { id: failureHeaderText; - text: "Purchase Failed.
Your Purchases and HFC balance haven't changed."; + text: "Purchase Failed.
Your Inventory and HFC balance haven't changed."; // Text size size: 24; // Anchors @@ -1037,7 +1041,7 @@ Rectangle { width: parent.width/2 - anchors.leftMargin*2; text: "Back to Marketplace"; onClicked: { - sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + sendToScript({method: 'checkout_continue', itemId: itemId}); } } } @@ -1122,10 +1126,10 @@ Rectangle { if (root.balanceAfterPurchase < 0) { // If you already own the item... if (!root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; + buyText.text = "You do not have sufficient funds to purchase this item."; // Else if you don't already own the item... } else if (canBuyAgain()) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; + buyText.text = "You do not have sufficient funds to purchase this item again."; } else { buyText.text = "While you do not have sufficient funds to buy this, you already have this item." } @@ -1171,7 +1175,7 @@ Rectangle { buyText.text = ""; } } else { - buyText.text = 'This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.'; + buyText.text = 'This type of item cannot currently be certified, so it will not show up in "Inventory". You can access it again for free from the Marketplace.'; buyTextContainer.color = hifi.colors.white; buyTextContainer.border.color = hifi.colors.white; buyGlyph.text = ""; @@ -1185,6 +1189,7 @@ Rectangle { root.ownershipStatusReceived = false; Commerce.alreadyOwned(root.itemId); root.availableUpdatesReceived = false; + root.currentUpdatesPage = 1; Commerce.getAvailableUpdates(root.itemId); root.balanceReceived = false; Commerce.balance(); diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 9d9216c461..b7215500d2 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -14,9 +14,9 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit +import "qrc:////qml//controls" as HifiControls // references XXX from root context @@ -33,13 +33,15 @@ Rectangle { property string buttonLayout: "leftright"; readonly property string securityPicBodyText: "When you see your Security Pic, your actions and data are securely making use of your " + - "Wallet's private keys.

You can change your Security Pic in your Wallet."; + "private keys.

You can change your Security Pic via Settings > Security..."; id: root; visible: false; anchors.fill: parent; color: Qt.rgba(0, 0, 0, 0.5); z: 999; + + HifiConstants { id: hifi; } onVisibleChanged: { if (!visible) { diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 1b77dcd3e9..0d0af875d1 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context @@ -27,7 +27,6 @@ Item { property string referrerURL: (Account.metaverseServerURL + "/marketplace?"); readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin; property alias usernameDropdownVisible: usernameDropdown.visible; - property bool messagesWaiting: false; height: mainContainer.height + additionalDropdownHeight; @@ -38,7 +37,6 @@ Item { if (walletStatus === 0) { sendToParent({method: "needsLogIn"}); } else if (walletStatus === 5) { - Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } else if (walletStatus > 5) { console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus); @@ -59,14 +57,6 @@ Item { securityImage.source = "image://security/securityImage"; } } - - onAvailableUpdatesResult: { - if (result.status !== 'success') { - console.log("Failed to get Available Updates", result.data.message); - } else { - root.messagesWaiting = result.data.updates.length > 0; - } - } } Component.onCompleted: { @@ -118,50 +108,6 @@ Item { anchors.right: securityImage.left; anchors.rightMargin: 6; - Rectangle { - id: myPurchasesLink; - anchors.right: myUsernameButton.left; - anchors.rightMargin: 8; - anchors.verticalCenter: parent.verticalCenter; - height: 40; - width: myPurchasesText.paintedWidth + 10; - - RalewaySemiBold { - id: myPurchasesText; - text: "My Purchases"; - // Text size - size: 18; - // Style - color: hifi.colors.blueAccent; - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - // Anchors - anchors.centerIn: parent; - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - sendToParent({ method: 'header_goToPurchases', hasUpdates: root.messagesWaiting }); - } - onEntered: myPurchasesText.color = hifi.colors.blueHighlight; - onExited: myPurchasesText.color = hifi.colors.blueAccent; - } - } - - Rectangle { - id: messagesWaitingLight; - visible: root.messagesWaiting; - anchors.right: myPurchasesLink.left; - anchors.rightMargin: -2; - anchors.verticalCenter: parent.verticalCenter; - height: 10; - width: height; - radius: height/2; - color: "red"; - } - TextMetrics { id: textMetrics; font.family: "Raleway" @@ -267,7 +213,7 @@ Item { anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin; anchors.right: buttonAndUsernameContainer.right; height: childrenRect.height; - width: 100; + width: 150; Rectangle { id: myItemsButton; @@ -279,7 +225,7 @@ Item { RalewaySemiBold { anchors.fill: parent; - text: "My Items" + text: "My Submissions" color: hifi.colors.baseGray; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 5f874d3f04..c2d85b68b4 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context @@ -87,7 +87,7 @@ Rectangle { } RalewayRegular { id: introText2; - text: "My Purchases"; + text: "Inventory"; // Text size size: 22; // Anchors @@ -116,7 +116,7 @@ Rectangle { RalewayRegular { id: step1text; - text: "The 'REZ IT' button makes your purchase appear in front of you."; + text: "The 'REZ IT' button makes your item appear in front of you."; // Text size size: 20; // Anchors diff --git a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg index ce0d87363f..5632467c32 100644 Binary files a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg and b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg differ diff --git a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg index 40bed974af..38ebf08162 100644 Binary files a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg and b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg differ diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml index 41eacd68d5..1eb8af31e6 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml @@ -16,8 +16,8 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import "../../../../styles-uit" -import "../../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../../controls" as HifiControls import "../../wallet" as HifiWallet diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml index 9293dc83ab..9e1a967d50 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.6 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "../../../../styles-uit" -import "../../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../../controls" as HifiControls import "../" as HifiCommerceCommon diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index a515c8031f..ec7146f33e 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.6 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "../../../../styles-uit" -import "../../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../../controls" as HifiControls import "../" as HifiCommerceCommon import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. @@ -39,6 +39,7 @@ Item { property string sendingPubliclyEffectImage; property var http; property var listModelName; + property var keyboardContainer: nil; // This object is always used in a popup or full-screen Wallet section. // This MouseArea is used to prevent a user from being @@ -72,6 +73,10 @@ Item { } onTransferAssetToNodeResult: { + if (!root.visible) { + return; + } + root.isCurrentlySendingAsset = false; if (result.status === 'success') { @@ -91,6 +96,10 @@ Item { } onTransferAssetToUsernameResult: { + if (!root.visible) { + return; + } + root.isCurrentlySendingAsset = false; if (result.status === 'success') { @@ -1125,8 +1134,7 @@ Item { checked: Settings.getValue("sendAssetsNearbyPublicly", true); text: "Show Effect" // Anchors - anchors.top: messageContainer.bottom; - anchors.topMargin: 16; + anchors.verticalCenter: bottomBarContainer.verticalCenter; anchors.left: parent.left; anchors.leftMargin: 20; width: 130; @@ -1168,6 +1176,9 @@ Item { lightboxPopup.visible = false; } lightboxPopup.visible = true; + if (keyboardContainer) { + keyboardContainer.keyboardRaised = false; + } } } } @@ -1178,8 +1189,8 @@ Item { anchors.leftMargin: 20; anchors.right: parent.right; anchors.rightMargin: 20; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 20; + anchors.top: messageContainer.bottom; + anchors.topMargin: 20; height: 60; // "CANCEL" button @@ -1187,11 +1198,11 @@ Item { id: cancelButton_sendAssetStep; color: root.assetName === "" ? hifi.buttons.noneBorderlessWhite : hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.dark; - anchors.left: parent.left; - anchors.leftMargin: 24; + anchors.right: sendButton.left; + anchors.rightMargin: 24; anchors.verticalCenter: parent.verticalCenter; height: 40; - width: 150; + width: 100; text: "CANCEL"; onClicked: { resetSendAssetData(); @@ -1205,10 +1216,10 @@ Item { color: hifi.buttons.blue; colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light; anchors.right: parent.right; - anchors.rightMargin: 24; + anchors.rightMargin: 0; anchors.verticalCenter: parent.verticalCenter; height: 40; - width: 150; + width: 100; text: "SUBMIT"; onClicked: { if (root.assetName === "" && parseInt(amountTextField.text) > parseInt(balanceText.text)) { @@ -1306,13 +1317,13 @@ Item { Rectangle { anchors.top: parent.top; - anchors.topMargin: root.assetName === "" ? 15 : 150; + anchors.topMargin: root.assetName === "" ? 15 : 125; anchors.left: parent.left; anchors.leftMargin: root.assetName === "" ? 15 : 50; anchors.right: parent.right; anchors.rightMargin: root.assetName === "" ? 15 : 50; anchors.bottom: parent.bottom; - anchors.bottomMargin: root.assetName === "" ? 15 : 240; + anchors.bottomMargin: root.assetName === "" ? 15 : 125; color: "#FFFFFF"; RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index d24344b40a..8d0b93d11a 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet @@ -28,7 +28,7 @@ Rectangle { property string itemName: "--"; property string itemOwner: "--"; property string itemEdition: "--"; - property string dateOfPurchase: "--"; + property string dateAcquired: "--"; property string itemCost: "--"; property string certTitleTextColor: hifi.colors.darkGray; property string certTextColor: hifi.colors.white; @@ -64,7 +64,7 @@ Rectangle { root.itemName = ""; root.itemEdition = ""; root.itemOwner = ""; - root.dateOfPurchase = ""; + root.dateAcquired = ""; root.itemCost = ""; errorText.text = "Information about this certificate is currently unavailable. Please try again later."; } @@ -77,8 +77,9 @@ Rectangle { // "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc root.itemOwner = root.isMyCert ? Account.username : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; - root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; - root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed"; + root.dateAcquired = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; + root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? + (parseInt(result.data.cost) > 0 ? result.data.cost : "Free") : "Undisclosed"; } if (root.certInfoReplaceMode > 4) { root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run); @@ -86,7 +87,7 @@ Rectangle { if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED if (root.isMyCert) { - errorText.text = "This item is an uncertified copy of an item you purchased."; + errorText.text = "This item is an uncertified copy of an item you acquired."; } else { errorText.text = "The person who placed this item doesn't own it."; } @@ -102,8 +103,8 @@ Rectangle { showInMarketplaceButton.visible = false; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Purchase Date" text previously set above in this function - // "Purchase Price" text previously set above in this function + // "Acquisition Date" text previously set above in this function + // "Acquisition Price" text previously set above in this function if (result.data.invalid_reason) { errorText.text = result.data.invalid_reason; } @@ -117,8 +118,8 @@ Rectangle { showInMarketplaceButton.visible = true; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Purchase Date" text previously set above in this function - // "Purchase Price" text previously set above in this function + // "Acquisition Date" text previously set above in this function + // "Acquisition Price" text previously set above in this function errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " + "this entity will be cleaned up by the domain."; } @@ -145,8 +146,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" errorText.text = ""; } else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT root.useGoldCert = false; @@ -160,7 +161,7 @@ Rectangle { root.itemName = ""; root.itemEdition = ""; root.itemOwner = ""; - root.dateOfPurchase = ""; + root.dateAcquired = ""; root.itemCost = ""; errorText.text = "Your request to inspect this item timed out. Please try again later."; } else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED @@ -175,8 +176,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; } else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED root.useGoldCert = false; @@ -190,8 +191,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" root.itemEdition = "Uncertified Copy" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" // "Error Text" text will be set in "onCertificateInfoResult()" } else { console.log("Unknown certificate status received from ledger signal!"); @@ -485,8 +486,8 @@ Rectangle { } RalewayRegular { - id: dateOfPurchaseHeader; - text: "PURCHASE DATE"; + id: dateAcquiredHeader; + text: "ACQUISITION DATE"; // Text size size: 16; // Anchors @@ -500,15 +501,15 @@ Rectangle { color: hifi.colors.darkGray; } AnonymousProRegular { - id: dateOfPurchase; - text: root.dateOfPurchase; + id: dateAcquired; + text: root.dateAcquired; // Text size size: 18; // Anchors - anchors.top: dateOfPurchaseHeader.bottom; + anchors.top: dateAcquiredHeader.bottom; anchors.topMargin: 8; - anchors.left: dateOfPurchaseHeader.left; - anchors.right: dateOfPurchaseHeader.right; + anchors.left: dateAcquiredHeader.left; + anchors.right: dateAcquiredHeader.right; height: paintedHeight; // Style color: root.infoTextColor; @@ -516,7 +517,7 @@ Rectangle { RalewayRegular { id: priceHeader; - text: "PURCHASE PRICE"; + text: "ACQUISITION PRICE"; // Text size size: 16; // Anchors @@ -530,7 +531,7 @@ Rectangle { } HiFiGlyphs { id: hfcGlyph; - visible: priceText.text !== "Undisclosed" && priceText.text !== ""; + visible: priceText.text !== "Undisclosed" && priceText.text !== "" && priceText.text !== "Free"; text: hifi.glyphs.hfc; // Size size: 24; @@ -618,7 +619,7 @@ Rectangle { root.itemName = "--"; root.itemOwner = "--"; root.itemEdition = "--"; - root.dateOfPurchase = "--"; + root.dateAcquired = "--"; root.marketplaceUrl = ""; root.itemCost = "--"; root.isMyCert = false; diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml new file mode 100644 index 0000000000..966f359e92 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml @@ -0,0 +1,364 @@ +// +// ItemUnderTest +// qml/hifi/commerce/marketplaceItemTester +// +// Load items not in the marketplace for testing purposes +// +// Created by Kerry Ivan Kurian on 2018-10-18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import Hifi 1.0 as Hifi +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit + +Rectangle { + id: root; + color: hifi.colors.baseGray + width: parent.width - 16 + height: childrenRect.height + itemHeaderContainer.anchors.topMargin + detailsContainer.anchors.topMargin + + property var detailsExpanded: false + + property var actions: { + "forward": function(resource, assetType, resourceObjectId){ + switch(assetType) { + case "application": + Commerce.installApp(resource, true); + break; + case "avatar": + MyAvatar.useFullAvatarURL(resource); + break; + case "content set": + urlHandler.handleUrl("hifi://localhost/0,0,0"); + Commerce.replaceContentSet(toUrl(resource), ""); + break; + case "entity": + case "wearable": + rezEntity(resource, assetType, resourceObjectId); + break; + default: + print("Marketplace item tester unsupported assetType " + assetType); + } + }, + "trash": function(resource, assetType){ + if ("application" === assetType) { + Commerce.uninstallApp(resource); + } + sendToScript({ + method: "tester_deleteResourceObject", + objectId: resourceListModel.get(index).resourceObjectId}); + resourceListModel.remove(index); + } + } + + Item { + id: itemHeaderContainer + anchors.top: parent.top + anchors.topMargin: 8 + anchors.left: parent.left + anchors.leftMargin: 8 + width: parent.width - 16 + height: childrenRect.height + + Item { + id: itemNameContainer + width: parent.width * 0.5 + height: childrenRect.height + + HifiStylesUit.RalewaySemiBold { + id: resourceName + height: paintedHeight + width: parent.width + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + size: 14 + color: hifi.colors.white + wrapMode: Text.WrapAnywhere + } + + HifiStylesUit.RalewayRegular { + id: resourceUrl + anchors.top: resourceName.bottom; + anchors.topMargin: 4; + height: paintedHeight + width: parent.width + text: resource + size: 12 + color: hifi.colors.faintGray; + wrapMode: Text.WrapAnywhere + } + } + + HifiControlsUit.ComboBox { + id: comboBox + anchors.left: itemNameContainer.right + anchors.leftMargin: 4 + anchors.verticalCenter: itemNameContainer.verticalCenter + height: 30 + width: parent.width * 0.3 - anchors.leftMargin + + model: [ + "application", + "avatar", + "content set", + "entity", + "wearable", + "unknown" + ] + + currentIndex: (("entity or wearable" === assetType) ? + model.indexOf("unknown") : model.indexOf(assetType)) + + Component.onCompleted: { + onCurrentIndexChanged.connect(function() { + assetType = model[currentIndex]; + sendToScript({ + method: "tester_updateResourceObjectAssetType", + objectId: resourceListModel.get(index)["resourceObjectId"], + assetType: assetType }); + }); + } + } + + Button { + id: actionButton + property var glyphs: { + "application": hifi.glyphs.install, + "avatar": hifi.glyphs.avatar, + "content set": hifi.glyphs.globe, + "entity": hifi.glyphs.wand, + "trash": hifi.glyphs.trash, + "unknown": hifi.glyphs.circleSlash, + "wearable": hifi.glyphs.hat + } + property int color: hifi.buttons.blue; + property int colorScheme: hifi.colorSchemes.dark; + anchors.left: comboBox.right + anchors.leftMargin: 4 + anchors.verticalCenter: itemNameContainer.verticalCenter + width: parent.width * 0.10 - anchors.leftMargin + height: width + enabled: comboBox.model[comboBox.currentIndex] !== "unknown" + + onClicked: { + if (model.currentlyRecordingResources) { + model.currentlyRecordingResources = false; + } else { + model.resourceAccessEventText = ""; + model.currentlyRecordingResources = true; + root.actions["forward"](resource, comboBox.currentText, resourceObjectId); + } + sendToScript({ + method: "tester_updateResourceRecordingStatus", + objectId: resourceListModel.get(index).resourceObjectId, + status: model.currentlyRecordingResources + }); + } + + background: Rectangle { + radius: 4; + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!actionButton.enabled) { + hifi.buttons.disabledColorStart[actionButton.colorScheme] + } else if (actionButton.pressed) { + hifi.buttons.pressedColor[actionButton.color] + } else if (actionButton.hovered) { + hifi.buttons.hoveredColor[actionButton.color] + } else { + hifi.buttons.colorStart[actionButton.color] + } + } + } + GradientStop { + position: 1.0 + color: { + if (!actionButton.enabled) { + hifi.buttons.disabledColorFinish[actionButton.colorScheme] + } else if (actionButton.pressed) { + hifi.buttons.pressedColor[actionButton.color] + } else if (actionButton.hovered) { + hifi.buttons.hoveredColor[actionButton.color] + } else { + hifi.buttons.colorFinish[actionButton.color] + } + } + } + } + } + + contentItem: Item { + HifiStylesUit.HiFiGlyphs { + id: rezIcon; + text: model.currentlyRecordingResources ? hifi.glyphs.scriptStop : actionButton.glyphs[comboBox.model[comboBox.currentIndex]]; + anchors.fill: parent + size: 30; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + color: enabled ? hifi.buttons.textColor[actionButton.color] + : hifi.buttons.disabledTextColor[actionButton.colorScheme] + } + } + } + + Button { + id: trashButton + property int color: hifi.buttons.red; + property int colorScheme: hifi.colorSchemes.dark; + anchors.left: actionButton.right + anchors.verticalCenter: itemNameContainer.verticalCenter + anchors.leftMargin: 4 + width: parent.width * 0.10 - anchors.leftMargin + height: width + + onClicked: { + root.actions["trash"](resource, comboBox.currentText, resourceObjectId); + } + + background: Rectangle { + radius: 4; + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!trashButton.enabled) { + hifi.buttons.disabledColorStart[trashButton.colorScheme] + } else if (trashButton.pressed) { + hifi.buttons.pressedColor[trashButton.color] + } else if (trashButton.hovered) { + hifi.buttons.hoveredColor[trashButton.color] + } else { + hifi.buttons.colorStart[trashButton.color] + } + } + } + GradientStop { + position: 1.0 + color: { + if (!trashButton.enabled) { + hifi.buttons.disabledColorFinish[trashButton.colorScheme] + } else if (trashButton.pressed) { + hifi.buttons.pressedColor[trashButton.color] + } else if (trashButton.hovered) { + hifi.buttons.hoveredColor[trashButton.color] + } else { + hifi.buttons.colorFinish[trashButton.color] + } + } + } + } + } + + contentItem: Item { + HifiStylesUit.HiFiGlyphs { + id: trashIcon; + text: hifi.glyphs.trash + anchors.fill: parent + size: 22; + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: enabled ? hifi.buttons.textColor[trashButton.color] + : hifi.buttons.disabledTextColor[trashButton.colorScheme] + } + } + } + } + + Item { + id: detailsContainer + + width: parent.width - 16 + height: root.detailsExpanded ? 300 : 26 + anchors.top: itemHeaderContainer.bottom + anchors.topMargin: 12 + anchors.left: parent.left + anchors.leftMargin: 8 + + HifiStylesUit.HiFiGlyphs { + id: detailsToggle + anchors.left: parent.left + anchors.leftMargin: -4 + anchors.top: parent.top + anchors.topMargin: -2 + width: 22 + text: root.detailsExpanded ? hifi.glyphs.minimize : hifi.glyphs.maximize + color: hifi.colors.white + size: 22 + MouseArea { + anchors.fill: parent + onClicked: root.detailsExpanded = !root.detailsExpanded + } + } + + ScrollView { + id: detailsTextContainer + anchors.top: parent.top + anchors.left: detailsToggle.right + anchors.leftMargin: 4 + anchors.right: parent.right + height: detailsContainer.height - (root.detailsExpanded ? (copyToClipboardButton.height + copyToClipboardButton.anchors.topMargin) : 0) + clip: true + + TextArea { + id: detailsText + readOnly: true + color: hifi.colors.white + text: { + var numUniqueResources = (model.resourceAccessEventText.split("\n").length - 1); + if (root.detailsExpanded && numUniqueResources > 0) { + return model.resourceAccessEventText + } else { + return numUniqueResources.toString() + " unique source/resource url pair" + (numUniqueResources === 1 ? "" : "s") + " recorded" + } + } + font: Qt.font({ family: "Courier", pointSize: 8, weight: Font.Normal }) + wrapMode: TextEdit.NoWrap + + background: Rectangle { + anchors.fill: parent; + color: hifi.colors.baseGrayShadow; + border.width: 0; + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (root.detailsExpanded) { + detailsText.selectAll(); + } else { + root.detailsExpanded = true; + } + } + } + } + + HifiControlsUit.Button { + id: copyToClipboardButton; + visible: root.detailsExpanded + color: hifi.buttons.noneBorderlessWhite + colorScheme: hifi.colorSchemes.dark + + anchors.top: detailsTextContainer.bottom + anchors.topMargin: 8 + anchors.right: parent.right + width: 160 + height: 30 + text: "Copy to Clipboard" + + onClicked: { + Window.copyToClipboard(detailsText.text); + } + } + } +} diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index c3d87ca2f5..a37a0ac756 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -4,21 +4,20 @@ // // Load items not in the marketplace for testing purposes // -// Created by Zach Fox on 2018-09-05 +// Created by Kerry Ivan Kurian on 2018-09-05 // 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 // -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.0 +import QtQuick 2.10 import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.3 import Hifi 1.0 as Hifi -import "../../../styles-uit" as HifiStylesUit -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit + @@ -27,33 +26,223 @@ Rectangle { property string installedApps property var nextResourceObjectId: 0 - signal sendToScript(var message) HifiStylesUit.HifiConstants { id: hifi } ListModel { id: resourceListModel } - color: hifi.colors.white + color: hifi.colors.darkGray - AnimatedImage { - id: spinner; - source: "spinner.gif" - width: 74; - height: width; - anchors.verticalCenter: parent.verticalCenter; - anchors.horizontalCenter: parent.horizontalCenter; + // + // TITLE BAR START + // + Item { + id: titleBarContainer + // Size + width: root.width + height: 50 + // Anchors + anchors.left: parent.left + anchors.top: parent.top + + // Title bar text + HifiStylesUit.RalewaySemiBold { + id: titleBarText + text: "Marketplace Item Tester" + // Text size + size: 24 + // Anchors + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.leftMargin: 16 + width: paintedWidth + // Style + color: hifi.colors.lightGrayText + // Alignment + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.bottomMargin: 1 + } + } + // + // TITLE BAR END + // + + Rectangle { + id: spinner + z: 999 + anchors.top: titleBarContainer.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: buttonContainer.top + color: hifi.colors.darkGray + + AnimatedImage { + source: "spinner.gif" + width: 74 + height: width + anchors.centerIn: parent + } + } + + Rectangle { + id: instructionsContainer + z: 998 + color: hifi.colors.darkGray + visible: resourceListModel.count === 0 && !spinner.visible + anchors.top: titleBarContainer.bottom + anchors.topMargin: 20 + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.right: parent.right + anchors.rightMargin: 20 + anchors.bottom: buttonContainer.top + anchors.bottomMargin: 20 + + HifiStylesUit.RalewayRegular { + text: "Use Marketplace Item Tester to test out your items before submitting them to the Marketplace." + + "\n\nUse one of the buttons below to load your item." + // Text size + size: 20 + // Anchors + anchors.fill: parent + // Style + color: hifi.colors.lightGrayText + wrapMode: Text.Wrap + // Alignment + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + ListView { + id: itemList + visible: !instructionsContainer.visible + anchors.top: titleBarContainer.bottom + anchors.topMargin: 20 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: buttonContainer.top + anchors.bottomMargin: 20 + ScrollBar.vertical: ScrollBar { + visible: !instructionsContainer.visible + policy: ScrollBar.AlwaysOn + parent: itemList.parent + anchors.top: itemList.top + anchors.right: itemList.right + anchors.bottom: itemList.bottom + width: 16 + } + clip: true + model: resourceListModel + spacing: 8 + + delegate: ItemUnderTest { } + } + + Item { + id: buttonContainer + + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + height: 40 + + property string currentAction + property var actions: { + "Load File": function() { + buttonContainer.currentAction = "load file"; + Window.browseChanged.connect(onResourceSelected); + Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)"); + }, + "Load URL": function() { + buttonContainer.currentAction = "load url"; + Window.promptTextChanged.connect(onResourceSelected); + Window.promptAsync("Please enter a URL", ""); + } + } + + function onResourceSelected(resource) { + // It is possible that we received the present signal + // from something other than our browserAsync window. + // Alas, there is nothing we can do about that so charge + // ahead as though we are sure the present signal is one + // we expect. + print("!!!! resource selected"); + switch(currentAction) { + case "load file": + Window.browseChanged.disconnect(onResourceSelected); + break + case "load url": + Window.promptTextChanged.disconnect(onResourceSelected); + break; + } + if (resource) { + print("!!!! building resource object"); + var resourceObj = buildResourceObj(resource); + print("!!!! notifying script of resource object"); + sendToScript({ + method: 'tester_newResourceObject', + resourceObject: resourceObj + }); + } + } + + HifiControlsUit.Button { + enabled: !spinner.visible + anchors.right: parent.horizontalCenter + anchors.rightMargin: width/4 + anchors.verticalCenter: parent.verticalCenter + color: hifi.buttons.blue + fontSize: 20 + text: "Load File" + width: parent.width / 3 + height: parent.height + onClicked: buttonContainer.actions[text]() + } + + HifiControlsUit.Button { + enabled: !spinner.visible + anchors.left: parent.horizontalCenter + anchors.leftMargin: width/4 + anchors.verticalCenter: parent.verticalCenter + color: hifi.buttons.blue + fontSize: 20 + text: "Load URL" + width: parent.width / 3 + height: parent.height + onClicked: buttonContainer.actions[text]() + } } function fromScript(message) { switch (message.method) { case "newResourceObjectInTest": var resourceObject = message.resourceObject; + resourceListModel.clear(); // REMOVE THIS once we support specific referrers resourceListModel.append(resourceObject); spinner.visible = false; break; case "nextObjectIdInTest": + print("!!!! message from script! " + JSON.stringify(message)); nextResourceObjectId = message.id; spinner.visible = false; break; + case "resourceRequestEvent": + // When we support multiple items under test simultaneously, + // we'll have to replace "0" with the correct index. + resourceListModel.setProperty(0, "resourceAccessEventText", message.resourceAccessEventText); + break; } } @@ -64,227 +253,26 @@ Rectangle { resource.match(/\.json\.gz$/) ? "content set" : resource.match(/\.json$/) ? "entity or wearable" : "unknown"); - return { "id": nextResourceObjectId++, + // Uncomment this once we support more than one item in test at the same time + //nextResourceObjectId++; + return { "resourceObjectId": nextResourceObjectId, "resource": resource, "assetType": assetType }; } - function installResourceObj(resourceObj) { - if ("application" === resourceObj.assetType) { - Commerce.installApp(resourceObj.resource); - } - } - - function addAllInstalledAppsToList() { - var i, apps = Commerce.getInstalledApps().split(","), len = apps.length; - for(i = 0; i < len - 1; ++i) { - if (i in apps) { - resourceListModel.append(buildResourceObj(apps[i])); - } - } - } - function toUrl(resource) { var httpPattern = /^http/i; return httpPattern.test(resource) ? resource : "file:///" + resource; } - function rezEntity(resource, entityType) { + function rezEntity(resource, entityType, resourceObjectId) { + print("!!!! tester_rezClicked"); sendToScript({ method: 'tester_rezClicked', itemHref: toUrl(resource), - itemType: entityType}); + itemType: entityType, + itemId: resourceObjectId }); } - ListView { - anchors.fill: parent - anchors.leftMargin: 12 - anchors.bottomMargin: 40 - anchors.rightMargin: 12 - model: resourceListModel - spacing: 5 - interactive: false - - delegate: RowLayout { - anchors.left: parent.left - width: parent.width - spacing: 5 - - property var actions: { - "forward": function(resource, assetType){ - switch(assetType) { - case "application": - Commerce.openApp(resource); - break; - case "avatar": - MyAvatar.useFullAvatarURL(resource); - break; - case "content set": - urlHandler.handleUrl("hifi://localhost/0,0,0"); - Commerce.replaceContentSet(toUrl(resource), ""); - break; - case "entity": - case "wearable": - rezEntity(resource, assetType); - break; - default: - print("Marketplace item tester unsupported assetType " + assetType); - } - }, - "trash": function(resource, assetType){ - if ("application" === assetType) { - Commerce.uninstallApp(resource); - } - sendToScript({ - method: "tester_deleteResourceObject", - objectId: resourceListModel.get(index).id}); - resourceListModel.remove(index); - } - } - - Column { - Layout.preferredWidth: root.width * .6 - spacing: 5 - Text { - text: { - var match = resource.match(/\/([^/]*)$/); - return match ? match[1] : resource; - } - font.pointSize: 12 - horizontalAlignment: Text.AlignBottom - } - Text { - text: resource - font.pointSize: 8 - width: root.width * .6 - horizontalAlignment: Text.AlignBottom - wrapMode: Text.WrapAnywhere - } - } - - ComboBox { - id: comboBox - - Layout.preferredWidth: root.width * .2 - - model: [ - "application", - "avatar", - "content set", - "entity", - "wearable", - "unknown" - ] - - currentIndex: (("entity or wearable" === assetType) ? - model.indexOf("unknown") : model.indexOf(assetType)) - - Component.onCompleted: { - onCurrentIndexChanged.connect(function() { - assetType = model[currentIndex]; - sendToScript({ - method: "tester_updateResourceObjectAssetType", - objectId: resourceListModel.get(index)["id"], - assetType: assetType }); - }); - } - } - - Repeater { - model: [ "forward", "trash" ] - - HifiStylesUit.HiFiGlyphs { - property var glyphs: { - "application": hifi.glyphs.install, - "avatar": hifi.glyphs.avatar, - "content set": hifi.glyphs.globe, - "entity": hifi.glyphs.wand, - "trash": hifi.glyphs.trash, - "unknown": hifi.glyphs.circleSlash, - "wearable": hifi.glyphs.hat, - } - text: (("trash" === modelData) ? - glyphs.trash : - glyphs[comboBox.model[comboBox.currentIndex]]) - size: ("trash" === modelData) ? 22 : 30 - color: hifi.colors.black - horizontalAlignment: Text.AlignHCenter - MouseArea { - anchors.fill: parent - onClicked: { - actions[modelData](resource, comboBox.currentText); - } - } - } - } - } - - headerPositioning: ListView.OverlayHeader - header: HifiStylesUit.RalewayRegular { - id: rootHeader - text: "Marketplace Item Tester" - height: 80 - width: paintedWidth - size: 22 - color: hifi.colors.black - anchors.left: parent.left - anchors.leftMargin: 12 - } - - footerPositioning: ListView.OverlayFooter - footer: Row { - id: rootActions - spacing: 20 - anchors.horizontalCenter: parent.horizontalCenter - - property string currentAction - property var actions: { - "Load File": function(){ - rootActions.currentAction = "load file"; - Window.browseChanged.connect(onResourceSelected); - Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)"); - }, - "Load URL": function(){ - rootActions.currentAction = "load url"; - Window.promptTextChanged.connect(onResourceSelected); - Window.promptAsync("Please enter a URL", ""); - } - } - - function onResourceSelected(resource) { - // It is possible that we received the present signal - // from something other than our browserAsync window. - // Alas, there is nothing we can do about that so charge - // ahead as though we are sure the present signal is one - // we expect. - switch(currentAction) { - case "load file": - Window.browseChanged.disconnect(onResourceSelected); - break - case "load url": - Window.promptTextChanged.disconnect(onResourceSelected); - break; - } - if (resource) { - var resourceObj = buildResourceObj(resource); - installResourceObj(resourceObj); - sendToScript({ - method: 'tester_newResourceObject', - resourceObject: resourceObj }); - } - } - - Repeater { - model: [ "Load File", "Load URL" ] - HifiControlsUit.Button { - color: hifi.buttons.blue - fontSize: 20 - text: modelData - width: root.width / 3 - height: 40 - onClicked: actions[text]() - } - } - } - } + signal sendToScript(var message) } diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif b/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif index 00f75ae62f..0536bd1884 100644 Binary files a/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif and b/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif differ diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index eeb9ac3c54..c8ec7238d6 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet import TabletScriptingInterface 1.0 @@ -47,8 +47,7 @@ Item { property string wornEntityID; property string upgradeUrl; property string upgradeTitle; - property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems; - property bool isShowingMyItems; + property bool updateAvailable: root.upgradeUrl !== ""; property bool valid; property string originalStatusText; @@ -231,7 +230,7 @@ Item { Loader { id: giftButton; - visible: !root.isShowingMyItems; + visible: root.itemEdition > 0; sourceComponent: contextCardButton; anchors.right: parent.right; anchors.top: parent.top; @@ -345,6 +344,7 @@ Item { Rectangle { id: permissionExplanationCard; + visible: false; anchors.left: parent.left; anchors.leftMargin: 30; anchors.top: parent.top; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 2435678e77..932a3ab6c4 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -13,13 +13,12 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. import "../wallet" as HifiWallet import "../common" as HifiCommerceCommon -import "../inspectionCertificate" as HifiInspectionCertificate import "../common/sendAsset" as HifiSendAsset import "../.." as HifiCommon @@ -34,7 +33,6 @@ Rectangle { property bool securityImageResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; - property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; property string installedApps; property bool keyboardRaised: false; @@ -92,7 +90,6 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get Available Updates", result.data.message); } else { - sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length }); root.numUpdatesAvailable = result.total_entries; } } @@ -106,10 +103,6 @@ Rectangle { } } - onIsShowingMyItemsChanged: { - getPurchases(); - } - Timer { id: notSetUpTimer; interval: 200; @@ -118,19 +111,6 @@ Rectangle { } } - HifiInspectionCertificate.InspectionCertificate { - id: inspectionCertificate; - z: 998; - visible: false; - anchors.fill: parent; - - Connections { - onSendToScript: { - sendToScript(message); - } - } - } - HifiCommerceCommon.CommerceLightbox { id: lightboxPopup; z: 999; @@ -158,6 +138,7 @@ Rectangle { listModelName: "Gift Connections"; z: 998; visible: root.activeView === "giftAsset"; + keyboardContainer: root; anchors.fill: parent; parentAppTitleBarHeight: 70; parentAppNavBarHeight: 0; @@ -179,7 +160,8 @@ Rectangle { HifiCommerceCommon.EmulatedMarketplaceHeader { id: titleBarContainer; z: 997; - visible: !needsLogIn.visible; + visible: false; + height: 100; // Size width: parent.width; // Anchors @@ -198,11 +180,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - sendToScript({method: 'purchases_openWallet'}); - lightboxPopup.visible = false; - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -474,7 +451,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: isShowingMyItems ? "My Items" : "My Purchases"; + text: "Items"; color: hifi.colors.black; size: 22; } @@ -516,8 +493,13 @@ Rectangle { "filterName": "wearable" }, { + "separator" : true, "displayName": "Updatable", "filterName": "updated" + }, + { + "displayName": "My Submissions", + "filterName": "proofs" } ] filterBar.primaryFilterChoices.clear(); @@ -532,6 +514,7 @@ Rectangle { onTextChanged: { purchasesModel.searchFilter = filterBar.text; filterBar.previousText = filterBar.text; + } } } @@ -555,10 +538,18 @@ Rectangle { listModelName: 'purchases'; listView: purchasesContentsList; getPage: function () { - console.debug('getPage', purchasesModel.listModelName, root.isShowingMyItems, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage); + console.debug('getPage', purchasesModel.listModelName, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage); + var editionFilter = ""; + var primaryFilter = ""; + + if (filterBar.primaryFilter_filterName === "proofs") { + editionFilter = "proofs"; + } else { + primaryFilter = filterBar.primaryFilter_filterName; + } Commerce.inventory( - root.isShowingMyItems ? "proofs" : "purchased", - filterBar.primaryFilter_filterName, + editionFilter, + primaryFilter, filterBar.text, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage @@ -585,7 +576,7 @@ Rectangle { visible: purchasesModel.count !== 0; clip: true; model: purchasesModel; - snapMode: ListView.SnapToItem; + snapMode: ListView.NoSnap; // Anchors anchors.top: separator.bottom; anchors.left: parent.left; @@ -608,7 +599,6 @@ Rectangle { upgradeUrl: model.upgrade_url; upgradeTitle: model.upgrade_title; itemType: model.item_type; - isShowingMyItems: root.isShowingMyItems; valid: model.valid; anchors.topMargin: 10; anchors.bottomMargin: 10; @@ -625,8 +615,6 @@ Rectangle { sendToScript({ method: 'purchases_updateWearables' }); } } else if (msg.method === 'purchases_itemCertificateClicked') { - inspectionCertificate.visible = true; - inspectionCertificate.isLightbox = true; sendToScript(msg); } else if (msg.method === "showInvalidatedLightbox") { lightboxPopup.titleText = "Item Invalidated"; @@ -639,7 +627,7 @@ Rectangle { lightboxPopup.visible = true; } else if (msg.method === "showPendingLightbox") { lightboxPopup.titleText = "Item Pending"; - lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed. ' + + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed. ' + "Usually, purchases take about 90 seconds to confirm."; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { @@ -821,7 +809,8 @@ Rectangle { Rectangle { id: updatesAvailableBanner; - visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems; + visible: root.numUpdatesAvailable > 0 && + filterBar.primaryFilter_filterName !== "proofs"; anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; @@ -882,9 +871,8 @@ Rectangle { id: noItemsAlertContainer; visible: !purchasesContentsList.visible && root.purchasesReceived && - root.isShowingMyItems && filterBar.text === "" && - filterBar.primaryFilter_displayName === ""; + filterBar.primaryFilter_filterName === "proofs"; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -894,7 +882,7 @@ Rectangle { // Explanitory text RalewayRegular { id: noItemsYet; - text: "You haven't submitted anything to the Marketplace yet!

Submit an item to the Marketplace to add it to My Items."; + text: "You haven't submitted anything to the Marketplace yet!

Submit an item to the Marketplace to add it to My Submissions."; // Text size size: 22; // Anchors @@ -932,7 +920,6 @@ Rectangle { id: noPurchasesAlertContainer; visible: !purchasesContentsList.visible && root.purchasesReceived && - !root.isShowingMyItems && filterBar.text === "" && filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; @@ -944,7 +931,7 @@ Rectangle { // Explanitory text RalewayRegular { id: haventPurchasedYet; - text: "You haven't purchased anything yet!

Get an item from Marketplace to add it to My Purchases."; + text: "You haven't gotten anything yet!

Get an item from Marketplace to add it to your Inventory."; // Text size size: 22; // Anchors @@ -1064,11 +1051,10 @@ Rectangle { titleBarContainer.referrerURL = message.referrerURL || ""; filterBar.text = message.filterText ? message.filterText : ""; break; - case 'inspectionCertificate_setCertificateId': - inspectionCertificate.fromScript(message); - break; case 'purchases_showMyItems': - root.isShowingMyItems = true; + filterBar.primaryFilter_filterName = "proofs"; + filterBar.primaryFilter_displayName = "Proofs"; + filterBar.primaryFilter_index = 6; break; case 'updateConnections': sendAsset.updateConnections(message.connections); diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 6d8fc3c33f..1598aec41c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context @@ -62,7 +62,7 @@ Item { isExpanded: false; question: "How can I get HFC?"; answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ -Get it by going to

BankOfHighFidelity. and meeting with the banker!"; +Get it by going to BankOfHighFidelity and meeting with the banker!"; } ListElement { isExpanded: false; @@ -74,8 +74,7 @@ In your Wallet's Send Money tab, choose from your list of connections, or choose isExpanded: false; question: "What is a Security Pic?" answer: "Your Security Pic acts as an extra layer of Wallet security. \ -When you see your Security Pic, you know that your actions and data are securely making use of your account. \ -

Tap here to change your Security Pic."; +When you see your Security Pic, you know that your actions and data are securely making use of your account."; } ListElement { isExpanded: false; @@ -137,7 +136,7 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta anchors.left: parent.left; width: parent.width; height: questionText.paintedHeight + 50; - + RalewaySemiBold { id: plusMinusButton; text: model.isExpanded ? "-" : "+"; @@ -217,8 +216,6 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta } } else if (link === "#support") { Qt.openUrlExternally("mailto:support@highfidelity.com"); - } else if (link === "#securitypic") { - sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index eadf1ca8a2..ab05f55deb 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context @@ -93,7 +93,7 @@ Item { // Text below helper text RalewayRegular { id: loginDetailText; - text: "To buy/sell items on the Marketplace, or to use your Wallet, you must first log in to High Fidelity."; + text: "To get items on the Marketplace, or to use your Assets, you must first log in to High Fidelity."; // Text size size: 18; // Anchors diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml index 8451c90836..6ddfe0da1c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index c4abd40d2a..86d50e87ec 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index e052b78876..179ffcf707 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml deleted file mode 100644 index 14ac696ef7..0000000000 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ /dev/null @@ -1,246 +0,0 @@ -// -// Security.qml -// qml/hifi/commerce/wallet -// -// Security -// -// Created by Zach Fox on 2017-08-18 -// Copyright 2017 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 -// - -import Hifi 1.0 as Hifi -import QtQuick 2.5 -import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls - -// references XXX from root context - -Item { - HifiConstants { id: hifi; } - - id: root; - property string keyFilePath; - - Connections { - target: Commerce; - - onKeyFilePathIfExistsResult: { - root.keyFilePath = path; - } - } - - // Username Text - RalewayRegular { - id: usernameText; - text: Account.username; - // Text size - size: 24; - // Style - color: hifi.colors.white; - elide: Text.ElideRight; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 20; - width: parent.width/2; - height: 80; - } - - Item { - id: securityContainer; - anchors.top: usernameText.bottom; - anchors.topMargin: 20; - anchors.left: parent.left; - anchors.right: parent.right; - anchors.bottom: parent.bottom; - - RalewaySemiBold { - id: securityText; - text: "Security"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - height: 30; - // Text size - size: 18; - // Style - color: hifi.colors.blueHighlight; - } - - Rectangle { - id: securityTextSeparator; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.right: parent.right; - anchors.top: securityText.bottom; - anchors.topMargin: 8; - // Style - color: hifi.colors.faintGray; - } - - Item { - id: changeSecurityImageContainer; - anchors.top: securityTextSeparator.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - height: 75; - - HiFiGlyphs { - id: changeSecurityImageImage; - text: hifi.glyphs.securityImage; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - RalewaySemiBold { - text: "Security Pic"; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: changeSecurityImageImage.right; - anchors.leftMargin: 30; - width: 50; - // Text size - size: 18; - // Style - color: hifi.colors.white; - } - - // "Change Security Pic" button - HifiControlsUit.Button { - id: changeSecurityImageButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - width: 140; - height: 40; - text: "Change"; - onClicked: { - sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); - } - } - } - - Item { - id: autoLogoutContainer; - anchors.top: changeSecurityImageContainer.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - height: 75; - - HiFiGlyphs { - id: autoLogoutImage; - text: hifi.glyphs.walletKey; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 20; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - HifiControlsUit.CheckBox { - id: autoLogoutCheckbox; - checked: Settings.getValue("wallet/autoLogout", false); - text: "Automatically Log Out when Exiting Interface" - // Anchors - anchors.verticalCenter: autoLogoutImage.verticalCenter; - anchors.left: autoLogoutImage.right; - anchors.leftMargin: 20; - anchors.right: autoLogoutHelp.left; - anchors.rightMargin: 12; - boxSize: 28; - labelFontSize: 18; - color: hifi.colors.white; - onCheckedChanged: { - Settings.setValue("wallet/autoLogout", checked); - if (checked) { - Settings.setValue("wallet/savedUsername", Account.username); - } else { - Settings.setValue("wallet/savedUsername", ""); - } - } - } - - RalewaySemiBold { - id: autoLogoutHelp; - text: '[?]'; - // Anchors - anchors.verticalCenter: autoLogoutImage.verticalCenter; - anchors.right: parent.right; - width: 30; - height: 30; - // Text size - size: 18; - // Style - color: hifi.colors.blueHighlight; - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - onEntered: { - parent.color = hifi.colors.blueAccent; - } - onExited: { - parent.color = hifi.colors.blueHighlight; - } - onClicked: { - sendSignalToWallet({method: 'walletSecurity_autoLogoutHelp'}); - } - } - } - } - } - - // - // FUNCTION DEFINITIONS START - // - // - // Function Name: fromScript() - // - // Relevant Variables: - // None - // - // Arguments: - // message: The message sent from the JavaScript. - // Messages are in format "{method, params}", like json-rpc. - // - // Description: - // Called when a message is received from a script. - // - function fromScript(message) { - switch (message.method) { - default: - console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); - } - } - signal sendSignalToWallet(var msg); - // - // FUNCTION DEFINITIONS END - // -} diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 588b80c435..81fec4ace3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -14,12 +14,14 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon import "../common/sendAsset" import "../.." as HifiCommon +import "../purchases" as HifiPurchases +import "../inspectionCertificate" as HifiInspectionCertificate Rectangle { HifiConstants { id: hifi; } @@ -27,6 +29,7 @@ Rectangle { id: root; property string activeView: "initialize"; + property string initialActiveViewAfterStatus5: "walletInventory"; property bool keyboardRaised: false; property bool isPassword: false; @@ -37,6 +40,10 @@ Rectangle { source: "images/wallet-bg.jpg"; } + Component.onDestruction: { + KeyboardScriptingInterface.raised = false; + } + Connections { target: Commerce; @@ -64,7 +71,8 @@ Rectangle { } } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { - root.activeView = "walletHome"; + root.activeView = root.initialActiveViewAfterStatus5; + Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } } else { @@ -86,6 +94,21 @@ Rectangle { titleBarSecurityImage.source = "image://security/securityImage"; } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + exchangeMoneyButtonContainer.messagesWaiting = result.data.updates.length > 0; + } + } + } + + onActiveViewChanged: { + if (activeView === "walletHome") { + walletHomeButtonContainer.messagesWaiting = false; + sendToScript({method: 'clearShouldShowDotHistory'}); + } } HifiCommerceCommon.CommerceLightbox { @@ -108,29 +131,32 @@ Rectangle { anchors.top: parent.top; // Wallet icon - HiFiGlyphs { + Image { id: walletIcon; - text: hifi.glyphs.wallet; - // Size - size: parent.height * 0.8; - // Anchors + source: "../../../../icons/tablet-icons/inventory-a.svg"; + height: parent.height * 0.5; + width: walletIcon.height; anchors.left: parent.left; anchors.leftMargin: 8; anchors.verticalCenter: parent.verticalCenter; - // Style + visible: false; // When we use a white .svg instead of a glyph with color property, we set to invisible and use the following ColorOverlay. + } + ColorOverlay { + anchors.fill: walletIcon; + source: walletIcon; color: hifi.colors.blueHighlight; } // Title Bar text RalewaySemiBold { id: titleBarText; - text: "WALLET"; + text: "INVENTORY"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors anchors.top: parent.top; anchors.left: walletIcon.right; - anchors.leftMargin: 4; + anchors.leftMargin: 6; anchors.bottom: parent.bottom; width: paintedWidth; // Style @@ -142,7 +168,7 @@ Rectangle { Image { id: titleBarSecurityImage; source: ""; - visible: titleBarSecurityImage.source !== "" && !securityImageChange.visible; + visible: titleBarSecurityImage.source !== ""; anchors.right: parent.right; anchors.rightMargin: 6; anchors.top: parent.top; @@ -232,10 +258,6 @@ Rectangle { root.isPassword = msg.isPasswordField; } else if (msg.method === 'walletSetup_lowerKeyboard') { root.keyboardRaised = false; - } else if (msg.method === 'walletSecurity_changePassphraseCancelled') { - root.activeView = "security"; - } else if (msg.method === 'walletSecurity_changePassphraseSuccess') { - root.activeView = "security"; } else { sendToScript(msg); } @@ -245,27 +267,6 @@ Rectangle { } } } - SecurityImageChange { - id: securityImageChange; - visible: root.activeView === "securityImageChange"; - z: 997; - anchors.top: titleBarContainer.bottom; - anchors.left: parent.left; - anchors.right: parent.right; - anchors.bottom: parent.bottom; - - Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSecurity_changeSecurityImageCancelled') { - root.activeView = "security"; - } else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') { - root.activeView = "security"; - } else { - sendToScript(msg); - } - } - } - } // // TAB CONTENTS START @@ -344,6 +345,39 @@ Rectangle { } } + HifiInspectionCertificate.InspectionCertificate { + id: inspectionCertificate; + z: 998; + visible: false; + anchors.fill: parent; + + Connections { + onSendToScript: { + sendToScript(message); + } + } + } + + HifiPurchases.Purchases { + id: walletInventory; + visible: root.activeView === "walletInventory"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: !WalletScriptingInterface.limitedCommerce ? tabButtonsContainer.top : parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + Connections { + onSendToScript: { + if (message.method === 'purchases_itemCertificateClicked') { + inspectionCertificate.visible = true; + inspectionCertificate.isLightbox = true; + sendToScript(message); + } else { + sendToScript(message); + } + } + } + } + HifiCommon.RootHttpRequest { id: http; } @@ -354,6 +388,7 @@ Rectangle { listModelName: "Send Money Connections"; z: 997; visible: root.activeView === "sendMoney"; + keyboardContainer: root; anchors.fill: parent; parentAppTitleBarHeight: titleBarContainer.height; parentAppNavBarHeight: tabButtonsContainer.height; @@ -365,39 +400,6 @@ Rectangle { } } - Security { - id: security; - visible: root.activeView === "security"; - anchors.top: titleBarContainer.bottom; - anchors.bottom: tabButtonsContainer.top; - anchors.left: parent.left; - anchors.right: parent.right; - - Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSecurity_changePassphrase') { - root.activeView = "passphraseChange"; - passphraseChange.clearPassphraseFields(); - passphraseChange.resetSubmitButton(); - } else if (msg.method === 'walletSecurity_changeSecurityImage') { - securityImageChange.initModel(); - root.activeView = "securityImageChange"; - } else if (msg.method === 'walletSecurity_autoLogoutHelp') { - lightboxPopup.titleText = "Automatically Log Out"; - lightboxPopup.bodyText = "By default, after you log in to High Fidelity, you will stay logged in to your High Fidelity " + - "account even after you close and re-open Interface. This means anyone who opens Interface on your computer " + - "could make purchases with your Wallet.\n\n" + - "If you do not want to stay logged in across Interface sessions, check this box."; - lightboxPopup.button1text = "CLOSE"; - lightboxPopup.button1method = function() { - lightboxPopup.visible = false; - } - lightboxPopup.visible = true; - } - } - } - } - Help { id: help; visible: root.activeView === "help"; @@ -406,14 +408,6 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; - Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSecurity_changeSecurityImage') { - securityImageChange.initModel(); - root.activeView = "securityImageChange"; - } - } - } } @@ -426,8 +420,8 @@ Rectangle { // Item { id: tabButtonsContainer; - visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendAssetStep"; - property int numTabs: 5; + visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && sendMoney.currentActiveView !== "sendAssetStep" && !WalletScriptingInterface.limitedCommerce; + property int numTabs: 4; // Size width: root.width; height: 90; @@ -445,16 +439,17 @@ Rectangle { // "WALLET HOME" tab button Rectangle { id: walletHomeButtonContainer; + property bool messagesWaiting: false; visible: !walletSetup.visible; color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: parent.left; + anchors.left: exchangeMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; HiFiGlyphs { id: homeTabIcon; - text: hifi.glyphs.home2; + text: hifi.glyphs.leftRightArrows; // Size size: 50; // Anchors @@ -462,11 +457,24 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); + } + + Rectangle { + id: recentActivityMessagesWaitingLight; + visible: parent.messagesWaiting; + anchors.right: homeTabIcon.left; + anchors.rightMargin: -4; + anchors.top: homeTabIcon.top; + anchors.topMargin: 16; + height: 10; + width: height; + radius: height/2; + color: "red"; } RalewaySemiBold { - text: "WALLET HOME"; + text: "RECENT ACTIVITY"; // Text size size: 16; // Anchors @@ -477,7 +485,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -486,6 +494,7 @@ Rectangle { MouseArea { id: walletHomeTabMouseArea; anchors.fill: parent; + enabled: !WalletScriptingInterface.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "walletHome"; @@ -499,28 +508,46 @@ Rectangle { // "EXCHANGE MONEY" tab button Rectangle { id: exchangeMoneyButtonContainer; + property bool messagesWaiting: false; + visible: !walletSetup.visible; - color: hifi.colors.black; + color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: walletHomeButtonContainer.right; + anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - HiFiGlyphs { + Image { id: exchangeMoneyTabIcon; - text: hifi.glyphs.leftRightArrows; - // Size - size: 50; - // Anchors + source: "images/items-tab-a.svg"; + height: 25; + width: exchangeMoneyTabIcon.height; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; - anchors.topMargin: -2; - // Style - color: hifi.colors.lightGray50; + anchors.topMargin: 10; + visible: false; // When we use a white .svg instead of a glyph with color property, we set to invisible and use the following ColorOverlay. + } + ColorOverlay { + anchors.fill: exchangeMoneyTabIcon; + source: exchangeMoneyTabIcon; + color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + } + + Rectangle { + id: exchangeMoneyMessagesWaitingLight; + visible: parent.messagesWaiting; + anchors.right: exchangeMoneyTabIcon.left; + anchors.rightMargin: 9; + anchors.top: exchangeMoneyTabIcon.top; + anchors.topMargin: 4; + height: 10; + width: height; + radius: height/2; + color: "red"; } RalewaySemiBold { - text: "EXCHANGE MONEY"; + text: "ITEMS"; // Text size size: 16; // Anchors @@ -531,12 +558,24 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: hifi.colors.lightGray50; + color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } + + MouseArea { + id: inventoryTabMouseArea; + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + root.activeView = "walletInventory"; + tabButtonsContainer.resetTabButtonColors(); + } + onEntered: parent.color = hifi.colors.blueHighlight; + onExited: parent.color = root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; + } } @@ -546,7 +585,7 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: exchangeMoneyButtonContainer.right; + anchors.left: walletHomeButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; @@ -560,7 +599,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); } RalewaySemiBold { @@ -575,7 +614,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -585,6 +624,7 @@ Rectangle { MouseArea { id: sendMoneyTabMouseArea; anchors.fill: parent; + enabled: !WalletScriptingInterface.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "sendMoney"; @@ -595,67 +635,13 @@ Rectangle { } } - // "SECURITY" tab button - Rectangle { - id: securityButtonContainer; - visible: !walletSetup.visible; - color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black; - anchors.top: parent.top; - anchors.left: sendMoneyButtonContainer.right; - anchors.bottom: parent.bottom; - width: parent.width / tabButtonsContainer.numTabs; - - HiFiGlyphs { - id: securityTabIcon; - text: hifi.glyphs.lock; - // Size - size: 38; - // Anchors - anchors.horizontalCenter: parent.horizontalCenter; - anchors.top: parent.top; - anchors.topMargin: 2; - // Style - color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; - } - - RalewaySemiBold { - text: "SECURITY"; - // Text size - size: 16; - // Anchors - anchors.bottom: parent.bottom; - height: parent.height/2; - anchors.left: parent.left; - anchors.leftMargin: 4; - anchors.right: parent.right; - anchors.rightMargin: 4; - // Style - color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignTop; - } - MouseArea { - id: securityTabMouseArea; - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - root.activeView = "security"; - tabButtonsContainer.resetTabButtonColors(); - } - onEntered: parent.color = hifi.colors.blueHighlight; - onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black; - } - } - // "HELP" tab button Rectangle { id: helpButtonContainer; visible: !walletSetup.visible; color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: securityButtonContainer.right; + anchors.left: sendMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; @@ -707,16 +693,16 @@ Rectangle { function resetTabButtonColors() { walletHomeButtonContainer.color = hifi.colors.black; sendMoneyButtonContainer.color = hifi.colors.black; - securityButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black; + exchangeMoneyButtonContainer.color = hifi.colors.black; if (root.activeView === "walletHome") { walletHomeButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "sendMoney") { sendMoneyButtonContainer.color = hifi.colors.blueAccent; - } else if (root.activeView === "security") { - securityButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "help") { helpButtonContainer.color = hifi.colors.blueAccent; + } else if (root.activeView == "walletInventory") { + exchangeMoneyButtonContainer.color = hifi.colors.blueAccent; } } } @@ -782,18 +768,40 @@ Rectangle { break; case 'updateConnections': sendMoney.updateConnections(message.connections); + walletInventory.fromScript(message); break; case 'selectRecipient': case 'updateSelectedRecipientUsername': sendMoney.fromScript(message); + walletInventory.fromScript(message); break; case 'http.response': http.handleHttpResponse(message); + // Duplicate handler is required because we don't track referrer for `http` + walletInventory.fromScript(message); break; case 'palIsStale': case 'avatarDisconnected': // Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs. break; + case 'inspectionCertificate_setCertificateId': + inspectionCertificate.fromScript(message); + break; + case 'updatePurchases': + case 'purchases_showMyItems': + case 'updateWearables': + walletInventory.fromScript(message); + break; + case 'updateRecentActivityMessageLight': + walletHomeButtonContainer.messagesWaiting = message.messagesWaiting; + break; + case 'checkout_openRecentActivity': + if (root.activeView === "initialize") { + root.initialActiveViewAfterStatus5 = "walletHome"; + } else { + root.activeView = "walletHome"; + } + break; default: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } @@ -841,7 +849,8 @@ Rectangle { root.activeView = "initialize"; Commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { - sendToScript({method: 'goToPurchases'}); + root.activeView = "walletInventory"; + tabButtonsContainer.resetTabButtonColors(); } else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') { sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer}); } else { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml b/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml index 19065ee542..e7163a3641 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import "../common" as HifiCommerceCommon -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit Item { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 627da1d43f..4c8e1e6ca5 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 import QtQuick.Controls 2.2 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. @@ -29,7 +29,6 @@ Item { if (visible) { Commerce.balance(); transactionHistoryModel.getFirstPage(); - Commerce.getAvailableUpdates(); } else { refreshTimer.stop(); } @@ -179,28 +178,6 @@ Item { color: hifi.colors.baseGrayHighlight; } - RalewaySemiBold { - id: myPurchasesLink; - text: 'My Purchases'; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 26; - anchors.right: parent.right; - anchors.rightMargin: 20; - width: paintedWidth; - height: 30; - y: 4; - // Text size - size: 18; - // Style - color: hifi.colors.baseGrayHighlight; - horizontalAlignment: Text.AlignRight; - - onLinkActivated: { - sendSignalToWallet({method: 'goToPurchases_fromWalletHome'}); - } - } - HifiModels.PSFListModel { id: transactionHistoryModel; property int lastPendingCount: 0; diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index dc6ce45a74..1cecebc41b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon @@ -243,7 +243,6 @@ Item { height: 50; text: "Set Up Wallet"; onClicked: { - securityImageSelection.initModel(); root.activeView = "step_2"; } } @@ -267,124 +266,6 @@ Item { // FIRST PAGE END // - // - // SECURITY IMAGE SELECTION START - // - Item { - id: securityImageContainer; - visible: root.activeView === "step_2"; - // Anchors - anchors.top: titleBarContainer.bottom; - anchors.topMargin: 30; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - - // Text below title bar - RalewayRegular { - id: securityImageTitleHelper; - text: "Choose a Security Pic:"; - // Text size - size: 24; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - height: 50; - width: paintedWidth; - // Style - color: hifi.colors.white; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - - SecurityImageSelection { - id: securityImageSelection; - // Anchors - anchors.top: securityImageTitleHelper.bottom; - anchors.left: parent.left; - anchors.right: parent.right; - height: 300; - - Connections { - onSendSignalToWallet: { - sendSignalToWallet(msg); - } - } - } - - // Text below security images - RalewayRegular { - text: "Your security picture shows you that the service asking for your passphrase is authorized. You can change your secure picture at any time."; - // Text size - size: 18; - // Anchors - anchors.top: securityImageSelection.bottom; - anchors.topMargin: 40; - anchors.left: parent.left; - anchors.right: parent.right; - height: paintedHeight; - // Style - color: hifi.colors.white; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - - // Navigation Bar - Item { - // Size - width: parent.width; - height: 50; - // Anchors: - anchors.left: parent.left; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 50; - - // "Back" button - HifiControlsUit.Button { - color: hifi.buttons.noneBorderlessWhite; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - anchors.leftMargin: 20; - width: 200; - text: "Back" - onClicked: { - securityImageSelection.resetSelection(); - root.activeView = "step_1"; - } - } - - // "Next" button - HifiControlsUit.Button { - enabled: securityImageSelection.currentIndex !== -1; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.right: parent.right; - anchors.rightMargin: 20; - width: 200; - text: "Next"; - onClicked: { - root.lastPage = "step_2"; - var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex()) - Commerce.chooseSecurityImage(securityImagePath); - root.activeView = "step_3"; - passphraseSelection.clearPassphraseFields(); - } - } - } - } - // - // SECURITY IMAGE SELECTION END - // - // // SECURE PASSPHRASE SELECTION START // @@ -525,7 +406,6 @@ Item { width: 200; text: "Back" onClicked: { - securityImageSelection.resetSelection(); root.lastPage = "step_3"; root.activeView = "step_2"; } diff --git a/interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg b/interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg new file mode 100644 index 0000000000..7474f4ed57 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/dialogs/AboutDialog.qml b/interface/resources/qml/hifi/dialogs/AboutDialog.qml index b8e6e89aec..3d5d1a94a3 100644 --- a/interface/resources/qml/hifi/dialogs/AboutDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AboutDialog.qml @@ -10,7 +10,7 @@ import QtQuick 2.8 -import "../../styles-uit" +import stylesUit 1.0 import "../../windows" ScrollingWindow { diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 9a180a66f6..be17e65ab3 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "../" diff --git a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml index 579aa1cb1e..d26bf81e57 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import "../../styles-uit" +import stylesUit 1.0 Rectangle { width: 480 diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 0eeb252049..f665032b01 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -14,8 +14,8 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import ".." diff --git a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml index afe06897df..763f56b92b 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" Rectangle { diff --git a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml index 50df4dedbc..213dca8b48 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import Hifi 1.0 as Hifi -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Rectangle { id: root diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index 24798af21a..4cfc99e0eb 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" Rectangle { diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml index d5c5a5ee02..e86dfd7554 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Column { id: root diff --git a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml index ab53f03477..bb3d668850 100644 --- a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml +++ b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" Rectangle { diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 018c8f5737..b0f17ff841 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "../" @@ -88,6 +88,10 @@ Rectangle { checkMenu.restart(); } + Component.onDestruction: { + keyboard.raised = false; + } + function updateRunningScripts() { function simplify(path) { // trim URI querystring/fragment diff --git a/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml b/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml index ce1abc6154..b1aa8e5c45 100644 --- a/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml @@ -1,7 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import "../../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls Column { width: pane.contentWidth diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml new file mode 100644 index 0000000000..b754cb06ab --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -0,0 +1,344 @@ +// +// Security.qml +// qml\hifi\dialogs\security +// +// Security +// +// Created by Zach Fox on 2018-10-31 +// 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 +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtGraphicalEffects 1.0 +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit +import "qrc:////qml//controls" as HifiControls +import "qrc:////qml//hifi//commerce//common" as HifiCommerceCommon + +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } + + id: root; + color: hifi.colors.baseGray; + + property string title: "Security Settings"; + property bool walletSetUp; + + QtObject { + id: margins + property real paddings: root.width / 20.25 + + property real sizeCheckBox: root.width / 13.5 + property real sizeText: root.width / 2.5 + property real sizeLevel: root.width / 5.8 + property real sizeDesktop: root.width / 5.8 + property real sizeVR: root.width / 13.5 + } + + Connections { + target: Commerce; + + onWalletStatusResult: { + if (walletStatus === 5) { + Commerce.getSecurityImage(); + root.walletSetUp = true; + } else { + root.walletSetUp = false; + } + } + + onSecurityImageResult: { + if (exists) { + currentSecurityPicture.source = ""; + currentSecurityPicture.source = "image://security/securityImage"; + } + } + } + + Component.onCompleted: { + Commerce.getWalletStatus(); + } + + HifiCommerceCommon.CommerceLightbox { + z: 996; + id: lightboxPopup; + visible: false; + anchors.fill: parent; + } + + SecurityImageChange { + id: securityImageChange; + visible: false; + z: 997; + anchors.top: usernameText.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + Connections { + onSendSignalToParent: { + securityImageChange.visible = false; + } + } + } + + // Username Text + HifiStylesUit.RalewayRegular { + id: usernameText; + text: Account.username === "Unknown user" ? "Please Log In" : Account.username; + // Text size + size: 24; + // Style + color: hifi.colors.white; + elide: Text.ElideRight; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 60; + } + + Item { + id: pleaseLogInContainer; + visible: Account.username === "Unknown user"; + anchors.top: usernameText.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + HifiStylesUit.RalewayRegular { + text: "Please log in for security settings." + // Text size + size: 24; + // Style + color: hifi.colors.white; + // Anchors + anchors.bottom: openLoginButton.top; + anchors.left: parent.left; + anchors.right: parent.right; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + height: 60; + } + + HifiControlsUit.Button { + id: openLoginButton; + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.centerIn: parent; + width: 140; + height: 40; + text: "Log In"; + onClicked: { + DialogsManager.showLoginDialog(); + } + } + } + + Item { + id: securitySettingsContainer; + visible: !pleaseLogInContainer.visible; + anchors.top: usernameText.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + Item { + id: accountContainer; + anchors.top: securitySettingsContainer.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: childrenRect.height; + + Rectangle { + id: accountHeaderContainer; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 55; + color: hifi.colors.baseGrayHighlight; + + HifiStylesUit.RalewaySemiBold { + text: "Account"; + anchors.fill: parent; + anchors.leftMargin: 20; + color: hifi.colors.white; + size: 18; + } + } + + Item { + id: keepMeLoggedInContainer; + anchors.top: accountHeaderContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + + HifiControlsUit.CheckBox { + id: autoLogoutCheckbox; + checked: Settings.getValue("keepMeLoggedIn", false); + text: "Keep Me Logged In" + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: 20; + boxSize: 24; + labelFontSize: 18; + colorScheme: hifi.colorSchemes.dark + color: hifi.colors.white; + width: 240; + onCheckedChanged: { + Settings.setValue("keepMeLoggedIn", checked); + if (checked) { + Settings.setValue("keepMeLoggedIn/savedUsername", Account.username); + } else { + Settings.setValue("keepMeLoggedIn/savedUsername", ""); + } + } + } + + HifiStylesUit.RalewaySemiBold { + id: autoLogoutHelp; + text: '[?]'; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.right: autoLogoutCheckbox.right; + width: 30; + height: 30; + // Text size + size: 18; + // Style + color: hifi.colors.blueHighlight; + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.color = hifi.colors.blueAccent; + } + onExited: { + parent.color = hifi.colors.blueHighlight; + } + onClicked: { + lightboxPopup.titleText = "Keep Me Logged In"; + lightboxPopup.bodyText = "If you choose to stay logged in, ensure that this is a trusted device.\n\n" + + "Also, remember that logging out may not disconnect you from a domain."; + lightboxPopup.button1text = "OK"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.visible = true; + } + } + } + } + } + + Item { + id: walletContainer; + anchors.top: accountContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: childrenRect.height; + + Rectangle { + id: walletHeaderContainer; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 55; + color: hifi.colors.baseGrayHighlight; + + HifiStylesUit.RalewaySemiBold { + text: "Wallet"; + anchors.fill: parent; + anchors.leftMargin: 20; + color: hifi.colors.white; + size: 18; + } + } + + Item { + id: walletSecurityPictureContainer; + visible: root.walletSetUp; + anchors.top: walletHeaderContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + + Image { + id: currentSecurityPicture; + source: ""; + visible: true; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: height; + mipmap: true; + cache: false; + } + + HifiStylesUit.RalewaySemiBold { + id: securityPictureText; + text: "Wallet Security Picture"; + // Anchors + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: currentSecurityPicture.right; + anchors.leftMargin: 12; + width: paintedWidth; + // Text size + size: 18; + // Style + color: hifi.colors.white; + } + + // "Change Security Pic" button + HifiControlsUit.Button { + id: changeSecurityImageButton; + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.left: securityPictureText.right; + anchors.leftMargin: 12; + anchors.verticalCenter: parent.verticalCenter; + width: 140; + height: 40; + text: "Change"; + onClicked: { + securityImageChange.visible = true; + securityImageChange.initModel(); + } + } + } + + Item { + id: walletNotSetUpContainer; + visible: !root.walletSetUp; + anchors.top: walletHeaderContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 60; + + HifiStylesUit.RalewayRegular { + text: "Your wallet is not set up.\n" + + "Open the ASSETS app to get started."; + // Anchors + anchors.fill: parent; + // Text size + size: 18; + // Style + color: hifi.colors.white; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + } + } + } +} diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml similarity index 88% rename from interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml rename to interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml index 01df18352b..e563bd13ca 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml @@ -1,11 +1,11 @@ // // SecurityImageChange.qml -// qml/hifi/commerce/wallet +// qml\hifi\dialogs\security // -// SecurityImageChange +// Security // -// Created by Zach Fox on 2017-08-18 -// Copyright 2017 High Fidelity, Inc. +// Created by Zach Fox on 2018-10-31 +// 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 @@ -13,16 +13,17 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit +import "qrc:////qml//controls" as HifiControls // references XXX from root context -Item { - HifiConstants { id: hifi; } +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } id: root; + color: hifi.colors.baseGray; property bool justSubmitted: false; Connections { @@ -33,7 +34,7 @@ Item { securityImageChangePageSecurityImage.source = "image://security/securityImage"; if (exists) { // Success submitting new security image if (root.justSubmitted) { - sendSignalToWallet({method: "walletSecurity_changeSecurityImageSuccess"}); + sendSignalToParent({method: "walletSecurity_changeSecurityImageSuccess"}); root.justSubmitted = false; } } else if (root.justSubmitted) { @@ -72,7 +73,7 @@ Item { anchors.bottom: parent.bottom; height: 22; // Lock icon - HiFiGlyphs { + HifiStylesUit.HiFiGlyphs { id: lockIcon; text: hifi.glyphs.lock; anchors.bottom: parent.bottom; @@ -83,8 +84,8 @@ Item { verticalAlignment: Text.AlignBottom; color: hifi.colors.white; } - // "Security image" text below pic - RalewayRegular { + // "Security image" text below image + HifiStylesUit.RalewayRegular { id: securityImageText; text: "SECURITY PIC"; // Text size @@ -116,9 +117,9 @@ Item { anchors.bottom: parent.bottom; // "Change Security Image" text - RalewaySemiBold { + HifiStylesUit.RalewaySemiBold { id: securityImageTitle; - text: "Change Security Pic:"; + text: "Change Security Image:"; // Text size size: 18; anchors.top: parent.top; @@ -139,12 +140,6 @@ Item { anchors.right: parent.right; anchors.rightMargin: 16; height: 300; - - Connections { - onSendSignalToWallet: { - sendSignalToWallet(msg); - } - } } // Navigation Bar @@ -169,7 +164,7 @@ Item { width: 150; text: "Cancel" onClicked: { - sendSignalToWallet({method: "walletSecurity_changeSecurityImageCancelled"}); + sendSignalToParent({method: "walletSecurity_changeSecurityImageCancelled"}); } } @@ -197,7 +192,7 @@ Item { // SECURITY IMAGE SELECTION END // - signal sendSignalToWallet(var msg); + signal sendSignalToParent(var msg); function initModel() { securityImageSelection.initModel(); diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageModel.qml similarity index 89% rename from interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml rename to interface/resources/qml/hifi/dialogs/security/SecurityImageModel.qml index b8e9db09ab..946f979c1a 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageModel.qml @@ -1,11 +1,11 @@ // // SecurityImageModel.qml -// qml/hifi/commerce +// qml\hifi\dialogs\security // -// SecurityImageModel +// Security // -// Created by Zach Fox on 2017-08-17 -// Copyright 2017 High Fidelity, Inc. +// Created by Zach Fox on 2018-10-31 +// 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 diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml similarity index 81% rename from interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml rename to interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml index 599c6a1851..3451cbcf50 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml @@ -1,11 +1,11 @@ // // SecurityImageSelection.qml -// qml/hifi/commerce/wallet +// qml\hifi\dialogs\security // -// SecurityImageSelection +// Security // -// Created by Zach Fox on 2017-08-17 -// Copyright 2017 High Fidelity, Inc. +// Created by Zach Fox on 2018-10-31 +// 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 @@ -13,14 +13,14 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit +import "qrc:////qml//controls" as HifiControls // references XXX from root context Item { - HifiConstants { id: hifi; } + HifiStylesUit.HifiConstants { id: hifi; } id: root; property alias currentIndex: securityImageGrid.currentIndex; @@ -64,17 +64,15 @@ Item { } } highlight: Rectangle { - width: securityImageGrid.cellWidth; - height: securityImageGrid.cellHeight; - color: hifi.colors.blueHighlight; - } + width: securityImageGrid.cellWidth; + height: securityImageGrid.cellHeight; + color: hifi.colors.blueHighlight; + } } // // FUNCTION DEFINITIONS START // - signal sendSignalToWallet(var msg); - function getImagePathFromImageID(imageID) { return (imageID ? gridModel.getImagePathFromImageID(imageID) : ""); } diff --git a/interface/resources/qml/hifi/commerce/wallet/images/01.jpg b/interface/resources/qml/hifi/dialogs/security/images/01.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/01.jpg rename to interface/resources/qml/hifi/dialogs/security/images/01.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/02.jpg b/interface/resources/qml/hifi/dialogs/security/images/02.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/02.jpg rename to interface/resources/qml/hifi/dialogs/security/images/02.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/03.jpg b/interface/resources/qml/hifi/dialogs/security/images/03.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/03.jpg rename to interface/resources/qml/hifi/dialogs/security/images/03.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/04.jpg b/interface/resources/qml/hifi/dialogs/security/images/04.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/04.jpg rename to interface/resources/qml/hifi/dialogs/security/images/04.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/05.jpg b/interface/resources/qml/hifi/dialogs/security/images/05.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/05.jpg rename to interface/resources/qml/hifi/dialogs/security/images/05.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/06.jpg b/interface/resources/qml/hifi/dialogs/security/images/06.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/06.jpg rename to interface/resources/qml/hifi/dialogs/security/images/06.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/07.jpg b/interface/resources/qml/hifi/dialogs/security/images/07.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/07.jpg rename to interface/resources/qml/hifi/dialogs/security/images/07.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/08.jpg b/interface/resources/qml/hifi/dialogs/security/images/08.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/08.jpg rename to interface/resources/qml/hifi/dialogs/security/images/08.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/09.jpg b/interface/resources/qml/hifi/dialogs/security/images/09.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/09.jpg rename to interface/resources/qml/hifi/dialogs/security/images/09.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/10.jpg b/interface/resources/qml/hifi/dialogs/security/images/10.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/10.jpg rename to interface/resources/qml/hifi/dialogs/security/images/10.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/11.jpg b/interface/resources/qml/hifi/dialogs/security/images/11.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/11.jpg rename to interface/resources/qml/hifi/dialogs/security/images/11.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/12.jpg b/interface/resources/qml/hifi/dialogs/security/images/12.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/12.jpg rename to interface/resources/qml/hifi/dialogs/security/images/12.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/13.jpg b/interface/resources/qml/hifi/dialogs/security/images/13.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/13.jpg rename to interface/resources/qml/hifi/dialogs/security/images/13.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/14.jpg b/interface/resources/qml/hifi/dialogs/security/images/14.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/14.jpg rename to interface/resources/qml/hifi/dialogs/security/images/14.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/15.jpg b/interface/resources/qml/hifi/dialogs/security/images/15.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/15.jpg rename to interface/resources/qml/hifi/dialogs/security/images/15.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/16.jpg b/interface/resources/qml/hifi/dialogs/security/images/16.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/16.jpg rename to interface/resources/qml/hifi/dialogs/security/images/16.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/17.jpg b/interface/resources/qml/hifi/dialogs/security/images/17.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/17.jpg rename to interface/resources/qml/hifi/dialogs/security/images/17.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/18.jpg b/interface/resources/qml/hifi/dialogs/security/images/18.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/18.jpg rename to interface/resources/qml/hifi/dialogs/security/images/18.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/19.jpg b/interface/resources/qml/hifi/dialogs/security/images/19.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/19.jpg rename to interface/resources/qml/hifi/dialogs/security/images/19.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/20.jpg b/interface/resources/qml/hifi/dialogs/security/images/20.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/20.jpg rename to interface/resources/qml/hifi/dialogs/security/images/20.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/21.jpg b/interface/resources/qml/hifi/dialogs/security/images/21.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/21.jpg rename to interface/resources/qml/hifi/dialogs/security/images/21.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/22.jpg b/interface/resources/qml/hifi/dialogs/security/images/22.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/22.jpg rename to interface/resources/qml/hifi/dialogs/security/images/22.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/23.jpg b/interface/resources/qml/hifi/dialogs/security/images/23.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/23.jpg rename to interface/resources/qml/hifi/dialogs/security/images/23.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/24.jpg b/interface/resources/qml/hifi/dialogs/security/images/24.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/24.jpg rename to interface/resources/qml/hifi/dialogs/security/images/24.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/25.jpg b/interface/resources/qml/hifi/dialogs/security/images/25.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/25.jpg rename to interface/resources/qml/hifi/dialogs/security/images/25.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/26.jpg b/interface/resources/qml/hifi/dialogs/security/images/26.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/26.jpg rename to interface/resources/qml/hifi/dialogs/security/images/26.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/27.jpg b/interface/resources/qml/hifi/dialogs/security/images/27.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/27.jpg rename to interface/resources/qml/hifi/dialogs/security/images/27.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/28.jpg b/interface/resources/qml/hifi/dialogs/security/images/28.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/28.jpg rename to interface/resources/qml/hifi/dialogs/security/images/28.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/29.jpg b/interface/resources/qml/hifi/dialogs/security/images/29.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/29.jpg rename to interface/resources/qml/hifi/dialogs/security/images/29.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/30.jpg b/interface/resources/qml/hifi/dialogs/security/images/30.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/30.jpg rename to interface/resources/qml/hifi/dialogs/security/images/30.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/31.jpg b/interface/resources/qml/hifi/dialogs/security/images/31.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/31.jpg rename to interface/resources/qml/hifi/dialogs/security/images/31.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/32.jpg b/interface/resources/qml/hifi/dialogs/security/images/32.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/32.jpg rename to interface/resources/qml/hifi/dialogs/security/images/32.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/33.jpg b/interface/resources/qml/hifi/dialogs/security/images/33.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/33.jpg rename to interface/resources/qml/hifi/dialogs/security/images/33.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/34.jpg b/interface/resources/qml/hifi/dialogs/security/images/34.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/34.jpg rename to interface/resources/qml/hifi/dialogs/security/images/34.jpg diff --git a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml index e3115a5738..6b2aa331e8 100644 --- a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml +++ b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml @@ -10,9 +10,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index 6706830537..b8bbd71f33 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -11,9 +11,9 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "../../dialogs" import "../../dialogs/preferences" import "tabletWindows" diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index 4acced86ce..099c53cda2 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -49,5 +49,11 @@ StackView { if (currentItem && currentItem.fromScript) currentItem.fromScript(message); } + + Component.onDestruction: { + if (KeyboardScriptingInterface.raised) { + KeyboardScriptingInterface.raised = false; + } + } } diff --git a/interface/resources/qml/hifi/tablet/EditEntityList.qml b/interface/resources/qml/hifi/tablet/EditEntityList.qml index d484885103..d2fb99ea0a 100644 --- a/interface/resources/qml/hifi/tablet/EditEntityList.qml +++ b/interface/resources/qml/hifi/tablet/EditEntityList.qml @@ -4,8 +4,8 @@ import QtWebChannel 1.0 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 WebView { diff --git a/interface/resources/qml/hifi/tablet/EditTabButton.qml b/interface/resources/qml/hifi/tablet/EditTabButton.qml index 13894f4d15..5fc4341eb8 100644 --- a/interface/resources/qml/hifi/tablet/EditTabButton.qml +++ b/interface/resources/qml/hifi/tablet/EditTabButton.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabButton { id: control diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index 4ac8755570..d9549feeb0 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -4,8 +4,8 @@ import QtWebChannel 1.0 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabBar { id: editTabView @@ -175,7 +175,7 @@ TabBar { method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } }); - editTabView.currentIndex = 4 + editTabView.currentIndex = 2 } } @@ -279,21 +279,6 @@ TabBar { } } - EditTabButton { - title: "P" - active: true - enabled: true - property string originalUrl: "" - - property Component visualItem: Component { - WebView { - id: particleExplorerWebView - url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" - enabled: true - } - } - } - function fromScript(message) { switch (message.method) { case 'selectTab': @@ -326,9 +311,6 @@ TabBar { case 'grid': editTabView.currentIndex = 3; break; - case 'particle': - editTabView.currentIndex = 4; - break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml index 00084b8ca9..dc7ad683e3 100644 --- a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml @@ -4,8 +4,8 @@ import QtWebChannel 1.0 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabBar { id: editTabView @@ -18,7 +18,6 @@ TabBar { readonly property int create: 0 readonly property int properties: 1 readonly property int grid: 2 - readonly property int particle: 3 } readonly property HifiConstants hifi: HifiConstants {} @@ -182,7 +181,7 @@ TabBar { method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } }); - editTabView.currentIndex = tabIndex.particle + editTabView.currentIndex = tabIndex.properties } } @@ -271,21 +270,6 @@ TabBar { } } - EditTabButton { - title: "P" - active: true - enabled: true - property string originalUrl: "" - - property Component visualItem: Component { - WebView { - id: particleExplorerWebView - url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" - enabled: true - } - } - } - function fromScript(message) { switch (message.method) { case 'selectTab': @@ -299,7 +283,7 @@ TabBar { // Changes the current tab based on tab index or title as input function selectTab(id) { if (typeof id === 'number') { - if (id >= tabIndex.create && id <= tabIndex.particle) { + if (id >= tabIndex.create && id <= tabIndex.grid) { editTabView.currentIndex = id; } else { console.warn('Attempt to switch to invalid tab:', id); @@ -315,9 +299,6 @@ TabBar { case 'grid': editTabView.currentIndex = tabIndex.grid; break; - case 'particle': - editTabView.currentIndex = tabIndex.particle; - break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/interface/resources/qml/hifi/tablet/InputRecorder.qml b/interface/resources/qml/hifi/tablet/InputRecorder.qml index 527a6cacb4..9b63a612a8 100644 --- a/interface/resources/qml/hifi/tablet/InputRecorder.qml +++ b/interface/resources/qml/hifi/tablet/InputRecorder.qml @@ -9,8 +9,8 @@ import QtQuick 2.5 import Hifi 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "../../dialogs" diff --git a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml index 526a42f8e2..dde372648b 100644 --- a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml @@ -13,8 +13,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../../styles-uit" -import "../../controls-uit" +import stylesUit 1.0 +import controlsUit 1.0 import "../dialogs" Rectangle { diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index 10b844c987..9540979479 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../../styles-uit" -import "../../controls-uit" +import stylesUit 1.0 +import controlsUit 1.0 import "../dialogs" Rectangle { @@ -116,9 +116,14 @@ Rectangle { Column { id: column2 width: 200 - height: 400 + height: 600 spacing: 10 + CheckBox { + id: grabbable + text: qsTr("Grabbable") + } + CheckBox { id: dynamic text: qsTr("Dynamic") @@ -217,9 +222,10 @@ Rectangle { newModelDialog.sendToScript({ method: "newModelDialogAdd", params: { - textInput: modelURL.text, - checkBox: dynamic.checked, - comboBox: collisionType.currentIndex + url: modelURL.text, + dynamic: dynamic.checked, + collisionShapeIndex: collisionType.currentIndex, + grabbable: grabbable.checked } }); } diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index c2aff08e35..2fc5cc4196 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -9,9 +9,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "." @@ -822,11 +822,44 @@ Flickable { } } + Row { + id: outOfRangeDataStrategyRow + anchors.top: viveInDesktop.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 10 + spacing: 15 + + RalewayRegular { + id: outOfRangeDataStrategyLabel + size: 12 + text: "Out Of Range Data Strategy:" + color: hifi.colors.lightGrayText + topPadding: 5 + } + + HifiControls.ComboBox { + id: outOfRangeDataStrategyComboBox + + height: 25 + width: 100 + + editable: true + colorScheme: hifi.colorSchemes.dark + model: ["None", "Freeze", "Drop"] + label: "" + + onCurrentIndexChanged: { + sendConfigurationSettings(); + } + } + } + RalewayBold { id: viveDesktopText - size: 10 + size: 12 text: "Use " + stack.selectedPlugin + " devices in desktop mode" - color: hifi.colors.white + color: hifi.colors.lightGrayText anchors { left: viveInDesktop.right @@ -946,6 +979,7 @@ Flickable { viveInDesktop.checked = desktopMode; hmdInDesktop.checked = hmdDesktopPosition; + outOfRangeDataStrategyComboBox.currentIndex = outOfRangeDataStrategyComboBox.model.indexOf(settings.outOfRangeDataStrategy); initializeButtonState(); updateCalibrationText(); @@ -1107,7 +1141,8 @@ Flickable { "armCircumference": armCircumference.realValue, "shoulderWidth": shoulderWidth.realValue, "desktopMode": viveInDesktop.checked, - "hmdDesktopTracking": hmdInDesktop.checked + "hmdDesktopTracking": hmdInDesktop.checked, + "outOfRangeDataStrategy": outOfRangeDataStrategyComboBox.model[outOfRangeDataStrategyComboBox.currentIndex] } return settingsObject; diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 3d518289fb..b8972378ad 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -18,8 +18,8 @@ import "../../styles" import "../../windows" import "../" import "../toolbars" -import "../../styles-uit" as HifiStyles -import "../../controls-uit" as HifiControls +import stylesUit 1.0 as HifiStyles +import controlsUit 1.0 as HifiControls import QtQuick.Controls 2.2 as QQC2 import QtQuick.Templates 2.2 as T @@ -56,10 +56,13 @@ StackView { Qt.callLater(function() { addressBarDialog.keyboardEnabled = HMD.active; addressLine.forceActiveFocus(); + addressBarDialog.raised = true; }) } + Component.onDestruction: { root.parentChanged.disconnect(center); + keyboard.raised = false; } function center() { @@ -218,6 +221,11 @@ StackView { leftMargin: 8; verticalCenter: addressLineContainer.verticalCenter; } + + onFocusChanged: { + addressBarDialog.raised = focus; + } + onTextChanged: { updateLocationText(text.length > 0); } diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 1922b02f93..934ed91995 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3 import TabletScriptingInterface 1.0 import "." -import "../../styles-uit" +import stylesUit 1.0 import "../audio" as HifiAudio Item { diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml index 6540d53fca..267fb9f0cf 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenu.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -7,7 +7,7 @@ import QtWebEngine 1.1 import "." -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" FocusScope { diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml index 74f175e049..25db90c771 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: root diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index b632a17e57..73b0405984 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import TabletScriptingInterface 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "." FocusScope { diff --git a/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml b/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml index d69d760b95..ce4e641476 100644 --- a/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 import "../dialogs/content" Item { diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 9c027308b8..d55ec363f0 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -129,8 +129,21 @@ Windows.ScrollingWindow { height: pane.scrollHeight width: pane.contentWidth - anchors.left: parent.left - anchors.top: parent.top + + // this might be looking not clear from the first look + // but loader.parent is not tabletRoot and it can be null! + // unfortunately we can't use conditional bindings here due to https://bugreports.qt.io/browse/QTBUG-22005 + + onParentChanged: { + if (parent) { + anchors.left = Qt.binding(function() { return parent.left }) + anchors.top = Qt.binding(function() { return parent.top }) + } else { + anchors.left = undefined + anchors.top = undefined + } + } + signal loaded; onWidthChanged: { diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml index 871d1c92a9..8e91655dda 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -16,8 +16,8 @@ import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.3 import ".." -import "../../../controls-uit" -import "../../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../../../windows" import "../../../dialogs/fileDialog" diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index 3708f75114..a5d7b23df6 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import "." import "./preferences" -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { id: dialog @@ -242,6 +242,10 @@ Item { keyboardEnabled = HMD.active; } + Component.onDestruction: { + keyboard.raised = false; + } + onKeyboardRaisedChanged: { if (keyboardEnabled && keyboardRaised) { var delta = mouseArea.mouseY - (dialog.height - footer.height - keyboard.raisedHeight -hifi.dimensions.controlLineHeight); diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index 6ac3f706e4..57fdeb482b 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import Hifi 1.0 import "../../../../dialogs/preferences" -import "../../../../controls-uit" as HiFiControls -import "../../../../styles-uit" +import controlsUit 1.0 as HiFiControls +import stylesUit 1.0 import "." Preference { diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml index 8c0e934971..36b927f5f9 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../../../dialogs" -import "../../../../controls-uit" +import controlsUit 1.0 import "../" Preference { diff --git a/interface/resources/qml/hifi/tts/TTS.qml b/interface/resources/qml/hifi/tts/TTS.qml new file mode 100644 index 0000000000..d9507f6084 --- /dev/null +++ b/interface/resources/qml/hifi/tts/TTS.qml @@ -0,0 +1,314 @@ +// +// TTS.qml +// +// TTS App +// +// Created by Zach Fox on 2018-10-10 +// 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 +// + +import Hifi 1.0 as Hifi +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls + +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } + + id: root; + // Style + color: hifi.colors.darkGray; + property bool keyboardRaised: false; + + // + // TITLE BAR START + // + Item { + id: titleBarContainer; + // Size + width: root.width; + height: 50; + // Anchors + anchors.left: parent.left; + anchors.top: parent.top; + + // Title bar text + HifiStylesUit.RalewaySemiBold { + id: titleBarText; + text: "Text-to-Speech"; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.leftMargin: 16; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + } + } + // + // TITLE BAR END + // + + + Item { + id: tagButtonContainer; + anchors.top: titleBarContainer.bottom; + anchors.topMargin: 2; + anchors.left: parent.left; + anchors.right: parent.right; + height: 70; + + HifiStylesUit.RalewaySemiBold { + id: tagButtonTitle; + text: "Insert Tag:"; + // Text size + size: 18; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 35; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + HifiControlsUit.Button { + id: pitch10Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: parent.left; + anchors.leftMargin: 3; + width: parent.width/6 - 6; + height: 30; + text: "Pitch 10"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: pitch0Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: pitch10Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Pitch 0"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: pitchNeg10Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: pitch0Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Pitch -10"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: speed5Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: pitchNeg10Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Speed 5"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: speed0Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: speed5Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Speed 0"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: speedNeg10Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: speed0Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Speed -10"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + } + + Item { + anchors.top: tagButtonContainer.bottom; + anchors.topMargin: 8; + anchors.bottom: keyboardContainer.top; + anchors.bottomMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + + TextArea { + id: messageToSpeak; + font.family: "Fira Sans SemiBold"; + font.pixelSize: 20; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: speakButton.top; + anchors.bottomMargin: 8; + // Style + background: Rectangle { + anchors.fill: parent; + color: parent.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow; + border.width: parent.activeFocus ? 1 : 0; + border.color: parent.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; + } + color: hifi.colors.white; + textFormat: TextEdit.PlainText; + wrapMode: TextEdit.Wrap; + activeFocusOnPress: true; + activeFocusOnTab: true; + Keys.onPressed: { + if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) { + TextToSpeech.speakText(messageToSpeak.text, 480, 10, 24000, 16, true); + event.accepted = true; + } + } + + HifiStylesUit.FiraSansRegular { + text: "Input Text to Speak..."; + size: 20; + anchors.fill: parent; + anchors.topMargin: 4; + anchors.leftMargin: 4; + color: hifi.colors.lightGrayText; + visible: !parent.activeFocus && messageToSpeak.text === ""; + verticalAlignment: Text.AlignTop; + } + } + + HifiControlsUit.Button { + id: speakButton; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + width: 215; + height: 40; + text: "Speak"; + onClicked: { + TextToSpeech.speakText(messageToSpeak.text, 480, 10, 24000, 16, true); + } + } + + HifiControlsUit.Button { + id: clearButton; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.right: speakButton.left; + anchors.rightMargin: 16; + anchors.bottom: parent.bottom; + width: 100; + height: 40; + text: "Clear"; + onClicked: { + messageToSpeak.text = ""; + } + } + + HifiControlsUit.Button { + id: stopButton; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.red; + colorScheme: hifi.colorSchemes.dark; + anchors.right: clearButton.left; + anchors.rightMargin: 16; + anchors.bottom: parent.bottom; + width: 100; + height: 40; + text: "Stop Last"; + onClicked: { + TextToSpeech.stopLastSpeech(); + } + } + } + + Item { + id: keyboardContainer; + z: 998; + visible: keyboard.raised; + property bool punctuationMode: false; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + + HifiControlsUit.Keyboard { + id: keyboard; + raised: HMD.mounted && root.keyboardRaised; + numeric: parent.punctuationMode; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + } + } +} diff --git a/interface/resources/qml/styles-uit/AnonymousProRegular.qml b/interface/resources/qml/styles-uit/AnonymousProRegular.qml index 431ecd0f38..87a91f183a 100644 --- a/interface/resources/qml/styles-uit/AnonymousProRegular.qml +++ b/interface/resources/qml/styles-uit/AnonymousProRegular.qml @@ -1,20 +1,4 @@ -// -// AnonymousProRegular.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Anonymous Pro" +AnonymousProRegular { } diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml index d227cb4869..d677cd06ca 100644 --- a/interface/resources/qml/styles-uit/ButtonLabel.qml +++ b/interface/resources/qml/styles-uit/ButtonLabel.qml @@ -1,16 +1,4 @@ -// -// ButtonLabel.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayBold { - font.pixelSize: hifi.fontSizes.buttonLabel +ButtonLabel { } diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/styles-uit/FiraSansRegular.qml index 05f6ecf74b..65ee891443 100644 --- a/interface/resources/qml/styles-uit/FiraSansRegular.qml +++ b/interface/resources/qml/styles-uit/FiraSansRegular.qml @@ -1,20 +1,4 @@ -// -// FiraSansRegular.qml -// -// Created by David Rowe on 12 May 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Fira Sans" +FiraSansRegular { } diff --git a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml index 32554c2f25..635523feb9 100644 --- a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml +++ b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml @@ -1,20 +1,4 @@ -// -// FiraSansSemiBold.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Fira Sans SemiBold" +FiraSansSemiBold { } diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/styles-uit/HiFiGlyphs.qml index 07f0212f0c..646cfc2988 100644 --- a/interface/resources/qml/styles-uit/HiFiGlyphs.qml +++ b/interface/resources/qml/styles-uit/HiFiGlyphs.qml @@ -1,22 +1,4 @@ -// -// HiFiGlyphs.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 - -Text { - id: root - property int size: 32 - font.pixelSize: size - width: size - height: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "hifi-glyphs" +HiFiGlyphs { } diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 595c393de9..24b24cf019 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -1,353 +1,4 @@ -// -// HiFiConstants.qml -// -// Created by Bradley Austin Davis on 28 Apr 2015 -// Copyright 2015 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import QtQuick.Window 2.2 - -QtObject { - - function glyphForIcon(icon) { - // Translates icon enum to glyph char. - var glyph; - switch (icon) { - case icons.information: - glyph = glyphs.info; - break; - case icons.question: - glyph = glyphs.question; - break; - case icons.warning: - glyph = glyphs.alert; - break; - case icons.critical: - glyph = glyphs.error; - break; - case icons.placemark: - glyph = glyphs.placemark; - break; - default: - glyph = glyphs.noIcon; - } - return glyph; - } - - readonly property QtObject colors: QtObject { - // Base colors - readonly property color baseGray: "#393939" - readonly property color darkGray: "#121212" - readonly property color baseGrayShadow: "#252525" - readonly property color baseGrayHighlight: "#575757" - readonly property color lightGray: "#6a6a6a" - readonly property color lightGrayText: "#afafaf" - readonly property color faintGray: "#e3e3e3" - readonly property color primaryHighlight: "#00b4ef" - readonly property color blueHighlight: "#00b4ef" - readonly property color blueAccent: "#0093C5" - readonly property color redHighlight: "#EA4C5F" - readonly property color redAccent: "#C62147" - readonly property color greenHighlight: "#1ac567" - readonly property color greenShadow: "#359D85" - readonly property color orangeHighlight: "#FFC49C" - readonly property color orangeAccent: "#FF6309" - readonly property color indigoHighlight: "#C0D2FF" - readonly property color indigoAccent: "#9495FF" - readonly property color magentaHighlight: "#EF93D1" - readonly property color magentaAccent: "#A2277C" - readonly property color checkboxCheckedRed: "#FF0000" - readonly property color checkboxCheckedBorderRed: "#D00000" - readonly property color lightBlueHighlight: "#d6f6ff" - - // Semitransparent - readonly property color darkGray30: "#4d121212" - readonly property color darkGray0: "#00121212" - readonly property color baseGrayShadow60: "#99252525" - readonly property color baseGrayShadow50: "#80252525" - readonly property color baseGrayShadow25: "#40252525" - readonly property color baseGrayHighlight40: "#66575757" - readonly property color baseGrayHighlight15: "#26575757" - readonly property color lightGray50: "#806a6a6a" - readonly property color lightGrayText80: "#ccafafaf" - readonly property color faintGray80: "#cce3e3e3" - readonly property color faintGray50: "#80e3e3e3" - - // Other colors - readonly property color white: "#ffffff" - readonly property color gray: "#808080" - readonly property color black: "#000000" - readonly property color locked: "#252525" - // Semitransparent - readonly property color white50: "#80ffffff" - readonly property color white30: "#4dffffff" - readonly property color white25: "#40ffffff" - readonly property color transparent: "#00ffffff" - - // Control specific colors - readonly property color tableRowLightOdd: "#fafafa" - readonly property color tableRowLightEven: "#eeeeee" // Equivavlent to "#1a575757" over #e3e3e3 background - readonly property color tableRowDarkOdd: "#2e2e2e" // Equivalent to "#80393939" over #404040 background - readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background - readonly property color tableBackgroundLight: tableRowLightEven - readonly property color tableBackgroundDark: tableRowDarkEven - readonly property color tableScrollHandleLight: "#DDDDDD" - readonly property color tableScrollHandleDark: "#707070" - readonly property color tableScrollBackgroundLight: tableRowLightOdd - readonly property color tableScrollBackgroundDark: "#323232" - readonly property color checkboxLightStart: "#ffffff" - readonly property color checkboxLightFinish: "#afafaf" - readonly property color checkboxDarkStart: "#7d7d7d" - readonly property color checkboxDarkFinish: "#6b6a6b" - readonly property color checkboxChecked: primaryHighlight - readonly property color checkboxCheckedBorder: "#36cdff" - readonly property color sliderGutterLight: "#d4d4d4" - readonly property color sliderGutterDark: "#252525" - readonly property color sliderBorderLight: "#afafaf" - readonly property color sliderBorderDark: "#7d7d7d" - readonly property color sliderLightStart: "#ffffff" - readonly property color sliderLightFinish: "#afafaf" - readonly property color sliderDarkStart: "#7d7d7d" - readonly property color sliderDarkFinish: "#6b6a6b" - readonly property color dropDownPressedLight: "#d4d4d4" - readonly property color dropDownPressedDark: "#afafaf" - readonly property color dropDownLightStart: "#ffffff" - readonly property color dropDownLightFinish: "#afafaf" - readonly property color dropDownDarkStart: "#7d7d7d" - readonly property color dropDownDarkFinish: "#6b6a6b" - readonly property color textFieldLightBackground: "#d4d4d4" - readonly property color tabBackgroundDark: "#252525" - readonly property color tabBackgroundLight: "#d4d4d4" - } - - readonly property QtObject colorSchemes: QtObject { - readonly property int light: 0 - readonly property int dark: 1 - readonly property int faintGray: 2 - } - - readonly property QtObject dimensions: QtObject { - readonly property bool largeScreen: Screen.width >= 1920 && Screen.height >= 1080 - readonly property real borderRadius: largeScreen ? 7.5 : 5.0 - readonly property real borderWidth: largeScreen ? 2 : 1 - readonly property vector2d contentMargin: Qt.vector2d(21, 21) - readonly property vector2d contentSpacing: Qt.vector2d(11, 14) - readonly property real labelPadding: 40 - readonly property real textPadding: 8 - readonly property real sliderHandleSize: 18 - readonly property real sliderGrooveHeight: 8 - readonly property real frameIconSize: 22 - readonly property real spinnerSize: 50 - readonly property real tablePadding: 12 - readonly property real tableRowHeight: largeScreen ? 26 : 23 - readonly property real tableHeaderHeight: 29 - readonly property vector2d modalDialogMargin: Qt.vector2d(50, 30) - readonly property real modalDialogTitleHeight: 40 - readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor - readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight - readonly property vector2d menuPadding: Qt.vector2d(14, 102) - readonly property real scrollbarBackgroundWidth: 20 - readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2 - readonly property real tabletMenuHeader: 90 - readonly property real buttonWidth: 120 - } - - readonly property QtObject fontSizes: QtObject { - // In pixels - readonly property real overlayTitle: dimensions.largeScreen ? 18 : 14 - readonly property real tabName: dimensions.largeScreen ? 12 : 10 - readonly property real sectionName: dimensions.largeScreen ? 12 : 10 - readonly property real inputLabel: dimensions.largeScreen ? 14 : 10 - readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12 - readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9 - readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24 - readonly property real tableHeading: dimensions.largeScreen ? 12 : 10 - readonly property real tableHeadingIcon: dimensions.largeScreen ? 60 : 33 - readonly property real tableText: dimensions.largeScreen ? 15 : 12 - readonly property real buttonLabel: dimensions.largeScreen ? 14 : 9 - readonly property real iconButton: dimensions.largeScreen ? 13 : 9 - readonly property real listItem: dimensions.largeScreen ? 15 : 11 - readonly property real tabularData: dimensions.largeScreen ? 15 : 11 - readonly property real logs: dimensions.largeScreen ? 16 : 12 - readonly property real code: dimensions.largeScreen ? 16 : 12 - readonly property real rootMenu: dimensions.largeScreen ? 15 : 11 - readonly property real rootMenuDisclosure: dimensions.largeScreen ? 20 : 16 - readonly property real menuItem: dimensions.largeScreen ? 15 : 11 - readonly property real shortcutText: dimensions.largeScreen ? 13 : 9 - readonly property real carat: dimensions.largeScreen ? 38 : 30 - readonly property real disclosureButton: dimensions.largeScreen ? 30 : 22 - } - - readonly property QtObject icons: QtObject { - // Values per OffscreenUi::Icon - readonly property int none: 0 - readonly property int question: 1 - readonly property int information: 2 - readonly property int warning: 3 - readonly property int critical: 4 - readonly property int placemark: 5 - } - - readonly property QtObject buttons: QtObject { - readonly property int white: 0 - readonly property int blue: 1 - readonly property int red: 2 - readonly property int black: 3 - readonly property int none: 4 - readonly property int noneBorderless: 5 - readonly property int noneBorderlessWhite: 6 - readonly property int noneBorderlessGray: 7 - readonly property var textColor: [ colors.darkGray, colors.white, colors.white, colors.white, colors.white, colors.blueAccent, colors.white, colors.darkGray ] - readonly property var colorStart: [ colors.white, colors.primaryHighlight, "#d42043", "#343434", Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] - readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] - readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] - readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colors.lightGrayText ] - readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] - readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight] - readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow] - readonly property var disabledTextColor: [ colors.lightGrayText, colors.baseGrayShadow] - readonly property int radius: 5 - } - - readonly property QtObject effects: QtObject { - readonly property int fadeInDuration: 300 - } - - readonly property QtObject glyphs: QtObject { - readonly property string noIcon: "" - readonly property string hmd: "b" - readonly property string screen: "c" - readonly property string keyboard: "d" - readonly property string handControllers: "e" - readonly property string headphonesMic: "f" - readonly property string gamepad: "g" - readonly property string headphones: "h" - readonly property string mic: "i" - readonly property string upload: "j" - readonly property string script: "k" - readonly property string text: "l" - readonly property string cube: "m" - readonly property string sphere: "n" - readonly property string zone: "o" - readonly property string light: "p" - readonly property string web: "q" - readonly property string web2: "r" - readonly property string edit: "s" - readonly property string market: "t" - readonly property string directory: "u" - readonly property string menu: "v" - readonly property string close: "w" - readonly property string closeInverted: "x" - readonly property string pin: "y" - readonly property string pinInverted: "z" - readonly property string resizeHandle: "A" - readonly property string disclosureExpand: "B" - readonly property string reloadSmall: "a" - readonly property string closeSmall: "C" - readonly property string forward: "D" - readonly property string backward: "E" - readonly property string reload: "F" - readonly property string unmuted: "G" - readonly property string muted: "H" - readonly property string minimize: "I" - readonly property string maximize: "J" - readonly property string maximizeInverted: "K" - readonly property string disclosureButtonExpand: "L" - readonly property string disclosureButtonCollapse: "M" - readonly property string scriptStop: "N" - readonly property string scriptReload: "O" - readonly property string scriptRun: "P" - readonly property string scriptNew: "Q" - readonly property string hifiForum: "2" - readonly property string hifiLogoSmall: "S" - readonly property string avatar1: "T" - readonly property string placemark: "U" - readonly property string box: "V" - readonly property string community: "0" - readonly property string grabHandle: "X" - readonly property string search: "Y" - readonly property string disclosureCollapse: "Z" - readonly property string scriptUpload: "R" - readonly property string code: "W" - readonly property string avatar: "<" - readonly property string arrowsH: ":" - readonly property string arrowsV: ";" - readonly property string arrows: "`" - readonly property string compress: "!" - readonly property string expand: "\"" - readonly property string placemark1: "#" - readonly property string circle: "$" - readonly property string handPointer: "9" - readonly property string plusSquareO: "%" - readonly property string sliders: "&" - readonly property string square: "'" - readonly property string alignCenter: "8" - readonly property string alignJustify: ")" - readonly property string alignLeft: "*" - readonly property string alignRight: "^" - readonly property string bars: "7" - readonly property string circleSlash: "," - readonly property string sync: "()" - readonly property string key: "-" - readonly property string link: "." - readonly property string location: "/" - readonly property string caratR: "3" - readonly property string caratL: "4" - readonly property string caratDn: "5" - readonly property string caratUp: "6" - readonly property string folderLg: ">" - readonly property string folderSm: "?" - readonly property string levelUp: "1" - readonly property string info: "[" - readonly property string question: "]" - readonly property string alert: "+" - readonly property string home: "_" - readonly property string error: "=" - readonly property string settings: "@" - readonly property string trash: "{" - readonly property string objectGroup: "\ue000" - readonly property string cm: "}" - readonly property string msvg79: "~" - readonly property string deg: "\\" - readonly property string px: "|" - readonly property string editPencil: "\ue00d" - readonly property string vol_0: "\ue00e" - readonly property string vol_1: "\ue00f" - readonly property string vol_2: "\ue010" - readonly property string vol_3: "\ue011" - readonly property string vol_4: "\ue012" - readonly property string vol_x_0: "\ue013" - readonly property string vol_x_1: "\ue014" - readonly property string vol_x_2: "\ue015" - readonly property string vol_x_3: "\ue016" - readonly property string vol_x_4: "\ue017" - readonly property string source: "\ue01c" - readonly property string playback_play: "\ue01d" - readonly property string stop_square: "\ue01e" - readonly property string avatarTPose: "\ue01f" - readonly property string lock: "\ue006" - readonly property string checkmark: "\ue020" - readonly property string leftRightArrows: "\ue021" - readonly property string hfc: "\ue022" - readonly property string home2: "\ue023" - readonly property string walletKey: "\ue024" - readonly property string lightning: "\ue025" - readonly property string securityImage: "\ue026" - readonly property string wallet: "\ue027" - readonly property string paperPlane: "\ue028" - readonly property string passphrase: "\ue029" - readonly property string globe: "\ue02c" - readonly property string wand: "\ue02d" - readonly property string hat: "\ue02e" - readonly property string install: "\ue02f" - readonly property string certificate: "\ue030" - readonly property string gift: "\ue031" - readonly property string update: "\ue032" - readonly property string uninstall: "\ue033" - readonly property string verticalEllipsis: "\ue034" - } +HifiConstants { } diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml index e5a18e2ae7..e60ed9eb10 100644 --- a/interface/resources/qml/styles-uit/IconButton.qml +++ b/interface/resources/qml/styles-uit/IconButton.qml @@ -1,18 +1,4 @@ -// -// IconButton.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayRegular { - font.pixelSize: hifi.fontSizes.iconButton - font.capitalization: Font.AllUppercase - font.letterSpacing: 1.5 +IconButton { } diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml index fa7684e8e7..d09f26649d 100644 --- a/interface/resources/qml/styles-uit/InfoItem.qml +++ b/interface/resources/qml/styles-uit/InfoItem.qml @@ -1,17 +1,4 @@ -// -// InfoItem.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewaySemiBold { - lineHeight: 2 - font.pixelSize: hifi.fontSizes.menuItem +InfoItem { } diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml index 3853dd5b19..89d74afc8f 100644 --- a/interface/resources/qml/styles-uit/InputLabel.qml +++ b/interface/resources/qml/styles-uit/InputLabel.qml @@ -1,16 +1,4 @@ -// -// InputLabel.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewaySemiBold { - font.pixelSize: hifi.fontSizes.inputLabel +InputLabel { } diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml index a69c4b48c2..8d72762644 100644 --- a/interface/resources/qml/styles-uit/ListItem.qml +++ b/interface/resources/qml/styles-uit/ListItem.qml @@ -1,16 +1,4 @@ -// -// ListItem.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayRegular { - font.pixelSize: hifi.fontSizes.listItem +ListItem { } diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml index 45d4436fbf..d9453afade 100644 --- a/interface/resources/qml/styles-uit/Logs.qml +++ b/interface/resources/qml/styles-uit/Logs.qml @@ -1,16 +1,4 @@ -// -// Logs.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -AnonymousProRegular { - font.pixelSize: hifi.fontSizes.logs +Logs { } diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml index 0fb423baab..51fc11a77d 100644 --- a/interface/resources/qml/styles-uit/OverlayTitle.qml +++ b/interface/resources/qml/styles-uit/OverlayTitle.qml @@ -1,16 +1,4 @@ -// -// OverlayTitle.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayRegular { - font.pixelSize: hifi.fontSizes.overlayTitle +OverlayTitle { } diff --git a/interface/resources/qml/styles-uit/RalewayBold.qml b/interface/resources/qml/styles-uit/RalewayBold.qml index 7edde91271..1373859b32 100644 --- a/interface/resources/qml/styles-uit/RalewayBold.qml +++ b/interface/resources/qml/styles-uit/RalewayBold.qml @@ -1,21 +1,4 @@ -// -// RalewayBold.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Raleway" - font.bold: true +RalewayBold { } diff --git a/interface/resources/qml/styles-uit/RalewayLight.qml b/interface/resources/qml/styles-uit/RalewayLight.qml index 666ebc2ea9..9573eb6649 100644 --- a/interface/resources/qml/styles-uit/RalewayLight.qml +++ b/interface/resources/qml/styles-uit/RalewayLight.qml @@ -1,20 +1,4 @@ -// -// RalewayLight.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Raleway Light" +RalewayLight { } diff --git a/interface/resources/qml/styles-uit/RalewayRegular.qml b/interface/resources/qml/styles-uit/RalewayRegular.qml index e263922095..d5a66ff696 100644 --- a/interface/resources/qml/styles-uit/RalewayRegular.qml +++ b/interface/resources/qml/styles-uit/RalewayRegular.qml @@ -1,20 +1,4 @@ -// -// RalewayRegular.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.7 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Raleway" +RalewayRegular { } diff --git a/interface/resources/qml/styles-uit/RalewaySemiBold.qml b/interface/resources/qml/styles-uit/RalewaySemiBold.qml index 19d8b6b8c9..874a48555b 100644 --- a/interface/resources/qml/styles-uit/RalewaySemiBold.qml +++ b/interface/resources/qml/styles-uit/RalewaySemiBold.qml @@ -1,21 +1,4 @@ -// -// RalewaySemiBold.qml -// -// Created by David Rowe on 12 Feb 2016 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.7 - -Text { - id: root - property real size: 32 - font.pixelSize: size - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - font.family: "Raleway" - font.weight: Font.DemiBold +RalewaySemiBold { } diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml index 20f8e1e116..819ed87d8b 100644 --- a/interface/resources/qml/styles-uit/SectionName.qml +++ b/interface/resources/qml/styles-uit/SectionName.qml @@ -1,17 +1,4 @@ -// -// SectionName.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayRegular { - font.pixelSize: hifi.fontSizes.sectionName - font.capitalization: Font.AllUppercase +SectionName { } diff --git a/interface/resources/qml/styles-uit/Separator.qml b/interface/resources/qml/styles-uit/Separator.qml index 4134b928a7..9708ec4c26 100644 --- a/interface/resources/qml/styles-uit/Separator.qml +++ b/interface/resources/qml/styles-uit/Separator.qml @@ -1,41 +1,4 @@ -// -// Separator.qml -// -// Created by Zach Fox on 2017-06-06 -// Copyright 2017 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "../styles-uit" - -Item { - // Size - height: 2; - width: parent.width; - - Rectangle { - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.bottom: parent.bottom; - anchors.bottomMargin: height; - // Style - color: hifi.colors.baseGrayShadow; - } - - Rectangle { - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.bottom: parent.bottom; - // Style - color: hifi.colors.baseGrayHighlight; - } +Separator { } diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml index 8504ffa2b8..16e1b2f1c1 100644 --- a/interface/resources/qml/styles-uit/ShortcutText.qml +++ b/interface/resources/qml/styles-uit/ShortcutText.qml @@ -1,16 +1,4 @@ -// -// ShortcutText.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayLight { - font.pixelSize: hifi.fontSizes.shortcutText +ShortcutText { } diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml index 0f620fe8c2..b66192e582 100644 --- a/interface/resources/qml/styles-uit/TabName.qml +++ b/interface/resources/qml/styles-uit/TabName.qml @@ -1,17 +1,4 @@ -// -// TabName.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -RalewayRegular { - font.pixelSize: hifi.fontSizes.tabName - font.capitalization: Font.AllUppercase +TabName { } diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml index f2a57e57fc..1498dde821 100644 --- a/interface/resources/qml/styles-uit/TextFieldInput.qml +++ b/interface/resources/qml/styles-uit/TextFieldInput.qml @@ -1,16 +1,4 @@ -// -// TextFieldInput.qml -// -// Created by Clement on 7/18/16 -// Copyright 2016 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 -// +import stylesUit 1.0 -import QtQuick 2.5 -import "." - -FiraSansSemiBold { - font.pixelSize: hifi.fontSizes.textFieldInput +TextFieldInput { } diff --git a/interface/resources/qml/styles-uit/readme.txt b/interface/resources/qml/styles-uit/readme.txt new file mode 100644 index 0000000000..105eda3c81 --- /dev/null +++ b/interface/resources/qml/styles-uit/readme.txt @@ -0,0 +1 @@ +this folder exists purely for compatibility reasons and might be deleted in future! please consider using 'import stylesUit 1.0' instead of including this folder \ No newline at end of file diff --git a/interface/resources/qml/styles-uit/+android/HifiConstants.qml b/interface/resources/qml/stylesUit/+android/HifiConstants.qml similarity index 100% rename from interface/resources/qml/styles-uit/+android/HifiConstants.qml rename to interface/resources/qml/stylesUit/+android/HifiConstants.qml diff --git a/interface/resources/qml/stylesUit/AnonymousProRegular.qml b/interface/resources/qml/stylesUit/AnonymousProRegular.qml new file mode 100644 index 0000000000..431ecd0f38 --- /dev/null +++ b/interface/resources/qml/stylesUit/AnonymousProRegular.qml @@ -0,0 +1,20 @@ +// +// AnonymousProRegular.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Anonymous Pro" +} diff --git a/interface/resources/qml/stylesUit/ButtonLabel.qml b/interface/resources/qml/stylesUit/ButtonLabel.qml new file mode 100644 index 0000000000..d227cb4869 --- /dev/null +++ b/interface/resources/qml/stylesUit/ButtonLabel.qml @@ -0,0 +1,16 @@ +// +// ButtonLabel.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayBold { + font.pixelSize: hifi.fontSizes.buttonLabel +} diff --git a/interface/resources/qml/stylesUit/FiraSansRegular.qml b/interface/resources/qml/stylesUit/FiraSansRegular.qml new file mode 100644 index 0000000000..05f6ecf74b --- /dev/null +++ b/interface/resources/qml/stylesUit/FiraSansRegular.qml @@ -0,0 +1,20 @@ +// +// FiraSansRegular.qml +// +// Created by David Rowe on 12 May 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Fira Sans" +} diff --git a/interface/resources/qml/stylesUit/FiraSansSemiBold.qml b/interface/resources/qml/stylesUit/FiraSansSemiBold.qml new file mode 100644 index 0000000000..32554c2f25 --- /dev/null +++ b/interface/resources/qml/stylesUit/FiraSansSemiBold.qml @@ -0,0 +1,20 @@ +// +// FiraSansSemiBold.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Fira Sans SemiBold" +} diff --git a/interface/resources/qml/stylesUit/HiFiGlyphs.qml b/interface/resources/qml/stylesUit/HiFiGlyphs.qml new file mode 100644 index 0000000000..07f0212f0c --- /dev/null +++ b/interface/resources/qml/stylesUit/HiFiGlyphs.qml @@ -0,0 +1,22 @@ +// +// HiFiGlyphs.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Text { + id: root + property int size: 32 + font.pixelSize: size + width: size + height: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "hifi-glyphs" +} diff --git a/interface/resources/qml/stylesUit/HifiConstants.qml b/interface/resources/qml/stylesUit/HifiConstants.qml new file mode 100644 index 0000000000..595c393de9 --- /dev/null +++ b/interface/resources/qml/stylesUit/HifiConstants.qml @@ -0,0 +1,353 @@ +// +// HiFiConstants.qml +// +// Created by Bradley Austin Davis on 28 Apr 2015 +// Copyright 2015 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 +// + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +QtObject { + + function glyphForIcon(icon) { + // Translates icon enum to glyph char. + var glyph; + switch (icon) { + case icons.information: + glyph = glyphs.info; + break; + case icons.question: + glyph = glyphs.question; + break; + case icons.warning: + glyph = glyphs.alert; + break; + case icons.critical: + glyph = glyphs.error; + break; + case icons.placemark: + glyph = glyphs.placemark; + break; + default: + glyph = glyphs.noIcon; + } + return glyph; + } + + readonly property QtObject colors: QtObject { + // Base colors + readonly property color baseGray: "#393939" + readonly property color darkGray: "#121212" + readonly property color baseGrayShadow: "#252525" + readonly property color baseGrayHighlight: "#575757" + readonly property color lightGray: "#6a6a6a" + readonly property color lightGrayText: "#afafaf" + readonly property color faintGray: "#e3e3e3" + readonly property color primaryHighlight: "#00b4ef" + readonly property color blueHighlight: "#00b4ef" + readonly property color blueAccent: "#0093C5" + readonly property color redHighlight: "#EA4C5F" + readonly property color redAccent: "#C62147" + readonly property color greenHighlight: "#1ac567" + readonly property color greenShadow: "#359D85" + readonly property color orangeHighlight: "#FFC49C" + readonly property color orangeAccent: "#FF6309" + readonly property color indigoHighlight: "#C0D2FF" + readonly property color indigoAccent: "#9495FF" + readonly property color magentaHighlight: "#EF93D1" + readonly property color magentaAccent: "#A2277C" + readonly property color checkboxCheckedRed: "#FF0000" + readonly property color checkboxCheckedBorderRed: "#D00000" + readonly property color lightBlueHighlight: "#d6f6ff" + + // Semitransparent + readonly property color darkGray30: "#4d121212" + readonly property color darkGray0: "#00121212" + readonly property color baseGrayShadow60: "#99252525" + readonly property color baseGrayShadow50: "#80252525" + readonly property color baseGrayShadow25: "#40252525" + readonly property color baseGrayHighlight40: "#66575757" + readonly property color baseGrayHighlight15: "#26575757" + readonly property color lightGray50: "#806a6a6a" + readonly property color lightGrayText80: "#ccafafaf" + readonly property color faintGray80: "#cce3e3e3" + readonly property color faintGray50: "#80e3e3e3" + + // Other colors + readonly property color white: "#ffffff" + readonly property color gray: "#808080" + readonly property color black: "#000000" + readonly property color locked: "#252525" + // Semitransparent + readonly property color white50: "#80ffffff" + readonly property color white30: "#4dffffff" + readonly property color white25: "#40ffffff" + readonly property color transparent: "#00ffffff" + + // Control specific colors + readonly property color tableRowLightOdd: "#fafafa" + readonly property color tableRowLightEven: "#eeeeee" // Equivavlent to "#1a575757" over #e3e3e3 background + readonly property color tableRowDarkOdd: "#2e2e2e" // Equivalent to "#80393939" over #404040 background + readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background + readonly property color tableBackgroundLight: tableRowLightEven + readonly property color tableBackgroundDark: tableRowDarkEven + readonly property color tableScrollHandleLight: "#DDDDDD" + readonly property color tableScrollHandleDark: "#707070" + readonly property color tableScrollBackgroundLight: tableRowLightOdd + readonly property color tableScrollBackgroundDark: "#323232" + readonly property color checkboxLightStart: "#ffffff" + readonly property color checkboxLightFinish: "#afafaf" + readonly property color checkboxDarkStart: "#7d7d7d" + readonly property color checkboxDarkFinish: "#6b6a6b" + readonly property color checkboxChecked: primaryHighlight + readonly property color checkboxCheckedBorder: "#36cdff" + readonly property color sliderGutterLight: "#d4d4d4" + readonly property color sliderGutterDark: "#252525" + readonly property color sliderBorderLight: "#afafaf" + readonly property color sliderBorderDark: "#7d7d7d" + readonly property color sliderLightStart: "#ffffff" + readonly property color sliderLightFinish: "#afafaf" + readonly property color sliderDarkStart: "#7d7d7d" + readonly property color sliderDarkFinish: "#6b6a6b" + readonly property color dropDownPressedLight: "#d4d4d4" + readonly property color dropDownPressedDark: "#afafaf" + readonly property color dropDownLightStart: "#ffffff" + readonly property color dropDownLightFinish: "#afafaf" + readonly property color dropDownDarkStart: "#7d7d7d" + readonly property color dropDownDarkFinish: "#6b6a6b" + readonly property color textFieldLightBackground: "#d4d4d4" + readonly property color tabBackgroundDark: "#252525" + readonly property color tabBackgroundLight: "#d4d4d4" + } + + readonly property QtObject colorSchemes: QtObject { + readonly property int light: 0 + readonly property int dark: 1 + readonly property int faintGray: 2 + } + + readonly property QtObject dimensions: QtObject { + readonly property bool largeScreen: Screen.width >= 1920 && Screen.height >= 1080 + readonly property real borderRadius: largeScreen ? 7.5 : 5.0 + readonly property real borderWidth: largeScreen ? 2 : 1 + readonly property vector2d contentMargin: Qt.vector2d(21, 21) + readonly property vector2d contentSpacing: Qt.vector2d(11, 14) + readonly property real labelPadding: 40 + readonly property real textPadding: 8 + readonly property real sliderHandleSize: 18 + readonly property real sliderGrooveHeight: 8 + readonly property real frameIconSize: 22 + readonly property real spinnerSize: 50 + readonly property real tablePadding: 12 + readonly property real tableRowHeight: largeScreen ? 26 : 23 + readonly property real tableHeaderHeight: 29 + readonly property vector2d modalDialogMargin: Qt.vector2d(50, 30) + readonly property real modalDialogTitleHeight: 40 + readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor + readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight + readonly property vector2d menuPadding: Qt.vector2d(14, 102) + readonly property real scrollbarBackgroundWidth: 20 + readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2 + readonly property real tabletMenuHeader: 90 + readonly property real buttonWidth: 120 + } + + readonly property QtObject fontSizes: QtObject { + // In pixels + readonly property real overlayTitle: dimensions.largeScreen ? 18 : 14 + readonly property real tabName: dimensions.largeScreen ? 12 : 10 + readonly property real sectionName: dimensions.largeScreen ? 12 : 10 + readonly property real inputLabel: dimensions.largeScreen ? 14 : 10 + readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12 + readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9 + readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24 + readonly property real tableHeading: dimensions.largeScreen ? 12 : 10 + readonly property real tableHeadingIcon: dimensions.largeScreen ? 60 : 33 + readonly property real tableText: dimensions.largeScreen ? 15 : 12 + readonly property real buttonLabel: dimensions.largeScreen ? 14 : 9 + readonly property real iconButton: dimensions.largeScreen ? 13 : 9 + readonly property real listItem: dimensions.largeScreen ? 15 : 11 + readonly property real tabularData: dimensions.largeScreen ? 15 : 11 + readonly property real logs: dimensions.largeScreen ? 16 : 12 + readonly property real code: dimensions.largeScreen ? 16 : 12 + readonly property real rootMenu: dimensions.largeScreen ? 15 : 11 + readonly property real rootMenuDisclosure: dimensions.largeScreen ? 20 : 16 + readonly property real menuItem: dimensions.largeScreen ? 15 : 11 + readonly property real shortcutText: dimensions.largeScreen ? 13 : 9 + readonly property real carat: dimensions.largeScreen ? 38 : 30 + readonly property real disclosureButton: dimensions.largeScreen ? 30 : 22 + } + + readonly property QtObject icons: QtObject { + // Values per OffscreenUi::Icon + readonly property int none: 0 + readonly property int question: 1 + readonly property int information: 2 + readonly property int warning: 3 + readonly property int critical: 4 + readonly property int placemark: 5 + } + + readonly property QtObject buttons: QtObject { + readonly property int white: 0 + readonly property int blue: 1 + readonly property int red: 2 + readonly property int black: 3 + readonly property int none: 4 + readonly property int noneBorderless: 5 + readonly property int noneBorderlessWhite: 6 + readonly property int noneBorderlessGray: 7 + readonly property var textColor: [ colors.darkGray, colors.white, colors.white, colors.white, colors.white, colors.blueAccent, colors.white, colors.darkGray ] + readonly property var colorStart: [ colors.white, colors.primaryHighlight, "#d42043", "#343434", Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] + readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ] + readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] + readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colors.lightGrayText ] + readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ] + readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight] + readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow] + readonly property var disabledTextColor: [ colors.lightGrayText, colors.baseGrayShadow] + readonly property int radius: 5 + } + + readonly property QtObject effects: QtObject { + readonly property int fadeInDuration: 300 + } + + readonly property QtObject glyphs: QtObject { + readonly property string noIcon: "" + readonly property string hmd: "b" + readonly property string screen: "c" + readonly property string keyboard: "d" + readonly property string handControllers: "e" + readonly property string headphonesMic: "f" + readonly property string gamepad: "g" + readonly property string headphones: "h" + readonly property string mic: "i" + readonly property string upload: "j" + readonly property string script: "k" + readonly property string text: "l" + readonly property string cube: "m" + readonly property string sphere: "n" + readonly property string zone: "o" + readonly property string light: "p" + readonly property string web: "q" + readonly property string web2: "r" + readonly property string edit: "s" + readonly property string market: "t" + readonly property string directory: "u" + readonly property string menu: "v" + readonly property string close: "w" + readonly property string closeInverted: "x" + readonly property string pin: "y" + readonly property string pinInverted: "z" + readonly property string resizeHandle: "A" + readonly property string disclosureExpand: "B" + readonly property string reloadSmall: "a" + readonly property string closeSmall: "C" + readonly property string forward: "D" + readonly property string backward: "E" + readonly property string reload: "F" + readonly property string unmuted: "G" + readonly property string muted: "H" + readonly property string minimize: "I" + readonly property string maximize: "J" + readonly property string maximizeInverted: "K" + readonly property string disclosureButtonExpand: "L" + readonly property string disclosureButtonCollapse: "M" + readonly property string scriptStop: "N" + readonly property string scriptReload: "O" + readonly property string scriptRun: "P" + readonly property string scriptNew: "Q" + readonly property string hifiForum: "2" + readonly property string hifiLogoSmall: "S" + readonly property string avatar1: "T" + readonly property string placemark: "U" + readonly property string box: "V" + readonly property string community: "0" + readonly property string grabHandle: "X" + readonly property string search: "Y" + readonly property string disclosureCollapse: "Z" + readonly property string scriptUpload: "R" + readonly property string code: "W" + readonly property string avatar: "<" + readonly property string arrowsH: ":" + readonly property string arrowsV: ";" + readonly property string arrows: "`" + readonly property string compress: "!" + readonly property string expand: "\"" + readonly property string placemark1: "#" + readonly property string circle: "$" + readonly property string handPointer: "9" + readonly property string plusSquareO: "%" + readonly property string sliders: "&" + readonly property string square: "'" + readonly property string alignCenter: "8" + readonly property string alignJustify: ")" + readonly property string alignLeft: "*" + readonly property string alignRight: "^" + readonly property string bars: "7" + readonly property string circleSlash: "," + readonly property string sync: "()" + readonly property string key: "-" + readonly property string link: "." + readonly property string location: "/" + readonly property string caratR: "3" + readonly property string caratL: "4" + readonly property string caratDn: "5" + readonly property string caratUp: "6" + readonly property string folderLg: ">" + readonly property string folderSm: "?" + readonly property string levelUp: "1" + readonly property string info: "[" + readonly property string question: "]" + readonly property string alert: "+" + readonly property string home: "_" + readonly property string error: "=" + readonly property string settings: "@" + readonly property string trash: "{" + readonly property string objectGroup: "\ue000" + readonly property string cm: "}" + readonly property string msvg79: "~" + readonly property string deg: "\\" + readonly property string px: "|" + readonly property string editPencil: "\ue00d" + readonly property string vol_0: "\ue00e" + readonly property string vol_1: "\ue00f" + readonly property string vol_2: "\ue010" + readonly property string vol_3: "\ue011" + readonly property string vol_4: "\ue012" + readonly property string vol_x_0: "\ue013" + readonly property string vol_x_1: "\ue014" + readonly property string vol_x_2: "\ue015" + readonly property string vol_x_3: "\ue016" + readonly property string vol_x_4: "\ue017" + readonly property string source: "\ue01c" + readonly property string playback_play: "\ue01d" + readonly property string stop_square: "\ue01e" + readonly property string avatarTPose: "\ue01f" + readonly property string lock: "\ue006" + readonly property string checkmark: "\ue020" + readonly property string leftRightArrows: "\ue021" + readonly property string hfc: "\ue022" + readonly property string home2: "\ue023" + readonly property string walletKey: "\ue024" + readonly property string lightning: "\ue025" + readonly property string securityImage: "\ue026" + readonly property string wallet: "\ue027" + readonly property string paperPlane: "\ue028" + readonly property string passphrase: "\ue029" + readonly property string globe: "\ue02c" + readonly property string wand: "\ue02d" + readonly property string hat: "\ue02e" + readonly property string install: "\ue02f" + readonly property string certificate: "\ue030" + readonly property string gift: "\ue031" + readonly property string update: "\ue032" + readonly property string uninstall: "\ue033" + readonly property string verticalEllipsis: "\ue034" + } +} diff --git a/interface/resources/qml/stylesUit/IconButton.qml b/interface/resources/qml/stylesUit/IconButton.qml new file mode 100644 index 0000000000..e5a18e2ae7 --- /dev/null +++ b/interface/resources/qml/stylesUit/IconButton.qml @@ -0,0 +1,18 @@ +// +// IconButton.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.iconButton + font.capitalization: Font.AllUppercase + font.letterSpacing: 1.5 +} diff --git a/interface/resources/qml/stylesUit/InfoItem.qml b/interface/resources/qml/stylesUit/InfoItem.qml new file mode 100644 index 0000000000..fa7684e8e7 --- /dev/null +++ b/interface/resources/qml/stylesUit/InfoItem.qml @@ -0,0 +1,17 @@ +// +// InfoItem.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewaySemiBold { + lineHeight: 2 + font.pixelSize: hifi.fontSizes.menuItem +} diff --git a/interface/resources/qml/stylesUit/InputLabel.qml b/interface/resources/qml/stylesUit/InputLabel.qml new file mode 100644 index 0000000000..3853dd5b19 --- /dev/null +++ b/interface/resources/qml/stylesUit/InputLabel.qml @@ -0,0 +1,16 @@ +// +// InputLabel.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewaySemiBold { + font.pixelSize: hifi.fontSizes.inputLabel +} diff --git a/interface/resources/qml/stylesUit/ListItem.qml b/interface/resources/qml/stylesUit/ListItem.qml new file mode 100644 index 0000000000..a69c4b48c2 --- /dev/null +++ b/interface/resources/qml/stylesUit/ListItem.qml @@ -0,0 +1,16 @@ +// +// ListItem.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.listItem +} diff --git a/interface/resources/qml/stylesUit/Logs.qml b/interface/resources/qml/stylesUit/Logs.qml new file mode 100644 index 0000000000..45d4436fbf --- /dev/null +++ b/interface/resources/qml/stylesUit/Logs.qml @@ -0,0 +1,16 @@ +// +// Logs.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +AnonymousProRegular { + font.pixelSize: hifi.fontSizes.logs +} diff --git a/interface/resources/qml/stylesUit/OverlayTitle.qml b/interface/resources/qml/stylesUit/OverlayTitle.qml new file mode 100644 index 0000000000..0fb423baab --- /dev/null +++ b/interface/resources/qml/stylesUit/OverlayTitle.qml @@ -0,0 +1,16 @@ +// +// OverlayTitle.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.overlayTitle +} diff --git a/interface/resources/qml/stylesUit/RalewayBold.qml b/interface/resources/qml/stylesUit/RalewayBold.qml new file mode 100644 index 0000000000..7edde91271 --- /dev/null +++ b/interface/resources/qml/stylesUit/RalewayBold.qml @@ -0,0 +1,21 @@ +// +// RalewayBold.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Raleway" + font.bold: true +} diff --git a/interface/resources/qml/stylesUit/RalewayLight.qml b/interface/resources/qml/stylesUit/RalewayLight.qml new file mode 100644 index 0000000000..666ebc2ea9 --- /dev/null +++ b/interface/resources/qml/stylesUit/RalewayLight.qml @@ -0,0 +1,20 @@ +// +// RalewayLight.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Raleway Light" +} diff --git a/interface/resources/qml/stylesUit/RalewayRegular.qml b/interface/resources/qml/stylesUit/RalewayRegular.qml new file mode 100644 index 0000000000..e263922095 --- /dev/null +++ b/interface/resources/qml/stylesUit/RalewayRegular.qml @@ -0,0 +1,20 @@ +// +// RalewayRegular.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Raleway" +} diff --git a/interface/resources/qml/stylesUit/RalewaySemiBold.qml b/interface/resources/qml/stylesUit/RalewaySemiBold.qml new file mode 100644 index 0000000000..19d8b6b8c9 --- /dev/null +++ b/interface/resources/qml/stylesUit/RalewaySemiBold.qml @@ -0,0 +1,21 @@ +// +// RalewaySemiBold.qml +// +// Created by David Rowe on 12 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.7 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Raleway" + font.weight: Font.DemiBold +} diff --git a/interface/resources/qml/stylesUit/SectionName.qml b/interface/resources/qml/stylesUit/SectionName.qml new file mode 100644 index 0000000000..20f8e1e116 --- /dev/null +++ b/interface/resources/qml/stylesUit/SectionName.qml @@ -0,0 +1,17 @@ +// +// SectionName.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase +} diff --git a/interface/resources/qml/stylesUit/Separator.qml b/interface/resources/qml/stylesUit/Separator.qml new file mode 100644 index 0000000000..d9f11e192c --- /dev/null +++ b/interface/resources/qml/stylesUit/Separator.qml @@ -0,0 +1,41 @@ +// +// Separator.qml +// +// Created by Zach Fox on 2017-06-06 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import "." + +Item { + // Size + height: 2; + width: parent.width; + + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: height; + // Style + color: hifi.colors.baseGrayShadow; + } + + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + // Style + color: hifi.colors.baseGrayHighlight; + } +} diff --git a/interface/resources/qml/stylesUit/ShortcutText.qml b/interface/resources/qml/stylesUit/ShortcutText.qml new file mode 100644 index 0000000000..8504ffa2b8 --- /dev/null +++ b/interface/resources/qml/stylesUit/ShortcutText.qml @@ -0,0 +1,16 @@ +// +// ShortcutText.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayLight { + font.pixelSize: hifi.fontSizes.shortcutText +} diff --git a/interface/resources/qml/stylesUit/TabName.qml b/interface/resources/qml/stylesUit/TabName.qml new file mode 100644 index 0000000000..0f620fe8c2 --- /dev/null +++ b/interface/resources/qml/stylesUit/TabName.qml @@ -0,0 +1,17 @@ +// +// TabName.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.tabName + font.capitalization: Font.AllUppercase +} diff --git a/interface/resources/qml/stylesUit/TextFieldInput.qml b/interface/resources/qml/stylesUit/TextFieldInput.qml new file mode 100644 index 0000000000..f2a57e57fc --- /dev/null +++ b/interface/resources/qml/stylesUit/TextFieldInput.qml @@ -0,0 +1,16 @@ +// +// TextFieldInput.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import "." + +FiraSansSemiBold { + font.pixelSize: hifi.fontSizes.textFieldInput +} diff --git a/interface/resources/qml/styles-uit/qmldir b/interface/resources/qml/stylesUit/qmldir similarity index 100% rename from interface/resources/qml/styles-uit/qmldir rename to interface/resources/qml/stylesUit/qmldir diff --git a/interface/resources/qml/windows/Decoration.qml b/interface/resources/qml/windows/Decoration.qml index f8fd9f4e6c..efaea6be8a 100644 --- a/interface/resources/qml/windows/Decoration.qml +++ b/interface/resources/qml/windows/Decoration.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Rectangle { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml index 60e744bec3..5a366e367b 100644 --- a/interface/resources/qml/windows/DefaultFrame.qml +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "." -import "../styles-uit" +import stylesUit 1.0 Frame { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/DefaultFrameDecoration.qml b/interface/resources/qml/windows/DefaultFrameDecoration.qml index 1ddd83976e..fb0dd55985 100644 --- a/interface/resources/qml/windows/DefaultFrameDecoration.qml +++ b/interface/resources/qml/windows/DefaultFrameDecoration.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Decoration { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/Fadable.qml b/interface/resources/qml/windows/Fadable.qml index 406c6be556..6d88fb067a 100644 --- a/interface/resources/qml/windows/Fadable.qml +++ b/interface/resources/qml/windows/Fadable.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 // Enable window visibility transitions FocusScope { diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml index 271d4f2e07..7b0fbf8d8c 100644 --- a/interface/resources/qml/windows/Frame.qml +++ b/interface/resources/qml/windows/Frame.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../styles-uit" +import stylesUit 1.0 import "../js/Utils.js" as Utils Item { diff --git a/interface/resources/qml/windows/ModalFrame.qml b/interface/resources/qml/windows/ModalFrame.qml index cb23ccd5ad..ae149224e3 100644 --- a/interface/resources/qml/windows/ModalFrame.qml +++ b/interface/resources/qml/windows/ModalFrame.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import "." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Frame { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index c156b80388..4cab96701e 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -14,8 +14,8 @@ import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" -import "../controls-uit" as HiFiControls +import stylesUit 1.0 +import controlsUit 1.0 as HiFiControls // FIXME how do I set the initial position of a window without // overriding places where the a individual client of the window diff --git a/interface/resources/qml/windows/TabletModalFrame.qml b/interface/resources/qml/windows/TabletModalFrame.qml index 550eec8357..1e9310eb5a 100644 --- a/interface/resources/qml/windows/TabletModalFrame.qml +++ b/interface/resources/qml/windows/TabletModalFrame.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import "." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Rectangle { diff --git a/interface/resources/qml/windows/ToolFrame.qml b/interface/resources/qml/windows/ToolFrame.qml index 20c86afb5e..bb2bada498 100644 --- a/interface/resources/qml/windows/ToolFrame.qml +++ b/interface/resources/qml/windows/ToolFrame.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Frame { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/ToolFrameDecoration.qml b/interface/resources/qml/windows/ToolFrameDecoration.qml index ba36a2a38c..4f149037b3 100644 --- a/interface/resources/qml/windows/ToolFrameDecoration.qml +++ b/interface/resources/qml/windows/ToolFrameDecoration.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Decoration { id: root diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index 835967c628..9f180af55d 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 // FIXME how do I set the initial position of a window without // overriding places where the a individual client of the window diff --git a/interface/resources/sounds/keyboard_key.mp3 b/interface/resources/sounds/keyboard_key.mp3 new file mode 100644 index 0000000000..e2cec81032 Binary files /dev/null and b/interface/resources/sounds/keyboard_key.mp3 differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aa2b382c58..3fef5bd8d8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -52,6 +52,8 @@ #include #include +#include +#include #include #include @@ -152,6 +154,8 @@ #include #include #include +#include +#include "recording/ClipCache.h" #include "AudioClient.h" #include "audio/AudioScope.h" @@ -182,6 +186,8 @@ #include "scripting/RatesScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" #include "scripting/WalletScriptingInterface.h" +#include "scripting/TTSScriptingInterface.h" +#include "scripting/KeyboardScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -200,6 +206,7 @@ #include "ui/UpdateDialog.h" #include "ui/overlays/Overlays.h" #include "ui/DomainConnectionModel.h" +#include "ui/Keyboard.h" #include "Util.h" #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" @@ -224,6 +231,7 @@ #include "commerce/Ledger.h" #include "commerce/Wallet.h" #include "commerce/QmlCommerce.h" +#include "ResourceRequestObserver.h" #include "webbrowser/WebBrowserSuggestionsEngine.h" #include @@ -328,9 +336,9 @@ static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains #endif #if !defined(Q_OS_ANDROID) -static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; +static const uint32_t MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; #else -static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 4; +static const uint32_t MAX_CONCURRENT_RESOURCE_DOWNLOADS = 4; #endif // For processing on QThreadPool, we target a number of threads after reserving some @@ -377,7 +385,7 @@ static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; -static const QString AUTO_LOGOUT_SETTING_NAME = "wallet/autoLogout"; +static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn"; const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, @@ -528,11 +536,11 @@ bool isDomainURL(QUrl url) { if (url.scheme() == URL_SCHEME_HIFI) { return true; } - if (url.scheme() != URL_SCHEME_FILE) { + if (url.scheme() != HIFI_URL_SCHEME_FILE) { // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can // be loaded over http(s) - // && url.scheme() != URL_SCHEME_HTTP && - // url.scheme() != URL_SCHEME_HTTPS + // && url.scheme() != HIFI_URL_SCHEME_HTTP && + // url.scheme() != HIFI_URL_SCHEME_HTTPS return false; } if (url.path().endsWith(".json", Qt::CaseInsensitive) || @@ -943,8 +951,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -967,9 +979,11 @@ OffscreenGLCanvas* _qmlShareContext { nullptr }; // and manually set THAT to be the shared context for the Chromium helper #if !defined(DISABLE_QML) OffscreenGLCanvas* _chromiumShareContext { nullptr }; -Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); #endif +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); + Setting::Handle sessionRunTime{ "sessionRunTime", 0 }; const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f; @@ -1024,8 +1038,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file // This is done so as not break previous command line scripts - if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || - testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { + if (testScriptPath.left(HIFI_URL_SCHEME_HTTP.length()) == HIFI_URL_SCHEME_HTTP || + testScriptPath.left(HIFI_URL_SCHEME_FTP.length()) == HIFI_URL_SCHEME_FTP) { setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath)); } else if (QFileInfo(testScriptPath).exists()) { @@ -1140,7 +1154,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store static const QString OCULUS_STORE_ARG = "--oculus-store"; - setProperty(hifi::properties::OCULUS_STORE, arguments().indexOf(OCULUS_STORE_ARG) != -1); + bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1; + setProperty(hifi::properties::OCULUS_STORE, isStore); + DependencyManager::get()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties? updateHeartbeat(); @@ -1327,7 +1343,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); bool success; - int concurrentDownloads = concurrentDownloadsStr.toInt(&success); + uint32_t concurrentDownloads = concurrentDownloadsStr.toUInt(&success); if (!success) { concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS; } @@ -1366,7 +1382,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _glWidget->setMouseTracking(true); // Make sure the window is set to the correct size by processing the pending events QCoreApplication::processEvents(); - _glWidget->createContext(); // Create the main thread context, the GPU backend initializeGL(); @@ -2054,7 +2069,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } properties["active_downloads"] = loadingRequests.size(); - properties["pending_downloads"] = ResourceCache::getPendingRequestCount(); + properties["pending_downloads"] = (int)ResourceCache::getPendingRequestCount(); properties["active_downloads_details"] = loadingRequestsStats; auto statTracker = DependencyManager::get(); @@ -2316,6 +2331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Preload Tablet sounds DependencyManager::get()->preloadSounds(); + DependencyManager::get()->createKeyboard(); _pendingIdleEvent = false; _pendingRenderEvent = false; @@ -2328,23 +2344,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); #else - static int CHECK_LOGIN_TIMER = 3000; - QTimer* checkLoginTimer = new QTimer(this); - checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); - checkLoginTimer->setSingleShot(true); - connect(checkLoginTimer, &QTimer::timeout, this, []() { - auto accountManager = DependencyManager::get(); - auto dialogsManager = DependencyManager::get(); - if (!accountManager->isLoggedIn()) { - Setting::Handle{"loginDialogPoppedUp", false}.set(true); - dialogsManager->showLoginDialog(); - QJsonObject loginData = {}; - loginData["action"] = "login dialog shown"; - UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - } - }); - Setting::Handle{"loginDialogPoppedUp", false}.set(false); - checkLoginTimer->start(); + // Do not show login dialog if requested not to on the command line + const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "--no-login-suggestion"; + int index = arguments().indexOf(HIFI_NO_LOGIN_COMMAND_LINE_KEY); + if (index == -1) { + // request not found + static int CHECK_LOGIN_TIMER = 3000; + QTimer* checkLoginTimer = new QTimer(this); + checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); + checkLoginTimer->setSingleShot(true); + connect(checkLoginTimer, &QTimer::timeout, this, []() { + auto accountManager = DependencyManager::get(); + auto dialogsManager = DependencyManager::get(); + if (!accountManager->isLoggedIn()) { + Setting::Handle{ "loginDialogPoppedUp", false }.set(true); + dialogsManager->showLoginDialog(); + QJsonObject loginData = {}; + loginData["action"] = "login dialog shown"; + UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); + } + }); + Setting::Handle{ "loginDialogPoppedUp", false }.set(false); + checkLoginTimer->start(); + } #endif } @@ -2425,11 +2447,17 @@ QString Application::getUserAgent() { } void Application::toggleTabletUI(bool shouldOpen) const { - auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); if (!(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); + + if (!HMD->getShouldShowTablet()) { + DependencyManager::get()->setRaised(false); + _window->activateWindow(); + auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); + tablet->unfocus(); + } } } @@ -2560,8 +2588,8 @@ void Application::cleanupBeforeQuit() { } DependencyManager::destroy(); - bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); - if (autoLogout) { + bool keepMeLoggedIn = Setting::Handle(KEEP_ME_LOGGED_IN_SETTING_NAME, false).get(); + if (!keepMeLoggedIn) { DependencyManager::get()->removeAccountFromFile(); } @@ -2622,6 +2650,8 @@ void Application::cleanupBeforeQuit() { // it accesses the PickManager to delete its associated Pick DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } @@ -2723,46 +2753,67 @@ void Application::initializeGL() { _isGLInitialized = true; } - if (!_glWidget->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make window context current"); - } + _glWidget->windowHandle()->setFormat(getDefaultOpenGLSurfaceFormat()); + // When loading QtWebEngineWidgets, it creates a global share context on startup. + // We have to account for this possibility by checking here for an existing + // global share context + auto globalShareContext = qt_gl_global_share_context(); + #if !defined(DISABLE_QML) // Build a shared canvas / context for the Chromium processes - { - // Disable signed distance field font rendering on ATI/AMD GPUs, due to - // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app - std::string vendor{ (const char*)glGetString(GL_VENDOR) }; - if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { - qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); - } - + if (!globalShareContext) { // Chromium rendering uses some GL functions that prevent nSight from capturing // frames, so we only create the shared context if nsight is NOT active. if (!nsightActive()) { _chromiumShareContext = new OffscreenGLCanvas(); _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_glWidget->qglContext()); + auto format =QSurfaceFormat::defaultFormat(); +#ifdef Q_OS_MAC + // On mac, the primary shared OpenGL context must be a 3.2 core context, + // or chromium flips out and spews error spam (but renders fine) + format.setMajorVersion(3); + format.setMinorVersion(2); +#endif + _chromiumShareContext->setFormat(format); + _chromiumShareContext->create(); if (!_chromiumShareContext->makeCurrent()) { qCWarning(interfaceapp, "Unable to make chromium shared context current"); } - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + globalShareContext = _chromiumShareContext->getContext(); + qt_gl_set_global_share_context(globalShareContext); _chromiumShareContext->doneCurrent(); - // Restore the GL widget context - if (!_glWidget->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make window context current"); - } - } else { - qCWarning(interfaceapp) << "nSight detected, disabling chrome rendering"; } } #endif + + _glWidget->createContext(globalShareContext); + + if (!_glWidget->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make window context current"); + } + +#if !defined(DISABLE_QML) + // Disable signed distance field font rendering on ATI/AMD GPUs, due to + // https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app + std::string vendor{ (const char*)glGetString(GL_VENDOR) }; + if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) { + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text")); + } +#endif + + if (!globalShareContext) { + globalShareContext = _glWidget->qglContext(); + qt_gl_set_global_share_context(globalShareContext); + } + // Build a shared canvas / context for the QML rendering +#if !defined(DISABLE_QML) { _qmlShareContext = new OffscreenGLCanvas(); _qmlShareContext->setObjectName("QmlShareContext"); - _qmlShareContext->create(_glWidget->qglContext()); + _qmlShareContext->create(globalShareContext); if (!_qmlShareContext->makeCurrent()) { qCWarning(interfaceapp, "Unable to make QML shared context current"); } @@ -2772,6 +2823,8 @@ void Application::initializeGL() { qCWarning(interfaceapp, "Unable to make window context current"); } } +#endif + _renderEventHandler = new RenderEventHandler(); @@ -2886,6 +2939,7 @@ void Application::initializeRenderEngine() { // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. DependencyManager::get()->initializeShapePipelines(); + DependencyManager::get()->registerKeyboardHighlighting(); }); } @@ -2898,7 +2952,7 @@ void Application::initializeUi() { LoginDialog::registerType(); Tooltip::registerType(); UpdateDialog::registerType(); - QmlContextCallback callback = [](QQmlContext* context) { + QmlContextCallback commerceCallback = [](QQmlContext* context) { context->setContextProperty("Commerce", new QmlCommerce()); }; OffscreenQmlSurface::addWhitelistContextHandler({ @@ -2917,14 +2971,20 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/wallet/PassphraseChange.qml" }, QUrl{ "hifi/commerce/wallet/PassphraseModal.qml" }, QUrl{ "hifi/commerce/wallet/PassphraseSelection.qml" }, - QUrl{ "hifi/commerce/wallet/Security.qml" }, - QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" }, - QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" }, - QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" }, QUrl{ "hifi/commerce/wallet/Wallet.qml" }, QUrl{ "hifi/commerce/wallet/WalletHome.qml" }, QUrl{ "hifi/commerce/wallet/WalletSetup.qml" }, - }, callback); + QUrl{ "hifi/dialogs/security/Security.qml" }, + QUrl{ "hifi/dialogs/security/SecurityImageChange.qml" }, + QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" }, + QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" }, + }, commerceCallback); + QmlContextCallback ttsCallback = [](QQmlContext* context) { + context->setContextProperty("TextToSpeech", DependencyManager::get().data()); + }; + OffscreenQmlSurface::addWhitelistContextHandler({ + QUrl{ "hifi/tts/TTS.qml" } + }, ttsCallback); qmlRegisterType("Hifi", 1, 0, "ResourceImageItem"); qmlRegisterType("Hifi", 1, 0, "Preference"); qmlRegisterType("HifiWeb", 1, 0, "WebBrowserSuggestionsEngine"); @@ -3076,6 +3136,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Vec3", new Vec3()); surfaceContext->setContextProperty("Uuid", new ScriptUUID()); surfaceContext->setContextProperty("Assets", DependencyManager::get().data()); + surfaceContext->setContextProperty("Keyboard", DependencyManager::get().data()); surfaceContext->setContextProperty("AvatarList", DependencyManager::get().data()); surfaceContext->setContextProperty("Users", DependencyManager::get().data()); @@ -3125,8 +3186,9 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("Selection", DependencyManager::get().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); - surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); + surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); + surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -3532,6 +3594,7 @@ void Application::setIsInterstitialMode(bool interstitialMode) { if (enableInterstitial) { if (_interstitialMode != interstitialMode) { _interstitialMode = interstitialMode; + emit interstitialModeChanged(_interstitialMode); DependencyManager::get()->setAudioPaused(_interstitialMode); DependencyManager::get()->setMyAvatarDataPacketsPaused(_interstitialMode); @@ -4653,8 +4716,8 @@ void Application::idle() { PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate()); } PROFILE_COUNTER_IF_CHANGED(app, "renderLoopRate", float, _renderLoopCounter.rate()); - PROFILE_COUNTER_IF_CHANGED(app, "currentDownloads", int, ResourceCache::getLoadingRequests().length()); - PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", int, ResourceCache::getPendingRequestCount()); + PROFILE_COUNTER_IF_CHANGED(app, "currentDownloads", uint32_t, ResourceCache::getLoadingRequestCount()); + PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", uint32_t, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); auto renderConfig = _renderEngine->getConfiguration(); @@ -5019,12 +5082,12 @@ void Application::saveSettings() const { PluginManager::getInstance()->saveSettings(); } -bool Application::importEntities(const QString& urlOrFilename) { +bool Application::importEntities(const QString& urlOrFilename, const bool isObservable, const qint64 callerId) { bool success = false; _entityClipboard->withWriteLock([&] { _entityClipboard->eraseAllOctreeElements(); - success = _entityClipboard->readFromURL(urlOrFilename); + success = _entityClipboard->readFromURL(urlOrFilename, isObservable, callerId); if (success) { _entityClipboard->reaverageOctreeElements(); } @@ -5399,13 +5462,21 @@ void Application::reloadResourceCaches() { queryOctree(NodeType::EntityServer, PacketType::EntityQuery); + // Clear the entities and their renderables + getEntities()->clear(); + DependencyManager::get()->clearCache(); DependencyManager::get()->clearCache(); + // Clear all the resource caches + DependencyManager::get()->clear(); DependencyManager::get()->refreshAll(); - DependencyManager::get()->refreshAll(); DependencyManager::get()->refreshAll(); + MaterialCache::instance().refreshAll(); + DependencyManager::get()->refreshAll(); + ShaderCache::instance().refreshAll(); DependencyManager::get()->refreshAll(); + DependencyManager::get()->refreshAll(); DependencyManager::get()->reset(); // Force redownload of .fst models @@ -5801,6 +5872,42 @@ void Application::update(float deltaTime) { controller::Pose pose = userInputMapper->getPoseState(action); myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix)); } + + static const std::vector trackedObjectStringLiterals = { + QStringLiteral("_TrackedObject00"), QStringLiteral("_TrackedObject01"), QStringLiteral("_TrackedObject02"), QStringLiteral("_TrackedObject03"), + QStringLiteral("_TrackedObject04"), QStringLiteral("_TrackedObject05"), QStringLiteral("_TrackedObject06"), QStringLiteral("_TrackedObject07"), + QStringLiteral("_TrackedObject08"), QStringLiteral("_TrackedObject09"), QStringLiteral("_TrackedObject10"), QStringLiteral("_TrackedObject11"), + QStringLiteral("_TrackedObject12"), QStringLiteral("_TrackedObject13"), QStringLiteral("_TrackedObject14"), QStringLiteral("_TrackedObject15") + }; + + // Controlled by the Developer > Avatar > Show Tracked Objects menu. + if (_showTrackedObjects) { + static const std::vector trackedObjectActions = { + controller::Action::TRACKED_OBJECT_00, controller::Action::TRACKED_OBJECT_01, controller::Action::TRACKED_OBJECT_02, controller::Action::TRACKED_OBJECT_03, + controller::Action::TRACKED_OBJECT_04, controller::Action::TRACKED_OBJECT_05, controller::Action::TRACKED_OBJECT_06, controller::Action::TRACKED_OBJECT_07, + controller::Action::TRACKED_OBJECT_08, controller::Action::TRACKED_OBJECT_09, controller::Action::TRACKED_OBJECT_10, controller::Action::TRACKED_OBJECT_11, + controller::Action::TRACKED_OBJECT_12, controller::Action::TRACKED_OBJECT_13, controller::Action::TRACKED_OBJECT_14, controller::Action::TRACKED_OBJECT_15 + }; + + int i = 0; + glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f); + for (auto& action : trackedObjectActions) { + controller::Pose pose = userInputMapper->getPoseState(action); + if (pose.valid) { + glm::vec3 pos = transformPoint(myAvatarMatrix, pose.translation); + glm::quat rot = glmExtractRotation(myAvatarMatrix) * pose.rotation; + DebugDraw::getInstance().addMarker(trackedObjectStringLiterals[i], rot, pos, BLUE); + } else { + DebugDraw::getInstance().removeMarker(trackedObjectStringLiterals[i]); + } + i++; + } + } else if (_prevShowTrackedObjects) { + for (auto& key : trackedObjectStringLiterals) { + DebugDraw::getInstance().removeMarker(key); + } + } + _prevShowTrackedObjects = _showTrackedObjects; } updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... @@ -5967,7 +6074,9 @@ void Application::update(float deltaTime) { { PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("overlays"); - _overlays.update(deltaTime); + if (qApp->shouldPaint()) { + _overlays.update(deltaTime); + } } // Update _viewFrustum with latest camera and view frustum data... @@ -6052,8 +6161,10 @@ void Application::update(float deltaTime) { PROFILE_RANGE_EX(app, "PostUpdateLambdas", 0xffff0000, (uint64_t)0); PerformanceTimer perfTimer("postUpdateLambdas"); std::unique_lock guard(_postUpdateLambdasLock); - for (auto& iter : _postUpdateLambdas) { - iter.second(); + if (qApp->shouldPaint()) { + for (auto& iter : _postUpdateLambdas) { + iter.second(); + } } _postUpdateLambdas.clear(); } @@ -6470,9 +6581,12 @@ void Application::clearDomainOctreeDetails() { skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_DEFAULT); DependencyManager::get()->clearUnusedResources(); - DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); + MaterialCache::instance().clearUnusedResources(); + DependencyManager::get()->clearUnusedResources(); + ShaderCache::instance().clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); + DependencyManager::get()->clearUnusedResources(); getMyAvatar()->setAvatarEntityDataChanged(true); } @@ -6758,6 +6872,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HMD", DependencyManager::get().data()); @@ -6794,9 +6910,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); scriptEngine->registerGlobalObject("Selection", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Wallet", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); + scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); @@ -7183,7 +7300,8 @@ void Application::addAssetToWorldFromURL(QString url) { addAssetToWorldInfo(filename, "Downloading model file " + filename + "."); - auto request = DependencyManager::get()->createResourceRequest(nullptr, QUrl(url)); + auto request = DependencyManager::get()->createResourceRequest( + nullptr, QUrl(url), true, -1, "Application::addAssetToWorldFromURL"); connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished); request->send(); } @@ -7797,6 +7915,7 @@ void Application::loadAvatarBrowser() const { auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; + QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); DependencyManager::get()->openTablet(); @@ -8292,6 +8411,10 @@ void Application::setShowBulletConstraintLimits(bool value) { _physicsEngine->setShowBulletConstraintLimits(value); } +void Application::setShowTrackedObjects(bool value) { + _showTrackedObjects = value; +} + void Application::startHMDStandBySession() { _autoSwitchDisplayModeSupportedHMDPlugin->startStandBySession(); } @@ -8463,6 +8586,16 @@ QUuid Application::getTabletFrameID() const { return HMD->getCurrentTabletFrameID(); } +QVector Application::getTabletIDs() const { + // Most important overlays first. + QVector result; + auto HMD = DependencyManager::get(); + result << HMD->getCurrentTabletScreenID(); + result << HMD->getCurrentHomeButtonID(); + result << HMD->getCurrentTabletFrameID(); + return result; +} + void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; diff --git a/interface/src/Application.h b/interface/src/Application.h index 75260b910f..14e30b8006 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -52,7 +52,6 @@ #include #include "avatar/MyAvatar.h" -#include "BandwidthRecorder.h" #include "FancyCamera.h" #include "ConnectionMonitor.h" #include "CursorManager.h" @@ -298,6 +297,7 @@ public: OverlayID getTabletScreenID() const; OverlayID getTabletHomeButtonID() const; QUuid getTabletFrameID() const; // may be an entity or an overlay + QVector getTabletIDs() const; // In order of most important IDs first. void setAvatarOverrideUrl(const QUrl& url, bool save); void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; } @@ -334,13 +334,15 @@ signals: void uploadRequest(QString path); + void interstitialModeChanged(bool isInInterstitialMode); + void loginDialogPoppedUp(); public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, float x, float y, float z, float scale); - bool importEntities(const QString& url); + bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1); void updateThreadPoolCount() const; void updateSystemTabletMode(); void goToErrorDomainURL(QUrl errorDomainURL); @@ -496,6 +498,8 @@ private slots: void setShowBulletConstraints(bool value); void setShowBulletConstraintLimits(bool value); + void setShowTrackedObjects(bool value); + private: void init(); bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event); @@ -777,5 +781,8 @@ private: std::atomic _pendingRenderEvent { true }; bool quitWhenFinished { false }; + + bool _showTrackedObjects { false }; + bool _prevShowTrackedObjects { false }; }; #endif // hifi_Application_h diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 5c79bedc9a..ce21c68c3d 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -78,8 +78,10 @@ void addAvatarEntities(const QVariantList& avatarEntities) { } entity->setLastBroadcast(usecTimestampNow()); - // since we're creating this object we will immediately volunteer to own its simulation - entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); + if (entityProperties.getDynamic()) { + // since we're creating a dynamic object we volunteer immediately to own its simulation + entity->upgradeScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); + } entityProperties.setLastEdited(entity->getLastEdited()); } else { qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree"; @@ -108,6 +110,9 @@ AvatarBookmarks::AvatarBookmarks() { if (!QFile::copy(defaultBookmarksFilename, _bookmarksFilename)) { qDebug() << "failed to copy" << defaultBookmarksFilename << "to" << _bookmarksFilename; + } else { + QFile bookmarksFile(_bookmarksFilename); + bookmarksFile.setPermissions(bookmarksFile.permissions() | QFile::WriteUser); } } readFromFile(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index eef14c873e..1fc1e0c033 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -265,6 +265,18 @@ Menu::Menu() { QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog"); }); + // Settings > Security... + action = addActionToQMenuAndActionHash(settingsMenu, "Security..."); + connect(action, &QAction::triggered, [] { + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + auto hmd = DependencyManager::get(); + tablet->pushOntoStack("hifi/dialogs/security/Security.qml"); + + if (!hmd->getShouldShowTablet()) { + hmd->toggleShouldShowTablet(); + } + }); + // Settings > Developer Menu addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus())); @@ -273,21 +285,79 @@ 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(), + SLOT(toggleConsole()), + QAction::NoRole, + UNSPECIFIED_POSITION); + // Developer > Scripting > API Debugger + action = addActionToQMenuAndActionHash(scriptingOptionsMenu, "API Debugger"); + connect(action, &QAction::triggered, [] { + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); + 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())); + { + auto nodeList = DependencyManager::get(); + QObject::connect(nodeList.data(), &NodeList::canRezChanged, essLogAction, [essLogAction] { + auto nodeList = DependencyManager::get(); + essLogAction->setEnabled(nodeList->getThisNodeCanRez()); + }); + essLogAction->setEnabled(nodeList->getThisNodeCanRez()); + } + + // Developer > Scripting > Script Log (HMD friendly)... + addActionToQMenuAndActionHash(scriptingOptionsMenu, "Script Log (HMD friendly)...", Qt::NoButton, + qApp, SLOT(showScriptLogs())); + + // 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(); + QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(scriptingOptionsMenu, MenuOption::ControlWithSpeech, + Qt::CTRL | Qt::SHIFT | Qt::Key_C, + speechRecognizer->getEnabled(), + speechRecognizer.data(), + SLOT(setEnabled(bool)), + 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()); connect(action, &QAction::triggered, [action] { qApp->setHmdTabletBecomesToolbarSetting(action->isChecked()); }); + addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Use3DKeyboard, 0, true); + // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); @@ -570,6 +640,8 @@ Menu::Menu() { avatar.get(), SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowTrackedObjects, 0, false, qApp, SLOT(setShowTrackedObjects(bool))); + // Developer > Hands >>> MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false, @@ -684,10 +756,11 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(pickingOptionsMenu, MenuOption::ForceCoarsePicking, 0, false, DependencyManager::get().data(), SLOT(setForceCoarsePicking(bool))); - // Developer > Display Crash Options - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); // Developer > Crash >>> MenuWrapper* crashMenu = developerMenu->addMenu("Crash"); + + // Developer > Crash > Display Crash Options + addCheckableActionToQMenuAndActionHash(crashMenu, MenuOption::DisplayCrashOptions, 0, true); addActionToQMenuAndActionHash(crashMenu, MenuOption::DeadlockInterface, 0, qApp, SLOT(deadlockApplication())); addActionToQMenuAndActionHash(crashMenu, MenuOption::UnresponsiveInterface, 0, qApp, SLOT(unresponsiveApplication())); @@ -722,59 +795,15 @@ Menu::Menu() { action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFaultThreaded); connect(action, &QAction::triggered, qApp, []() { std::thread(crash::newFault).join(); }); - // Developer > Stats + // Developer > Show Statistics addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats); + + // Developer > Show Animation Statistics addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AnimStats); - // Settings > Enable Speech Control API -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) - auto speechRecognizer = DependencyManager::get(); - QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::ControlWithSpeech, - Qt::CTRL | Qt::SHIFT | Qt::Key_C, - speechRecognizer->getEnabled(), - speechRecognizer.data(), - SLOT(setEnabled(bool)), - UNSPECIFIED_POSITION); - connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); -#endif - - // console - addActionToQMenuAndActionHash(developerMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, - DependencyManager::get().data(), - SLOT(toggleConsole()), - QAction::NoRole, - UNSPECIFIED_POSITION); - - // Developer > API Debugger - action = addActionToQMenuAndActionHash(developerMenu, "API Debugger"); - connect(action, &QAction::triggered, [] { - QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); - defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js"); - DependencyManager::get()->loadScript(defaultScriptsLoc.toString()); - }); - - // Developer > Log... + // Developer > Log addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L, qApp, SLOT(toggleLogDialog())); - auto essLogAction = addActionToQMenuAndActionHash(developerMenu, MenuOption::EntityScriptServerLog, 0, - qApp, SLOT(toggleEntityScriptServerLogDialog())); - { - auto nodeList = DependencyManager::get(); - QObject::connect(nodeList.data(), &NodeList::canRezChanged, essLogAction, [essLogAction] { - auto nodeList = DependencyManager::get(); - essLogAction->setEnabled(nodeList->getThisNodeCanRez()); - }); - essLogAction->setEnabled(nodeList->getThisNodeCanRez()); - } - - addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)...", Qt::NoButton, - qApp, SLOT(showScriptLogs())); - - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VerboseLogging, 0, false, - qApp, SLOT(updateVerboseLogging())); - - // Developer > Show Overlays - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Overlays, 0, true); #if 0 /// -------------- REMOVED FOR NOW -------------- addDisabledActionAndSeparator(navigateMenu, "History"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 031ee2561c..f1d56825b5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -183,6 +183,7 @@ namespace MenuOption { const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; + const QString ShowTrackedObjects = "Show Tracked Objects"; const QString SendWrongDSConnectVersion = "Send wrong DS connect version"; const QString SendWrongProtocolVersion = "Send wrong protocol version"; const QString SetHomeLocation = "Set Home Location"; @@ -209,6 +210,7 @@ namespace MenuOption { const QString TurnWithHead = "Turn using Head"; const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; + const QString Use3DKeyboard = "Use 3D Keyboard"; const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 3a5d92eb8c..21d3477d7e 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -109,10 +109,10 @@ bool ModelPackager::loadModel() { qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); QByteArray fbxContents = fbx.readAll(); - _geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); + _hfmModel.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); // make sure we have some basic mappings - populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry); + populateBasicMapping(_mapping, _fbxInfo.filePath(), *_hfmModel); } catch (const QString& error) { qCDebug(interfaceapp) << "Error reading " << _fbxInfo.filePath() << ": " << error; return false; @@ -122,7 +122,7 @@ bool ModelPackager::loadModel() { bool ModelPackager::editProperties() { // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_geometry); + ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_hfmModel); if (properties.exec() == QDialog::Rejected) { return false; } @@ -235,18 +235,18 @@ bool ModelPackager::zipModel() { return true; } -void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { +void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel) { bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file - bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || - (geometry.blendshapeChannelNames.contains("BrowsDown_Right") && - geometry.blendshapeChannelNames.contains("MouthOpen") && - geometry.blendshapeChannelNames.contains("Blink_Left") && - geometry.blendshapeChannelNames.contains("Blink_Right") && - geometry.blendshapeChannelNames.contains("Squint_Right")); + bool likelyMixamoFile = hfmModel.applicationName == "mixamo.com" || + (hfmModel.blendshapeChannelNames.contains("BrowsDown_Right") && + hfmModel.blendshapeChannelNames.contains("MouthOpen") && + hfmModel.blendshapeChannelNames.contains("Blink_Left") && + hfmModel.blendshapeChannelNames.contains("Blink_Right") && + hfmModel.blendshapeChannelNames.contains("Squint_Right")); if (!mapping.contains(NAME_FIELD)) { mapping.insert(NAME_FIELD, QFileInfo(filename).baseName()); @@ -268,15 +268,15 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename } QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); if (!joints.contains("jointEyeLeft")) { - joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : - (geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); + joints.insert("jointEyeLeft", hfmModel.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : + (hfmModel.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); } if (!joints.contains("jointEyeRight")) { - joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : - geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); + joints.insert("jointEyeRight", hfmModel.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : + hfmModel.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); } if (!joints.contains("jointNeck")) { - joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); + joints.insert("jointNeck", hfmModel.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); } if (isBodyType) { @@ -296,7 +296,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename if (!joints.contains("jointHead")) { const char* topName = likelyMixamoFile ? "HeadTop_End" : "HeadEnd"; - joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); + joints.insert("jointHead", hfmModel.jointIndices.contains(topName) ? topName : "Head"); } mapping.insert(JOINT_FIELD, joints); @@ -370,7 +370,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (const FBXMaterial mat, _geometry->materials) { + foreach (const HFMMaterial mat, _hfmModel->materials) { if (!mat.albedoTexture.filename.isEmpty() && mat.albedoTexture.content.isEmpty() && !_textures.contains(mat.albedoTexture.filename)) { _textures << mat.albedoTexture.filename; diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 76295e5a85..ed86f15008 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -19,7 +19,7 @@ #include "ui/ModelsBrowser.h" -class FBXGeometry; +class HFMModel; class ModelPackager : public QObject { public: @@ -32,7 +32,7 @@ private: bool editProperties(); bool zipModel(); - void populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry); + void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel); void listTextures(); bool copyTextures(const QString& oldDir, const QDir& newDir); @@ -44,7 +44,7 @@ private: QString _scriptDir; QVariantHash _mapping; - std::unique_ptr _geometry; + std::unique_ptr _hfmModel; QStringList _textures; QStringList _scripts; }; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index 8984f89d07..49c57744a9 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -27,11 +27,11 @@ ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry) : + const QString& basePath, const HFMModel& hfmModel) : _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), -_geometry(geometry) +_hfmModel(hfmModel) { setWindowTitle("Set Model Properties"); @@ -108,8 +108,8 @@ QVariantHash ModelPropertiesDialog::getMapping() const { // update the joint indices QVariantHash jointIndices; - for (int i = 0; i < _geometry.joints.size(); i++) { - jointIndices.insert(_geometry.joints.at(i).name, QString::number(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); @@ -118,10 +118,10 @@ QVariantHash ModelPropertiesDialog::getMapping() const { if (_modelType == FSTReader::ATTACHMENT_MODEL) { glm::vec3 pivot; if (_pivotAboutCenter->isChecked()) { - pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; + pivot = (_hfmModel.meshExtents.minimum + _hfmModel.meshExtents.maximum) * 0.5f; } else if (_pivotJoint->currentIndex() != 0) { - pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); + pivot = extractTranslation(_hfmModel.joints.at(_pivotJoint->currentIndex() - 1).transform); } mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value()); mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value()); @@ -191,7 +191,7 @@ void ModelPropertiesDialog::reset() { } foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { QString jointName = joint.toString(); - if (_geometry.jointIndices.contains(jointName)) { + if (_hfmModel.jointIndices.contains(jointName)) { createNewFreeJoint(jointName); } } @@ -249,8 +249,8 @@ QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { if (withNone) { box->addItem("(none)"); } - foreach (const FBXJoint& joint, _geometry.joints) { - if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) { + foreach (const HFMJoint& joint, _hfmModel.joints) { + if (joint.isSkeletonJoint || !_hfmModel.hasSkeletonJoints) { box->addItem(joint.name); } } @@ -266,7 +266,7 @@ QDoubleSpinBox* ModelPropertiesDialog::createTranslationBox() const { } void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const { - if (_geometry.jointIndices.contains(name)) { + if (_hfmModel.jointIndices.contains(name)) { joints.insert(joint, name); } else { joints.remove(joint); diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index e3c2d8ed6a..0bf8075197 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -30,7 +30,7 @@ class ModelPropertiesDialog : public QDialog { public: ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry); + const QString& basePath, const HFMModel& hfmModel); QVariantHash getMapping() const; @@ -50,7 +50,7 @@ private: FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; - FBXGeometry _geometry; + HFMModel _hfmModel; QLineEdit* _name = nullptr; QPushButton* _textureDirectory = nullptr; QPushButton* _scriptDirectory = nullptr; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index e5e94da68a..bc72c7d7c5 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 45ac80b054..d1f882b232 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -53,7 +53,8 @@ void ATPAssetMigrator::loadEntityServerFile() { auto migrateResources = [=](QUrl migrationURL, QJsonValueRef jsonValue, bool isModelURL) { auto request = - DependencyManager::get()->createResourceRequest(this, migrationURL); + DependencyManager::get()->createResourceRequest( + this, migrationURL, true, -1, "ATPAssetMigrator::loadEntityServerFile"); if (request) { qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration"; @@ -121,8 +122,8 @@ void ATPAssetMigrator::loadEntityServerFile() { QUrl migrationURL = QUrl(migrationURLString); if (!_ignoredUrls.contains(migrationURL) - && (migrationURL.scheme() == URL_SCHEME_HTTP || migrationURL.scheme() == URL_SCHEME_HTTPS - || migrationURL.scheme() == URL_SCHEME_FILE || migrationURL.scheme() == URL_SCHEME_FTP)) { + && (migrationURL.scheme() == HIFI_URL_SCHEME_HTTP || migrationURL.scheme() == HIFI_URL_SCHEME_HTTPS + || migrationURL.scheme() == HIFI_URL_SCHEME_FILE || migrationURL.scheme() == HIFI_URL_SCHEME_FTP)) { if (_pendingReplacements.contains(migrationURL)) { // we already have a request out for this asset, just store the QJsonValueRef diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 230f8aa64b..53074ac4ba 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -135,7 +135,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: glm::vec3 palmPosition; glm::quat palmRotation; - bool isTransitingWithAvatar = holdingAvatar->getTransit()->isTransiting(); + bool isTransitingWithAvatar = holdingAvatar->getTransit()->isActive(); if (isTransitingWithAvatar != _isTransitingWithAvatar) { _isTransitingWithAvatar = isTransitingWithAvatar; auto ownerEntity = _ownerEntity.lock(); @@ -424,7 +424,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { if (ownerEntity) { ownerEntity->setDynamicDataDirty(true); ownerEntity->setDynamicDataNeedsTransmit(true); - ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isTransiting()); + ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isActive()); } }); } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d31b201dc7..a5c6f7b000 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -55,15 +55,7 @@ static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND / // We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key. const QUuid MY_AVATAR_KEY; // NULL key -namespace { - // For an unknown avatar-data packet, wait this long before requesting the identity. - constexpr std::chrono::milliseconds REQUEST_UNKNOWN_IDENTITY_DELAY { 5 * 1000 }; - constexpr int REQUEST_UNKNOWN_IDENTITY_TRANSMITS = 3; -} -using std::chrono::steady_clock; - AvatarManager::AvatarManager(QObject* parent) : - _avatarsToFade(), _myAvatar(new MyAvatar(qApp->thread()), [](MyAvatar* ptr) { ptr->deleteLater(); }) { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar @@ -79,14 +71,12 @@ AvatarManager::AvatarManager(QObject* parent) : } }); - const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f; - const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing - const int AVATAR_TRANSIT_FRAMES_PER_METER = 1; // Based on testing - _transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT; - _transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE; + _transitConfig._minTriggerDistance = AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE; + _transitConfig._maxTriggerDistance = AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE; _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; - _transitConfig._isDistanceBased = true; + _transitConfig._isDistanceBased = AVATAR_TRANSIT_DISTANCE_BASED; + _transitConfig._abortDistance = AVATAR_TRANSIT_ABORT_DISTANCE; } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -134,13 +124,39 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { _space = space; } +void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { + switch (status) { + case AvatarTransit::Status::STARTED: + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("preTransitAnim"); + break; + case AvatarTransit::Status::START_TRANSIT: + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("transitAnim"); + break; + case AvatarTransit::Status::END_TRANSIT: + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("postTransitAnim"); + break; + case AvatarTransit::Status::ENDED: + _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("idleAnim"); + break; + case AvatarTransit::Status::PRE_TRANSIT: + break; + case AvatarTransit::Status::POST_TRANSIT: + break; + case AvatarTransit::Status::IDLE: + break; + case AvatarTransit::Status::TRANSITING: + break; + case AvatarTransit::Status::ABORT_TRANSIT: + break; + } +} + void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); - AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig); - bool sendFirstTransitPackage = (status == AvatarTransit::Status::START_TRANSIT); - bool blockTransitData = (status == AvatarTransit::Status::TRANSITING); + AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _myAvatar->getSensorToWorldScale(), _transitConfig); + handleTransitAnimations(status); _myAvatar->update(deltaTime); render::Transaction transaction; @@ -150,18 +166,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) { quint64 now = usecTimestampNow(); quint64 dt = now - _lastSendAvatarDataTime; - - if (sendFirstTransitPackage || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused && !blockTransitData)) { + if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) { // send head/hand data to the avatar mixer and voxel server - PerformanceTimer perfTimer("send"); - if (sendFirstTransitPackage) { - _myAvatar->overrideNextPackagePositionData(_myAvatar->getTransit()->getEndPosition()); - } + PerformanceTimer perfTimer("send"); _myAvatar->sendAvatarDataPacket(); _lastSendAvatarDataTime = now; _myAvatarSendRate.increment(); } - } @@ -250,7 +261,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) { const SortableAvatar& sortData = *it; const auto avatar = std::static_pointer_cast(sortData.getAvatar()); - + if (!avatar->_isClientAvatar) { + avatar->setIsClientAvatar(true); + } // TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update if (avatar->getSkeletonModel()->isLoaded()) { // remove the orb if it is there @@ -275,7 +288,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } - auto transitStatus = avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig); + auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig); if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) { avatar->_transit.reset(); avatar->setIsNewAvatar(false); @@ -322,28 +335,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { simulateAvatarFades(deltaTime); - // Check on avatars with pending identities: - steady_clock::time_point now = steady_clock::now(); - QWriteLocker writeLock(&_hashLock); - for (auto pendingAvatar = _pendingAvatars.begin(); pendingAvatar != _pendingAvatars.end(); ++pendingAvatar) { - if (now - pendingAvatar->creationTime >= REQUEST_UNKNOWN_IDENTITY_DELAY) { - // Too long without an ID - sendIdentityRequest(pendingAvatar->avatar->getID()); - if (++pendingAvatar->transmits >= REQUEST_UNKNOWN_IDENTITY_TRANSMITS) { - qCDebug(avatars) << "Requesting identity for unknown avatar (final request)" << - pendingAvatar->avatar->getID().toString(); - - pendingAvatar = _pendingAvatars.erase(pendingAvatar); - if (pendingAvatar == _pendingAvatars.end()) { - break; - } - } else { - pendingAvatar->creationTime = now; - qCDebug(avatars) << "Requesting identity for unknown avatar" << pendingAvatar->avatar->getID().toString(); - } - } - } - _avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC; } @@ -846,7 +837,7 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV } } -QVariantMap AvatarManager::getPalData(const QList specificAvatarIdentifiers) { +QVariantMap AvatarManager::getPalData(const QStringList& specificAvatarIdentifiers) { QJsonArray palData; auto avatarMap = getHashCopy(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 209b976c44..422f6acffb 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -55,6 +55,7 @@ using SortedAvatar = std::pair>; * @borrows AvatarList.sessionUUIDChanged as sessionUUIDChanged * @borrows AvatarList.processAvatarDataPacket as processAvatarDataPacket * @borrows AvatarList.processAvatarIdentityPacket as processAvatarIdentityPacket + * @borrows AvatarList.processBulkAvatarTraits as processBulkAvatarTraits * @borrows AvatarList.processKillAvatar as processKillAvatar */ @@ -152,6 +153,13 @@ public: const QVector& avatarsToInclude, const QVector& avatarsToDiscard); + /**jsdoc + * @function AvatarManager.findParabolaIntersectionVector + * @param {PickParabola} pick + * @param {Uuid[]} avatarsToInclude + * @param {Uuid[]} avatarsToDiscard + * @returns {ParabolaToAvatarIntersectionResult} + */ Q_INVOKABLE ParabolaToAvatarIntersectionResult findParabolaIntersectionVector(const PickParabola& pick, const QVector& avatarsToInclude, const QVector& avatarsToDiscard); @@ -176,11 +184,11 @@ public: * than iterating over each avatar and obtaining data about them in JavaScript, as that method * locks and unlocks each avatar's data structure potentially hundreds of times per update tick. * @function AvatarManager.getPalData - * @param {string[]} specificAvatarIdentifiers - A list of specific Avatar Identifiers about - * which you want to get PAL data - * @returns {object} + * @param {string[]} [specificAvatarIdentifiers=[]] - The list of IDs of the avatars you want the PAL data for. + * If an empty list, the PAL data for all nearby avatars is returned. + * @returns {object[]} An array of objects, each object being the PAL data for an avatar. */ - Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList()); + Q_INVOKABLE QVariantMap getPalData(const QStringList& specificAvatarIdentifiers = QStringList()); float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } @@ -213,6 +221,7 @@ private: // frequently grabs a read lock on the hash to get a given avatar by ID void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; + void handleTransitAnimations(AvatarTransit::Status status); QVector _avatarsToFade; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b347963cf1..876e64c592 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -122,7 +123,6 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()), _dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND), _headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f), _scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale), @@ -155,8 +155,8 @@ MyAvatar::MyAvatar(QThread* thread) : }); connect(_skeletonModel.get(), &Model::rigReady, this, [this]() { if (_shouldLoadScripts) { - auto geometry = getSkeletonModel()->getFBXGeometry(); - qApp->loadAvatarScripts(geometry.scripts); + auto hfmModel = getSkeletonModel()->getHFMModel(); + qApp->loadAvatarScripts(hfmModel.scripts); _shouldLoadScripts = false; } // Load and convert old attachments to avatar entities @@ -464,10 +464,74 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { } } +void MyAvatar::updateSitStandState(float newHeightReading, float dt) { + const float STANDING_HEIGHT_MULTIPLE = 1.2f; + const float SITTING_HEIGHT_MULTIPLE = 0.833f; + const float SITTING_TIMEOUT = 4.0f; // 4 seconds + const float STANDING_TIMEOUT = 0.3333f; // 1/3 second + const float SITTING_UPPER_BOUND = 1.52f; + if (!getIsSitStandStateLocked()) { + if (!getIsAway() && qApp->isHMDMode()) { + if (getIsInSittingState()) { + if (newHeightReading > (STANDING_HEIGHT_MULTIPLE * _tippingPoint)) { + // if we recenter upwards then no longer in sitting state + _sitStandStateTimer += dt; + if (_sitStandStateTimer > STANDING_TIMEOUT) { + _averageUserHeightSensorSpace = newHeightReading; + _tippingPoint = newHeightReading; + setIsInSittingState(false); + } + } else if (newHeightReading < (SITTING_HEIGHT_MULTIPLE * _tippingPoint)) { + // if we are mis labelled as sitting but we are standing in the real world this will + // make sure that a real sit is still recognized so we won't be stuck in sitting unable to change state + _sitStandStateTimer += dt; + if (_sitStandStateTimer > SITTING_TIMEOUT) { + _averageUserHeightSensorSpace = newHeightReading; + _tippingPoint = newHeightReading; + // here we stay in sit state but reset the average height + setIsInSittingState(true); + } + } else { + // sanity check if average height greater than 5ft they are not sitting(or get off your dangerous barstool please) + if (_averageUserHeightSensorSpace > SITTING_UPPER_BOUND) { + setIsInSittingState(false); + } else { + // tipping point is average height when sitting. + _tippingPoint = _averageUserHeightSensorSpace; + _sitStandStateTimer = 0.0f; + } + } + } else { + // in the standing state + if (newHeightReading < (SITTING_HEIGHT_MULTIPLE * _tippingPoint)) { + _sitStandStateTimer += dt; + if (_sitStandStateTimer > SITTING_TIMEOUT) { + _averageUserHeightSensorSpace = newHeightReading; + _tippingPoint = newHeightReading; + setIsInSittingState(true); + } + } else { + // use the mode height for the tipping point when we are standing. + _tippingPoint = getCurrentStandingHeight(); + _sitStandStateTimer = 0.0f; + } + } + } else { + //if you are away then reset the average and set state to standing. + _averageUserHeightSensorSpace = _userHeight.get(); + _tippingPoint = _userHeight.get(); + setIsInSittingState(false); + } + } +} + void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders + const float COSINE_THIRTY_DEGREES = 0.866f; + const float SQUATTY_TIMEOUT = 30.0f; // 30 seconds + const float HEIGHT_FILTER_COEFFICIENT = 0.01f; float tau = deltaTime / HMD_FACING_TIMESCALE; setHipToHandController(computeHandAzimuth()); @@ -494,11 +558,36 @@ void MyAvatar::update(float deltaTime) { _smoothOrientationTimer += deltaTime; } - float newHeightReading = getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y; - int newHeightReadingInCentimeters = glm::floor(newHeightReading * CENTIMETERS_PER_METER); - _recentModeReadings.insert(newHeightReadingInCentimeters); - setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); + controller::Pose newHeightReading = getControllerPoseInSensorFrame(controller::Action::HEAD); + if (newHeightReading.isValid()) { + int newHeightReadingInCentimeters = glm::floor(newHeightReading.getTranslation().y * CENTIMETERS_PER_METER); + _averageUserHeightSensorSpace = lerp(_averageUserHeightSensorSpace, newHeightReading.getTranslation().y, HEIGHT_FILTER_COEFFICIENT); + _recentModeReadings.insert(newHeightReadingInCentimeters); + setCurrentStandingHeight(computeStandingHeightMode(newHeightReading)); + setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); + } + + // if the spine is straight and the head is below the default position by 5 cm then increment squatty count. + const float SQUAT_THRESHOLD = 0.05f; + glm::vec3 headDefaultPositionAvatarSpace = getAbsoluteDefaultJointTranslationInObjectFrame(getJointIndex("Head")); + glm::quat spine2OrientationAvatarSpace = getAbsoluteJointRotationInObjectFrame(getJointIndex("Spine2")); + glm::vec3 upSpine2 = spine2OrientationAvatarSpace * glm::vec3(0.0f, 1.0f, 0.0f); + if (glm::length(upSpine2) > 0.0f) { + upSpine2 = glm::normalize(upSpine2); + } + float angleSpine2 = glm::dot(upSpine2, glm::vec3(0.0f, 1.0f, 0.0f)); + if (getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) && (angleSpine2 > COSINE_THIRTY_DEGREES)) { + _squatTimer += deltaTime; + if (_squatTimer > SQUATTY_TIMEOUT) { + _squatTimer = 0.0f; + _follow._squatDetected = true; + } + } else { + _squatTimer = 0.0f; + } + + // put update sit stand state counts here + updateSitStandState(newHeightReading.getTranslation().y, deltaTime); if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); @@ -702,6 +791,46 @@ void MyAvatar::simulate(float deltaTime) { // before we perform rig animations and IK. updateSensorToWorldMatrix(); + // if we detect the hand controller is at rest, i.e. lying on the table, or the hand is too far away from the hmd + // disable the associated hand controller input. + { + // NOTE: all poses are in sensor space. + auto leftHandIter = _controllerPoseMap.find(controller::Action::LEFT_HAND); + if (leftHandIter != _controllerPoseMap.end() && leftHandIter->second.isValid()) { + _leftHandAtRestDetector.update(leftHandIter->second.getTranslation(), leftHandIter->second.getRotation()); + if (_leftHandAtRestDetector.isAtRest()) { + leftHandIter->second.valid = false; + } + } else { + _leftHandAtRestDetector.invalidate(); + } + + auto rightHandIter = _controllerPoseMap.find(controller::Action::RIGHT_HAND); + if (rightHandIter != _controllerPoseMap.end() && rightHandIter->second.isValid()) { + _rightHandAtRestDetector.update(rightHandIter->second.getTranslation(), rightHandIter->second.getRotation()); + if (_rightHandAtRestDetector.isAtRest()) { + rightHandIter->second.valid = false; + } + } else { + _rightHandAtRestDetector.invalidate(); + } + + auto headIter = _controllerPoseMap.find(controller::Action::HEAD); + + // The 99th percentile man has a spine to fingertip to height ratio of 0.45. Lets increase that by about 10% to 0.5 + // then measure the distance the center of the eyes to the finger tips. To come up with this ratio. + // From "The Measure of Man and Woman: Human Factors in Design, Revised Edition" by Alvin R. Tilley, Henry Dreyfuss Associates + const float MAX_HEAD_TO_HAND_DISTANCE_RATIO = 0.52f; + + float maxHeadHandDistance = getUserHeight() * MAX_HEAD_TO_HAND_DISTANCE_RATIO; + if (glm::length(headIter->second.getTranslation() - leftHandIter->second.getTranslation()) > maxHeadHandDistance) { + leftHandIter->second.valid = false; + } + if (glm::length(headIter->second.getTranslation() - rightHandIter->second.getTranslation()) > maxHeadHandDistance) { + rightHandIter->second.valid = false; + } + } + { PerformanceTimer perfTimer("skeleton"); @@ -929,7 +1058,6 @@ void MyAvatar::updateSensorToWorldMatrix() { updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); if (hasSensorToWorldScaleChanged) { - setTransitScale(sensorToWorldScale); emit sensorToWorldScaleChanged(sensorToWorldScale); } @@ -1765,8 +1893,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { bool isWearableEntity(const EntityItemPointer& entity) { return entity->isVisible() - && (entity->getParentJointIndex() != INVALID_JOINT_INDEX - || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) && (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == AVATAR_SELF_ID); } @@ -1942,7 +2068,7 @@ void MyAvatar::updateMotors() { horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE; verticalMotorTimescale = FLYING_MOTOR_TIMESCALE; } else { - horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE; + horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE * getSensorToWorldScale(); verticalMotorTimescale = INVALID_MOTOR_TIMESCALE; } @@ -2301,10 +2427,10 @@ void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, Enti void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { - neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex; + neckJointIndex = _skeletonModel->getHFMModel().neckJointIndex; } if (neckJointIndex == -1) { - neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); + neckJointIndex = (_skeletonModel->getHFMModel().headJointIndex - 1); if (neckJointIndex < 0) { // return if the head is not even there. can't cauterize!! return; @@ -2315,7 +2441,7 @@ void MyAvatar::initHeadBones() { q.push(neckJointIndex); _headBoneSet.insert(neckJointIndex); - // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + // hfmJoints only hold links to parents not children, so we have to do a bit of extra work here. while (q.size() > 0) { int jointIndex = q.front(); for (int i = 0; i < _skeletonModel->getJointStateCount(); i++) { @@ -2464,11 +2590,11 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel && _skeletonModel->isLoaded()) { const Rig& rig = _skeletonModel->getRig(); - const FBXGeometry& geometry = _skeletonModel->getFBXGeometry(); + const HFMModel& hfmModel = _skeletonModel->getHFMModel(); for (int i = 0; i < rig.getJointStateCount(); i++) { AnimPose jointPose; rig.getAbsoluteJointPoseInRigFrame(i, jointPose); - const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo; + const HFMJointShapeInfo& shapeInfo = hfmModel.joints[i].shapeInfo; const AnimPose pose = rigToWorldPose * jointPose; for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) { glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]); @@ -3518,12 +3644,9 @@ glm::vec3 MyAvatar::computeCounterBalance() { glm::vec3 counterBalancedCg = (1.0f / DEFAULT_AVATAR_HIPS_MASS) * counterBalancedForHead; // find the height of the hips - const float UPPER_LEG_FRACTION = 0.3333f; glm::vec3 xzDiff((cgHeadMass.position.x - counterBalancedCg.x), 0.0f, (cgHeadMass.position.z - counterBalancedCg.z)); float headMinusHipXz = glm::length(xzDiff); float headHipDefault = glm::length(tposeHead - tposeHips); - float hipFootDefault = tposeHips.y - tposeRightFoot.y; - float sitSquatThreshold = tposeHips.y - (UPPER_LEG_FRACTION * hipFootDefault); float hipHeight = 0.0f; if (headHipDefault > headMinusHipXz) { hipHeight = sqrtf((headHipDefault * headHipDefault) - (headMinusHipXz * headMinusHipXz)); @@ -3535,10 +3658,6 @@ glm::vec3 MyAvatar::computeCounterBalance() { if (counterBalancedCg.y > (tposeHips.y + 0.05f)) { // if the height is higher than default hips, clamp to default hips counterBalancedCg.y = tposeHips.y + 0.05f; - } else if (counterBalancedCg.y < sitSquatThreshold) { - //do a height reset - setResetMode(true); - _follow.activate(FollowHelper::Vertical); } return counterBalancedCg; } @@ -3779,6 +3898,18 @@ bool MyAvatar::getIsInWalkingState() const { return _isInWalkingState; } +bool MyAvatar::getIsInSittingState() const { + return _isInSittingState.get(); +} + +MyAvatar::SitStandModelType MyAvatar::getUserRecenterModel() const { + return _userRecenterModel.get(); +} + +bool MyAvatar::getIsSitStandStateLocked() const { + return _lockSitStandState.get(); +} + float MyAvatar::getWalkSpeed() const { return _walkSpeed.get() * _walkSpeedScalar; } @@ -3799,6 +3930,61 @@ void MyAvatar::setIsInWalkingState(bool isWalking) { _isInWalkingState = isWalking; } +void MyAvatar::setIsInSittingState(bool isSitting) { + _sitStandStateTimer = 0.0f; + _squatTimer = 0.0f; + // on reset height we need the count to be more than one in case the user sits and stands up quickly. + _isInSittingState.set(isSitting); + setResetMode(true); + if (isSitting) { + setCenterOfGravityModelEnabled(false); + } else { + setCenterOfGravityModelEnabled(true); + } + setSitStandStateChange(true); +} + +void MyAvatar::setUserRecenterModel(MyAvatar::SitStandModelType modelName) { + + _userRecenterModel.set(modelName); + + switch (modelName) { + case MyAvatar::SitStandModelType::ForceSit: + setHMDLeanRecenterEnabled(true); + setIsInSittingState(true); + setIsSitStandStateLocked(true); + break; + case MyAvatar::SitStandModelType::ForceStand: + setHMDLeanRecenterEnabled(true); + setIsInSittingState(false); + setIsSitStandStateLocked(true); + break; + case MyAvatar::SitStandModelType::Auto: + default: + setHMDLeanRecenterEnabled(true); + setIsInSittingState(false); + setIsSitStandStateLocked(false); + break; + case MyAvatar::SitStandModelType::DisableHMDLean: + setHMDLeanRecenterEnabled(false); + setIsInSittingState(false); + setIsSitStandStateLocked(false); + break; + } +} + +void MyAvatar::setIsSitStandStateLocked(bool isLocked) { + _lockSitStandState.set(isLocked); + _sitStandStateTimer = 0.0f; + _squatTimer = 0.0f; + _averageUserHeightSensorSpace = _userHeight.get(); + _tippingPoint = _userHeight.get(); + if (!isLocked) { + // always start the auto transition mode in standing state. + setIsInSittingState(false); + } +} + void MyAvatar::setWalkSpeed(float value) { _walkSpeed.set(value); } @@ -3815,8 +4001,16 @@ float MyAvatar::getSprintSpeed() const { return _sprintSpeed.get(); } +void MyAvatar::setSitStandStateChange(bool stateChanged) { + _sitStandStateChange = stateChanged; +} + +float MyAvatar::getSitStandStateChange() const { + return _sitStandStateChange; +} + QVector MyAvatar::getScriptUrls() { - QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector(); + QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getHFMModel().scripts : QVector(); return scripts; } @@ -3958,6 +4152,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, // x axis of currentBodyMatrix in world space. glm::vec3 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0])); glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); + controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD); float forwardLeanAmount = glm::dot(forward, offset); float lateralLeanAmount = glm::dot(right, offset); @@ -3966,14 +4161,19 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const float MAX_FORWARD_LEAN = 0.15f; const float MAX_BACKWARD_LEAN = 0.1f; - - if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) { - return true; + bool stepDetected = false; + if (myAvatar.getIsInSittingState()) { + if (!withinBaseOfSupport(currentHeadPose)) { + stepDetected = true; + } + } else if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) { + stepDetected = true; } else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) { - return true; + stepDetected = true; + } else { + stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; } - - return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; + return stepDetected; } bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) const { @@ -3982,6 +4182,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons controller::Pose currentHeadPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::HEAD); controller::Pose currentLeftHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); controller::Pose currentRightHandPose = myAvatar.getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); + controller::Pose currentHeadSensorPose = myAvatar.getControllerPoseInSensorFrame(controller::Action::HEAD); bool stepDetected = false; float myScale = myAvatar.getAvatarScale(); @@ -3991,7 +4192,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons } else { if (!withinBaseOfSupport(currentHeadPose) && headAngularVelocityBelowThreshold(currentHeadPose) && - isWithinThresholdHeightMode(currentHeadPose, myAvatar.getCurrentStandingHeight(), myScale) && + isWithinThresholdHeightMode(currentHeadSensorPose, myAvatar.getCurrentStandingHeight(), myScale) && handDirectionMatchesHeadDirection(currentLeftHandPose, currentRightHandPose, currentHeadPose) && handAngularVelocityBelowThreshold(currentLeftHandPose, currentRightHandPose) && headVelocityGreaterThanThreshold(currentHeadPose) && @@ -4007,6 +4208,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); if (!isActive(Horizontal) && + (!isActive(Vertical)) && (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) { myAvatar.setResetMode(true); stepDetected = true; @@ -4022,10 +4224,32 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { const float CYLINDER_TOP = 0.1f; const float CYLINDER_BOTTOM = -1.5f; + const float SITTING_BOTTOM = -0.02f; glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); + bool returnValue = false; - return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); + if (myAvatar.getSitStandStateChange()) { + returnValue = true; + } else { + if (myAvatar.getIsInSittingState()) { + if (myAvatar.getIsSitStandStateLocked()) { + returnValue = (offset.y > CYLINDER_TOP); + } + if (offset.y < SITTING_BOTTOM) { + // we recenter more easily when in sitting state. + returnValue = true; + } + } else { + // in the standing state + returnValue = (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); + // finally check for squats in standing + if (_squatDetected) { + returnValue = true; + } + } + } + return returnValue; } void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, @@ -4046,9 +4270,10 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } } } else { + // center of gravity model is not enabled if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); - if (myAvatar.getEnableStepResetRotation()) { + if (myAvatar.getEnableStepResetRotation() && !myAvatar.getIsInSittingState()) { activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } @@ -4056,6 +4281,9 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Vertical); + if (_squatDetected) { + _squatDetected = false; + } } } else { if (!isActive(Rotation) && getForceActivateRotation()) { @@ -4105,7 +4333,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining()); } -glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) { +glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) { if (isActive()) { float dt = myAvatar.getCharacterController()->getFollowTime(); decrementTimeRemaining(dt); @@ -4122,6 +4350,11 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix), sensorLinearDisplacement + extractTranslation(currentBodyMatrix)); + if (myAvatar.getSitStandStateChange()) { + myAvatar.setSitStandStateChange(false); + deactivate(Vertical); + setTranslation(newBodyMat, extractTranslation(myAvatar.deriveBodyFromHMDSensor())); + } return newBodyMat; } else { return currentBodyMatrix; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 16b765711a..799427530a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -142,6 +141,8 @@ class MyAvatar : public Avatar { * @property {number} walkSpeed * @property {number} walkBackwardSpeed * @property {number} sprintSpeed + * @property {number} isInSittingState + * @property {number} userRecenterModel * * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the * registration point of the 3D model. @@ -242,6 +243,9 @@ class MyAvatar : public Avatar { Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed); Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed); Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed); + Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState); + Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel); + Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked); const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -262,6 +266,15 @@ public: }; Q_ENUM(DriveKeys) + enum SitStandModelType { + ForceSit = 0, + ForceStand, + Auto, + DisableHMDLean, + NumSitStandTypes + }; + Q_ENUM(SitStandModelType) + explicit MyAvatar(QThread* thread); virtual ~MyAvatar(); @@ -629,8 +642,6 @@ public: const MyHead* getMyHead() const; - Q_INVOKABLE void toggleSmoothPoleVectors() { _skeletonModel->getRig().toggleSmoothPoleVectors(); }; - /**jsdoc * Get the current position of the avatar's "Head" joint. * @function MyAvatar.getHeadPosition @@ -951,50 +962,72 @@ public: void removeWearableAvatarEntities(); /**jsdoc + * Check whether your avatar is flying or not. * @function MyAvatar.isFlying - * @returns {boolean} + * @returns {boolean} true if your avatar is flying and not taking off or falling, otherwise + * false. */ Q_INVOKABLE bool isFlying(); /**jsdoc + * Check whether your avatar is in the air or not. * @function MyAvatar.isInAir - * @returns {boolean} + * @returns {boolean} true if your avatar is taking off, flying, or falling, otherwise false + * because your avatar is on the ground. */ Q_INVOKABLE bool isInAir(); /**jsdoc + * Set your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends + * on whether the domain you're in allows you to fly. * @function MyAvatar.setFlyingEnabled - * @param {boolean} enabled + * @param {boolean} enabled - Set true if you want to enable flying in your current desktop or HMD display + * mode, otherwise set false. */ Q_INVOKABLE void setFlyingEnabled(bool enabled); /**jsdoc + * Get your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends + * on whether the domain you're in allows you to fly. * @function MyAvatar.getFlyingEnabled - * @returns {boolean} + * @returns {boolean} true if your preference is to enable flying in your current desktop or HMD display mode, + * otherwise false. */ Q_INVOKABLE bool getFlyingEnabled(); /**jsdoc + * Set your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. * @function MyAvatar.setFlyingDesktopPref - * @param {boolean} enabled + * @param {boolean} enabled - Set true if you want to enable flying in desktop display mode, otherwise set + * false. */ Q_INVOKABLE void setFlyingDesktopPref(bool enabled); /**jsdoc + * Get your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. * @function MyAvatar.getFlyingDesktopPref - * @returns {boolean} + * @returns {boolean} true if your preference is to enable flying in desktop display mode, otherwise + * false. */ Q_INVOKABLE bool getFlyingDesktopPref(); /**jsdoc - * @function MyAvatar.setFlyingDesktopPref - * @param {boolean} enabled + * Set your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. + * @function MyAvatar.setFlyingHMDPref + * @param {boolean} enabled - Set true if you want to enable flying in HMD display mode, otherwise set + * false. */ Q_INVOKABLE void setFlyingHMDPref(bool enabled); /**jsdoc - * @function MyAvatar.getFlyingDesktopPref - * @returns {boolean} + * Get your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. + * @function MyAvatar.getFlyingHMDPref + * @returns {boolean} true if your preference is to enable flying in HMD display mode, otherwise + * false. */ Q_INVOKABLE bool getFlyingHMDPref(); @@ -1101,12 +1134,21 @@ public: void setIsInWalkingState(bool isWalking); bool getIsInWalkingState() const; + void setIsInSittingState(bool isSitting); + bool getIsInSittingState() const; + void setUserRecenterModel(MyAvatar::SitStandModelType modelName); + MyAvatar::SitStandModelType getUserRecenterModel() const; + void setIsSitStandStateLocked(bool isLocked); + bool getIsSitStandStateLocked() const; void setWalkSpeed(float value); float getWalkSpeed() const; void setWalkBackwardSpeed(float value); float getWalkBackwardSpeed() const; void setSprintSpeed(float value); float getSprintSpeed() const; + void setSitStandStateChange(bool stateChanged); + float getSitStandStateChange() const; + void updateSitStandState(float newHeightReading, float dt); QVector getScriptUrls(); @@ -1512,6 +1554,7 @@ signals: */ void disableHandTouchForIDChanged(const QUuid& entityID, bool disable); + private slots: void leaveDomain(); void updateCollisionCapsuleCache(); @@ -1722,7 +1765,7 @@ private: bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; bool shouldActivateHorizontalCG(MyAvatar& myAvatar) const; void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput); - glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix); + glm::mat4 postPhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix); bool getForceActivateRotation() const; void setForceActivateRotation(bool val); bool getForceActivateVertical() const; @@ -1731,6 +1774,7 @@ private: void setForceActivateHorizontal(bool val); bool getToggleHipsFollowing() const; void setToggleHipsFollowing(bool followHead); + bool _squatDetected { false }; std::atomic _forceActivateRotation { false }; std::atomic _forceActivateVertical { false }; std::atomic _forceActivateHorizontal { false }; @@ -1766,8 +1810,8 @@ private: glm::vec3 _customListenPosition; glm::quat _customListenOrientation; - AtRestDetector _hmdAtRestDetector; - bool _lastIsMoving { false }; + AtRestDetector _leftHandAtRestDetector; + AtRestDetector _rightHandAtRestDetector; // all poses are in sensor-frame std::map _controllerPoseMap; @@ -1800,10 +1844,13 @@ private: std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; + void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize); + // height of user in sensor space, when standing erect. ThreadSafeValueCache _userHeight { DEFAULT_AVATAR_HEIGHT }; - - void updateChildCauterization(SpatiallyNestablePointer object, bool cauterize); + float _averageUserHeightSensorSpace { _userHeight.get() }; + bool _sitStandStateChange { false }; + ThreadSafeValueCache _lockSitStandState { false }; // max unscaled forward movement speed ThreadSafeValueCache _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; @@ -1811,6 +1858,11 @@ private: ThreadSafeValueCache _sprintSpeed { AVATAR_SPRINT_SPEED_SCALAR }; float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; bool _isInWalkingState { false }; + ThreadSafeValueCache _isInSittingState { false }; + ThreadSafeValueCache _userRecenterModel { MyAvatar::SitStandModelType::Auto }; + float _sitStandStateTimer { 0.0f }; + float _squatTimer { 0.0f }; + float _tippingPoint { _userHeight.get() }; // load avatar scripts once when rig is ready bool _shouldLoadScripts { false }; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 3084542472..2a21f78b21 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -36,6 +36,7 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); + // check for pinned hips. auto hipsIndex = myAvatar->getJointIndex("Hips"); if (myAvatar->isJointPinned(hipsIndex)) { @@ -46,7 +47,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { } glm::mat4 hipsMat; - if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState())) { + if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && !(myAvatar->getIsInSittingState()) && myAvatar->getHMDLeanRecenterEnabled()) { // then we use center of gravity model hipsMat = myAvatar->deriveBodyUsingCgModel(); } else { @@ -89,7 +90,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -199,49 +200,38 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) { bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS); - if (!_prevHipsValid) { - AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying); - _prevHips = hips; - } - - AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying); - // timescale in seconds const float TRANS_HORIZ_TIMESCALE = 0.15f; const float TRANS_VERT_TIMESCALE = 0.01f; // We want the vertical component of the hips to follow quickly to prevent spine squash/stretch. const float ROT_TIMESCALE = 0.15f; const float FLY_IDLE_TRANSITION_TIMESCALE = 0.25f; - float transHorizAlpha, transVertAlpha, rotAlpha; if (_flyIdleTimer < 0.0f) { - transHorizAlpha = glm::min(deltaTime / TRANS_HORIZ_TIMESCALE, 1.0f); - transVertAlpha = glm::min(deltaTime / TRANS_VERT_TIMESCALE, 1.0f); - rotAlpha = glm::min(deltaTime / ROT_TIMESCALE, 1.0f); + _smoothHipsHelper.setHorizontalTranslationTimescale(TRANS_HORIZ_TIMESCALE); + _smoothHipsHelper.setVerticalTranslationTimescale(TRANS_VERT_TIMESCALE); + _smoothHipsHelper.setRotationTimescale(ROT_TIMESCALE); } else { - transHorizAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f); - transVertAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f); - rotAlpha = glm::min(deltaTime / FLY_IDLE_TRANSITION_TIMESCALE, 1.0f); + _smoothHipsHelper.setHorizontalTranslationTimescale(FLY_IDLE_TRANSITION_TIMESCALE); + _smoothHipsHelper.setVerticalTranslationTimescale(FLY_IDLE_TRANSITION_TIMESCALE); + _smoothHipsHelper.setRotationTimescale(FLY_IDLE_TRANSITION_TIMESCALE); } - // smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation. - float hipsY = hips.trans().y; - hips.trans() = lerp(_prevHips.trans(), hips.trans(), transHorizAlpha); - hips.trans().y = lerp(_prevHips.trans().y, hipsY, transVertAlpha); - hips.rot() = safeLerp(_prevHips.rot(), hips.rot(), rotAlpha); - - _prevHips = hips; - _prevHipsValid = true; + AnimPose sensorHips = computeHipsInSensorFrame(myAvatar, isFlying); + if (!_prevIsEstimatingHips) { + _smoothHipsHelper.teleport(sensorHips); + } + sensorHips = _smoothHipsHelper.update(sensorHips, deltaTime); glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180); AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; + params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * sensorHips; params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && - myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && - !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { + myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && + !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { AnimPose currentSpine2Pose; AnimPose currentHeadPose; @@ -250,6 +240,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); if (spine2Exists && headExists && hipsExists) { + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); glm::vec3 u, v, w; glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); @@ -267,8 +258,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } } + _prevIsEstimatingHips = true; } else { - _prevHipsValid = false; + _prevIsEstimatingHips = false; } params.isTalking = head->getTimeWithoutTalking() <= 1.5f; @@ -276,19 +268,19 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // pass detailed torso k-dops to rig. int hipsJoint = _rig.indexOfJoint("Hips"); if (hipsJoint >= 0) { - params.hipsShapeInfo = geometry.joints[hipsJoint].shapeInfo; + params.hipsShapeInfo = hfmModel.joints[hipsJoint].shapeInfo; } int spineJoint = _rig.indexOfJoint("Spine"); if (spineJoint >= 0) { - params.spineShapeInfo = geometry.joints[spineJoint].shapeInfo; + params.spineShapeInfo = hfmModel.joints[spineJoint].shapeInfo; } int spine1Joint = _rig.indexOfJoint("Spine1"); if (spine1Joint >= 0) { - params.spine1ShapeInfo = geometry.joints[spine1Joint].shapeInfo; + params.spine1ShapeInfo = hfmModel.joints[spine1Joint].shapeInfo; } int spine2Joint = _rig.indexOfJoint("Spine2"); if (spine2Joint >= 0) { - params.spine2ShapeInfo = geometry.joints[spine2Joint].shapeInfo; + params.spine2ShapeInfo = hfmModel.joints[spine2Joint].shapeInfo; } _rig.updateFromControllerParameters(params, deltaTime); @@ -298,7 +290,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto velocity = myAvatar->getLocalVelocity() / myAvatar->getSensorToWorldScale(); auto position = myAvatar->getLocalPosition(); auto orientation = myAvatar->getLocalOrientation(); - _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); + _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState, myAvatar->getSensorToWorldScale()); // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); @@ -308,8 +300,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.eyeSaccade = head->getSaccade(); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index ebef9796a4..9a3559ddf7 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -10,6 +10,7 @@ #define hifi_MySkeletonModel_h #include +#include #include "MyAvatar.h" /// A skeleton loaded from a model. @@ -26,11 +27,12 @@ public: private: void updateFingers(); - AnimPose _prevHips; // sensor frame - bool _prevHipsValid { false }; + CriticallyDampedSpringPoseHelper _smoothHipsHelper; // sensor frame bool _prevIsFlying { false }; float _flyIdleTimer { 0.0f }; + float _prevIsEstimatingHips { false }; + std::map _jointRotationFrameOffsetMap; }; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 625998eb95..c2687fd525 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -11,12 +11,12 @@ #include "AvatarMotionState.h" -static xColor getLoadingOrbColor(Avatar::LoadingStatus loadingStatus) { +static glm::u8vec3 getLoadingOrbColor(Avatar::LoadingStatus loadingStatus) { - const xColor NO_MODEL_COLOR(0xe3, 0xe3, 0xe3); - const xColor LOAD_MODEL_COLOR(0xef, 0x93, 0xd1); - const xColor LOAD_SUCCESS_COLOR(0x1f, 0xc6, 0xa6); - const xColor LOAD_FAILURE_COLOR(0xc6, 0x21, 0x47); + const glm::u8vec3 NO_MODEL_COLOR(0xe3, 0xe3, 0xe3); + const glm::u8vec3 LOAD_MODEL_COLOR(0xef, 0x93, 0xd1); + const glm::u8vec3 LOAD_SUCCESS_COLOR(0x1f, 0xc6, 0xa6); + const glm::u8vec3 LOAD_FAILURE_COLOR(0xc6, 0x21, 0x47); switch (loadingStatus) { case Avatar::LoadingStatus::NoModel: return NO_MODEL_COLOR; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 67303f2a9b..3512677650 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -219,7 +219,11 @@ QString transactionString(const QJsonObject& valueObject) { if (!message.isEmpty()) { result += QString("
with memo: \"%1\"").arg(message); } - } else if (sentMoney <= 0 && receivedMoney <= 0 && (sentCerts > 0 || receivedCerts > 0) && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) { + } else if (sentMoney <= 0 && receivedMoney <= 0 && + (sentCerts > 0 || receivedCerts > 0) && + !KNOWN_USERS.contains(valueObject["sender_name"].toString()) && + !KNOWN_USERS.contains(valueObject["recipient_name"].toString()) + ) { // this is a non-HFC asset transfer. if (sentCerts > 0) { QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); @@ -240,7 +244,6 @@ QString transactionString(const QJsonObject& valueObject) { return result; } -static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void Ledger::historySuccess(QNetworkReply* reply) { // here we send a historyResult with some extra stuff in it // Namely, the styled text we'd like to show. The issue is the @@ -451,7 +454,7 @@ void Ledger::alreadyOwned(const QString& marketplaceId) { } } -void Ledger::getAvailableUpdates(const QString& itemId) { +void Ledger::getAvailableUpdates(const QString& itemId, const int& pageNumber, const int& itemsPerPage) { auto wallet = DependencyManager::get(); QString endpoint = "available_updates"; QJsonObject request; @@ -459,6 +462,8 @@ void Ledger::getAvailableUpdates(const QString& itemId) { if (!itemId.isEmpty()) { request["marketplace_item_id"] = itemId; } + request["per_page"] = itemsPerPage; + request["page"] = pageNumber; send(endpoint, "availableUpdatesSuccess", "availableUpdatesFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); } diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 427395ee11..715d6337ad 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -37,7 +37,7 @@ public: void transferAssetToNode(const QString& hfc_key, const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage); void transferAssetToUsername(const QString& hfc_key, const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage); void alreadyOwned(const QString& marketplaceId); - void getAvailableUpdates(const QString& itemId = ""); + void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10); void updateItem(const QString& hfc_key, const QString& certificate_id); enum CertificateStatus { diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index aa39fdc1b9..0ef26a62b3 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -315,7 +315,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) { return installedAppsFromMarketplace; } -bool QmlCommerce::installApp(const QString& itemHref) { +bool QmlCommerce::installApp(const QString& itemHref, const bool& alsoOpenImmediately) { if (!QDir(_appsPath).exists()) { if (!QDir().mkdir(_appsPath)) { qCDebug(commerce) << "Couldn't make _appsPath directory."; @@ -325,7 +325,8 @@ bool QmlCommerce::installApp(const QString& itemHref) { QUrl appHref(itemHref); - auto request = DependencyManager::get()->createResourceRequest(this, appHref); + auto request = + DependencyManager::get()->createResourceRequest(this, appHref, true, -1, "QmlCommerce::installApp"); if (!request) { qCDebug(commerce) << "Couldn't create resource request for app."; @@ -357,13 +358,22 @@ bool QmlCommerce::installApp(const QString& itemHref) { QJsonObject appFileJsonObject = appFileJsonDocument.object(); QString scriptUrl = appFileJsonObject["scriptURL"].toString(); - if ((DependencyManager::get()->loadScript(scriptUrl.trimmed())).isNull()) { - qCDebug(commerce) << "Couldn't load script."; - return false; + // Don't try to re-load (install) a script if it's already running + QStringList runningScripts = DependencyManager::get()->getRunningScripts(); + if (!runningScripts.contains(scriptUrl)) { + if ((DependencyManager::get()->loadScript(scriptUrl.trimmed())).isNull()) { + qCDebug(commerce) << "Couldn't load script."; + return false; + } + + QFileInfo appFileInfo(appFile); + emit appInstalled(appFileInfo.baseName()); + } + + if (alsoOpenImmediately) { + QmlCommerce::openApp(itemHref); } - QFileInfo appFileInfo(appFile); - emit appInstalled(appFileInfo.baseName()); return true; }); request->send(); @@ -431,9 +441,9 @@ bool QmlCommerce::openApp(const QString& itemHref) { return true; } -void QmlCommerce::getAvailableUpdates(const QString& itemId) { +void QmlCommerce::getAvailableUpdates(const QString& itemId, const int& pageNumber, const int& itemsPerPage) { auto ledger = DependencyManager::get(); - ledger->getAvailableUpdates(itemId); + ledger->getAvailableUpdates(itemId, pageNumber, itemsPerPage); } void QmlCommerce::updateItem(const QString& certificateId) { diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index bee30e1b62..c5fbdaf4a4 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -88,11 +88,11 @@ protected: Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID); Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = ""); - Q_INVOKABLE bool installApp(const QString& appHref); + Q_INVOKABLE bool installApp(const QString& appHref, const bool& alsoOpenImmediately = false); Q_INVOKABLE bool uninstallApp(const QString& appHref); Q_INVOKABLE bool openApp(const QString& appHref); - Q_INVOKABLE void getAvailableUpdates(const QString& itemId = ""); + Q_INVOKABLE void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10); Q_INVOKABLE void updateItem(const QString& certificateId); private: diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 5b8417be7c..0e9ad7d79a 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -687,7 +687,7 @@ void Wallet::chooseSecurityImage(const QString& filename) { delete _securityImage; } QString path = PathUtils::resourcesPath(); - path.append("/qml/hifi/commerce/wallet/"); + path.append("/qml/hifi/dialogs/security/"); path.append(filename); // now create a new security image pixmap diff --git a/interface/src/main.cpp b/interface/src/main.cpp index d9396ae4d1..5af0a9371d 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "AddressManager.h" #include "Application.h" @@ -40,6 +41,18 @@ extern "C" { #endif int main(int argc, const char* argv[]) { +#ifdef Q_OS_MAC + auto format = getDefaultOpenGLSurfaceFormat(); + // Deal with some weirdness in the chromium context sharing on Mac. + // The primary share context needs to be 3.2, so that the Chromium will + // succeed in it's creation of it's command stub contexts. + format.setVersion(3, 2); + // This appears to resolve the issues with corrupted fonts on OSX. No + // idea why. + qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "true"); + // https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg + QSurfaceFormat::setDefaultFormat(format); +#endif setupHifiApplication(BuildInfo::INTERFACE_NAME); QStringList arguments; diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 0d991fc9bc..5c8868abdb 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -91,7 +91,9 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag return; // bail since piggyback version doesn't match } - qApp->trackIncomingOctreePacket(*message, sendingNode, wasStatsPacket); + if (packetType != PacketType::EntityQueryInitialResultsComplete) { + qApp->trackIncomingOctreePacket(*message, sendingNode, wasStatsPacket); + } // seek back to beginning of packet after tracking message->seek(0); diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 25927c5b68..2b75946e28 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -131,7 +131,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // 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 FBXGeometry& collisionGeometry = resource->getFBXGeometry(); + const HFMModel& collisionModel = resource->getHFMModel(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -139,15 +139,15 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // 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 FBXMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionModel.meshes) { // each meshPart is a convex hull - foreach (const FBXMeshPart &meshPart, mesh.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 uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // 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 FBXReader @@ -168,7 +168,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // 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 FBXMesh higher up + // 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 FBXReader @@ -206,7 +206,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / resource->getFBXGeometry().getUnscaledMeshExtents().size(); + 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++) { @@ -216,11 +216,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } shapeInfo.setParams(type, dimensions, resource->getURL().toString()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - const FBXGeometry& fbxGeometry = resource->getFBXGeometry(); - int numFbxMeshes = fbxGeometry.meshes.size(); + const HFMModel& hfmModel = resource->getHFMModel(); + int numHFMMeshes = hfmModel.meshes.size(); int totalNumVertices = 0; - for (int i = 0; i < numFbxMeshes; i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); + 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; @@ -230,7 +230,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha return; } - auto& meshes = resource->getFBXGeometry().meshes; + auto& meshes = resource->getHFMModel().meshes; int32_t numMeshes = (int32_t)(meshes.size()); const int MAX_ALLOWED_MESH_COUNT = 1000; @@ -285,12 +285,12 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha if (type == SHAPE_TYPE_STATIC_MESH) { // copy into triangleIndices size_t triangleIndicesCount = 0; - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { triangleIndicesCount += meshPart.triangleIndices.count(); } triangleIndices.reserve((int)triangleIndicesCount); - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { const int* indexItr = meshPart.triangleIndices.cbegin(); while (indexItr != meshPart.triangleIndices.cend()) { triangleIndices.push_back(*indexItr); @@ -299,11 +299,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { // for each mesh copy unique part indices, separated by special bogus (flag) index values - for (const FBXMeshPart& meshPart : mesh.parts) { + 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 FBXMesh higher up + // 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 FBXReader diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 67e39e4d72..92aa415f9e 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -76,4 +76,4 @@ protected: bool _includeNormals; }; -#endif // hifi_CollisionPick_h \ No newline at end of file +#endif // hifi_CollisionPick_h diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index dde0daf0df..e45b11b479 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -52,8 +52,7 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria if (!pathMap.isEmpty()) { enabled = true; if (pathMap["color"].isValid()) { - bool valid; - color = toGlm(xColorFromVariant(pathMap["color"], valid)); + color = toGlm(u8vec3FromVariant(pathMap["color"])); } if (pathMap["alpha"].isValid()) { alpha = pathMap["alpha"].toFloat(); @@ -250,8 +249,7 @@ std::shared_ptr ParabolaPointer::buildRenderState(const QVa enabled = true; QVariantMap pathMap = propMap["path"].toMap(); if (pathMap["color"].isValid()) { - bool valid; - color = toGlm(xColorFromVariant(pathMap["color"], valid)); + color = toGlm(u8vec3FromVariant(pathMap["color"])); } if (pathMap["alpha"].isValid()) { diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h index 385753a8e8..1aa4165c87 100644 --- a/interface/src/raypick/PathPointer.h +++ b/interface/src/raypick/PathPointer.h @@ -27,7 +27,7 @@ class StartEndRenderState { public: StartEndRenderState() {} StartEndRenderState(const OverlayID& startID, const OverlayID& endID); - virtual ~StartEndRenderState() {} + virtual ~StartEndRenderState() = default; const OverlayID& getStartID() const { return _startID; } const OverlayID& getEndID() const { return _endID; } diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 26b5aacac5..6e979d2d91 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -31,6 +31,9 @@ #include +static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand +static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, StylusPick::WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); + unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { switch (type) { case PickQuery::PickType::Ray: @@ -137,7 +140,12 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties maxDistance = propMap["maxDistance"].toFloat(); } - return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled)); + glm::vec3 tipOffset = TIP_OFFSET; + if (propMap["tipOffset"].isValid()) { + tipOffset = vec3FromVariant(propMap["tipOffset"]); + } + + return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled, tipOffset)); } // NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API. @@ -416,4 +424,4 @@ void PickScriptingInterface::setParentTransform(std::shared_ptr pick, pick->parentTransform = std::make_shared(pickID); } } -} \ No newline at end of file +} diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index a44d14b4a6..0009536479 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -16,6 +16,11 @@ #include "LaserPointer.h" #include "StylusPointer.h" #include "ParabolaPointer.h" +#include "StylusPick.h" + +static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; +static const glm::vec3 DEFAULT_POSITION_OFFSET{0.0f, 0.0f, -StylusPick::WEB_STYLUS_LENGTH / 2.0f}; +static const glm::vec3 DEFAULT_MODEL_DIMENSIONS{0.01f, 0.01f, StylusPick::WEB_STYLUS_LENGTH}; void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); @@ -50,7 +55,17 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& * @typedef {object} Pointers.StylusPointerProperties * @property {boolean} [hover=false] If this pointer should generate hover events. * @property {boolean} [enabled=false] + * @property {Vec3} [tipOffset] The specified offset of the from the joint index. + * @property {Pointers.StylusPointerProperties.model} [model] Data to replace the default model url, positionOffset and rotationOffset. */ + /**jsdoc + * properties defining stylus pick model that can be included to {@link Pointers.StylusPointerProperties} + * @typedef {object} Pointers.StylusPointerProperties.model + * @property {string} [url] url to the model + * @property {Vec3} [dimensions] the dimensions of the model + * @property {Vec3} [positionOffset] the position offset of the model from the stylus tip. + * @property {Vec3} [rotationOffset] the rotation offset of the model from the parent joint index + */ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); @@ -64,7 +79,28 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) enabled = propertyMap["enabled"].toBool(); } - return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled)); + glm::vec3 modelPositionOffset = DEFAULT_POSITION_OFFSET; + glm::quat modelRotationOffset = X_ROT_NEG_90; + glm::vec3 modelDimensions = DEFAULT_MODEL_DIMENSIONS; + + if (propertyMap["model"].isValid()) { + QVariantMap modelData = propertyMap["model"].toMap(); + + if (modelData["positionOffset"].isValid()) { + modelPositionOffset = vec3FromVariant(modelData["positionOffset"]); + } + + if (modelData["rotationOffset"].isValid()) { + modelRotationOffset = quatFromVariant(modelData["rotationOffset"]); + } + + if (modelData["dimensions"].isValid()) { + modelDimensions = vec3FromVariant(modelData["dimensions"]); + } + } + + return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled, modelPositionOffset, + modelRotationOffset, modelDimensions)); } /**jsdoc diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index ad12db4df2..a48d858504 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -118,4 +118,4 @@ glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm:: glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { auto props = DependencyManager::get()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); -} \ No newline at end of file +} diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index c495ddd194..0a76180be8 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -21,11 +21,7 @@ #include using namespace bilateral; - -// TODO: make these configurable per pick -static const float WEB_STYLUS_LENGTH = 0.2f; -static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand -static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); +float StylusPick::WEB_STYLUS_LENGTH = 0.2f; struct SideData { QString avatarJoint; @@ -64,8 +60,8 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) { return distance < maxDistance; } -StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled) : - Pick(StylusTip(side), filter, maxDistance, enabled) +StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset) : + Pick(StylusTip(side, tipOffset), filter, maxDistance, enabled) { } @@ -90,7 +86,7 @@ static StylusTip getFingerWorldLocation(Side side) { } // controllerWorldLocation is where the controller would be, in-world, with an added offset -static StylusTip getControllerWorldLocation(Side side) { +static StylusTip getControllerWorldLocation(Side side, const glm::vec3& tipOffset) { static const std::array INPUTS{ { UserInputMapper::makeStandardInput(SIDES[0].channel), UserInputMapper::makeStandardInput(SIDES[1].channel) } }; const auto sideIndex = index(side); @@ -114,7 +110,7 @@ static StylusTip getControllerWorldLocation(Side side) { // add to the real position so the grab-point is out in front of the hand, a bit result.position += result.orientation * (sideData.grabPointSphereOffset * sensorScaleFactor); // move the stylus forward a bit - result.position += result.orientation * (TIP_OFFSET * sensorScaleFactor); + result.position += result.orientation * (tipOffset * sensorScaleFactor); auto worldControllerPos = avatarPosition + avatarOrientation * pose.translation; // compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions. @@ -131,7 +127,7 @@ StylusTip StylusPick::getMathematicalPick() const { if (qApp->getPreferAvatarFingerOverStylus()) { result = getFingerWorldLocation(_mathPick.side); } else { - result = getControllerWorldLocation(_mathPick.side); + result = getControllerWorldLocation(_mathPick.side, _mathPick.tipOffset); } return result; } @@ -236,4 +232,4 @@ Transform StylusPick::getResultTransform() const { Transform transform; transform.setTranslation(stylusResult->intersection); return transform; -} \ No newline at end of file +} diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index cd01df20e9..14821c0ce5 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -58,7 +58,7 @@ public: class StylusPick : public Pick { using Side = bilateral::Side; public: - StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled); + StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset); StylusTip getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override; @@ -71,6 +71,8 @@ public: bool isLeftHand() const override { return _mathPick.side == Side::Left; } bool isRightHand() const override { return _mathPick.side == Side::Right; } bool isMouse() const override { return false; } + + static float WEB_STYLUS_LENGTH; }; -#endif // hifi_StylusPick_h \ No newline at end of file +#endif // hifi_StylusPick_h diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 4ba3813c4a..5595c54b71 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -17,9 +17,6 @@ #include "PickScriptingInterface.h" #include -// TODO: make these configurable per pointer -static const float WEB_STYLUS_LENGTH = 0.2f; - static const float TABLET_MIN_HOVER_DISTANCE = -0.1f; static const float TABLET_MAX_HOVER_DISTANCE = 0.1f; static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f; @@ -28,9 +25,15 @@ static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f; static const float HOVER_HYSTERESIS = 0.01f; static const float TOUCH_HYSTERESIS = 0.001f; -StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled) : +static const QString DEFAULT_STYLUS_MODEL_URL = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; + +StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions) : Pointer(DependencyManager::get()->createStylusPick(props), enabled, hover), - _stylusOverlay(stylusOverlay) + _stylusOverlay(stylusOverlay), + _modelPositionOffset(modelPositionOffset), + _modelDimensions(modelDimensions), + _modelRotationOffset(modelRotationOffset) { } @@ -42,9 +45,19 @@ StylusPointer::~StylusPointer() { OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { QVariantMap overlayProperties; + + QString modelUrl = DEFAULT_STYLUS_MODEL_URL; + + if (properties["model"].isValid()) { + QVariantMap modelData = properties["model"].toMap(); + + if (modelData["url"].isValid()) { + modelUrl = modelData["url"].toString(); + } + } // TODO: make these configurable per pointer overlayProperties["name"] = "stylus"; - overlayProperties["url"] = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; + overlayProperties["url"] = modelUrl; overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; @@ -72,13 +85,12 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { void StylusPointer::show(const StylusTip& tip) { if (!_stylusOverlay.isNull()) { QVariantMap props; - static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; - auto modelOrientation = tip.orientation * X_ROT_NEG_90; + auto modelOrientation = tip.orientation * _modelRotationOffset; auto sensorToWorldScale = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); - auto modelPositionOffset = modelOrientation * (vec3(0.0f, 0.0f, -WEB_STYLUS_LENGTH / 2.0f) * sensorToWorldScale); + auto modelPositionOffset = modelOrientation * (_modelPositionOffset * sensorToWorldScale); props["position"] = vec3toVariant(tip.position + modelPositionOffset); props["rotation"] = quatToVariant(modelOrientation); - props["dimensions"] = vec3toVariant(sensorToWorldScale * vec3(0.01f, 0.01f, WEB_STYLUS_LENGTH)); + props["dimensions"] = vec3toVariant(sensorToWorldScale * _modelDimensions); props["visible"] = true; qApp->getOverlays().editOverlay(_stylusOverlay, props); } diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index ff60fd78e5..64e2a38bed 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -21,7 +21,8 @@ class StylusPointer : public Pointer { using Ptr = std::shared_ptr; public: - StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled); + StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions); ~StylusPointer(); void updateVisuals(const PickResultPointer& pickResult) override; @@ -81,6 +82,10 @@ private: bool _showing { true }; + glm::vec3 _modelPositionOffset; + glm::vec3 _modelDimensions; + glm::quat _modelRotationOffset; + }; #endif // hifi_StylusPointer_h diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 4b8eb6aabc..4c4bf6dd60 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -50,6 +50,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * Read-only. * @property {object} devices Read-only. Deprecated: This property is deprecated and will be * removed. + * @property {boolean} isSoloing Read-only. true if any nodes are soloed. + * @property {Uuid[]} soloList Read-only. Get the list of currently soloed node UUIDs. */ Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index c2d2b69883..c14f4ea895 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -46,11 +46,17 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float return retVal; } -bool ClipboardScriptingInterface::importEntities(const QString& filename) { +bool ClipboardScriptingInterface::importEntities( + const QString& filename, + const bool isObservable, + const qint64 callerId +) { bool retVal; BLOCKING_INVOKE_METHOD(qApp, "importEntities", Q_RETURN_ARG(bool, retVal), - Q_ARG(const QString&, filename)); + Q_ARG(const QString&, filename), + Q_ARG(const bool, isObservable), + Q_ARG(const qint64, callerId)); return retVal; } diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 32b8c64a7d..60b6ca2e03 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -50,9 +50,11 @@ public: * You can generate a JSON file using {@link Clipboard.exportEntities}. * @function Clipboard.importEntities * @param {string} filename Path and name of file to import. + * @param {boolean} does the ResourceRequestObserver observe this request? + * @param {number} optional internal id of object causing this import. * @returns {boolean} true if the import was successful, otherwise false. */ - Q_INVOKABLE bool importEntities(const QString& filename); + Q_INVOKABLE bool importEntities(const QString& filename, const bool isObservable = true, const qint64 callerId = -1); /**jsdoc * Export the entities specified to a JSON file. diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index ea24d6c793..f2f8d3b8d4 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -119,8 +119,11 @@ void HMDScriptingInterface::toggleShouldShowTablet() { } void HMDScriptingInterface::setShouldShowTablet(bool value) { - _showTablet = value; - _tabletContextualMode = false; + if (_showTablet != value) { + _showTablet = value; + _tabletContextualMode = false; + emit showTabletChanged(value); + } } QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 2c0a3fe45f..6cc695762b 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -355,6 +355,8 @@ signals: */ bool shouldShowHandControllersChanged(); + void showTabletChanged(bool showTablet); + public: HMDScriptingInterface(); static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); diff --git a/interface/src/scripting/KeyboardScriptingInterface.cpp b/interface/src/scripting/KeyboardScriptingInterface.cpp new file mode 100644 index 0000000000..b26e1ec378 --- /dev/null +++ b/interface/src/scripting/KeyboardScriptingInterface.cpp @@ -0,0 +1,34 @@ +// +// KeyboardScriptingInterface.cpp +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2017 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 "KeyboardScriptingInterface.h" +#include "ui/Keyboard.h" + +bool KeyboardScriptingInterface::isRaised() { + return DependencyManager::get()->isRaised(); +} + +void KeyboardScriptingInterface::setRaised(bool raised) { + DependencyManager::get()->setRaised(raised); +} + + +bool KeyboardScriptingInterface::isPassword() { + return DependencyManager::get()->isPassword(); +} + +void KeyboardScriptingInterface::setPassword(bool password) { + DependencyManager::get()->setPassword(password); +} + +void KeyboardScriptingInterface::loadKeyboardFile(const QString& keyboardFile) { + DependencyManager::get()->loadKeyboardFile(keyboardFile); +} diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h new file mode 100644 index 0000000000..1ab91ea7c3 --- /dev/null +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -0,0 +1,43 @@ +// +// KeyboardScriptingInterface.h +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2017 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_KeyboardScriptingInterface_h +#define hifi_KeyboardScriptingInterface_h + +#include + +#include "DependencyManager.h" + +/**jsdoc + * The Keyboard API provides facilities to use 3D Physical keyboard. + * @namespace Keyboard + * + * @hifi-interface + * @hifi-client-entity + * + * @property {bool} raised - true If the keyboard is visible false otherwise + * @property {bool} password - true Will show * instead of characters in the text display false otherwise + */ +class KeyboardScriptingInterface : public QObject, public Dependency { + Q_OBJECT + Q_PROPERTY(bool raised READ isRaised WRITE setRaised) + Q_PROPERTY(bool password READ isPassword WRITE setPassword) + +public: + Q_INVOKABLE void loadKeyboardFile(const QString& string); +private: + bool isRaised(); + void setRaised(bool raised); + + bool isPassword(); + void setPassword(bool password); +}; +#endif diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index 9716b7e665..4a8a72b16d 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -421,7 +421,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { auto colorVariant = properties["outlineUnoccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._outlineUnoccluded.color = toGlm(color); } @@ -429,7 +429,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { colorVariant = properties["outlineOccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._outlineOccluded.color = toGlm(color); } @@ -437,7 +437,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { colorVariant = properties["fillUnoccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._fillUnoccluded.color = toGlm(color); } @@ -445,7 +445,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { colorVariant = properties["fillOccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._fillOccluded.color = toGlm(color); } @@ -497,10 +497,11 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { QVariantMap SelectionHighlightStyle::toVariantMap() const { QVariantMap properties; - properties["outlineUnoccludedColor"] = xColorToVariant(xColorFromGlm(_style._outlineUnoccluded.color)); - properties["outlineOccludedColor"] = xColorToVariant(xColorFromGlm(_style._outlineOccluded.color)); - properties["fillUnoccludedColor"] = xColorToVariant(xColorFromGlm(_style._fillUnoccluded.color)); - properties["fillOccludedColor"] = xColorToVariant(xColorFromGlm(_style._fillOccluded.color)); + const float MAX_COLOR = 255.0f; + properties["outlineUnoccludedColor"] = u8vec3ColortoVariant(_style._outlineUnoccluded.color * MAX_COLOR); + properties["outlineOccludedColor"] = u8vec3ColortoVariant(_style._outlineOccluded.color * MAX_COLOR); + properties["fillUnoccludedColor"] = u8vec3ColortoVariant(_style._fillUnoccluded.color * MAX_COLOR); + properties["fillOccludedColor"] = u8vec3ColortoVariant(_style._fillOccluded.color * MAX_COLOR); properties["outlineUnoccludedAlpha"] = _style._outlineUnoccluded.alpha; properties["outlineOccludedAlpha"] = _style._outlineOccluded.alpha; diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp new file mode 100644 index 0000000000..6b1677aecb --- /dev/null +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -0,0 +1,163 @@ +// +// TTSScriptingInterface.cpp +// libraries/audio-client/src/scripting +// +// Created by Zach Fox on 2018-10-10. +// 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 +// + +#include "TTSScriptingInterface.h" +#include "avatar/AvatarManager.h" + +TTSScriptingInterface::TTSScriptingInterface() { +#ifdef WIN32 + // + // Create text to speech engine + // + HRESULT hr = m_tts.CoCreateInstance(CLSID_SpVoice); + if (FAILED(hr)) { + qDebug() << "Text-to-speech engine creation failed."; + } + + // + // Get token corresponding to default voice + // + hr = SpGetDefaultTokenFromCategoryId(SPCAT_VOICES, &m_voiceToken, FALSE); + if (FAILED(hr)) { + qDebug() << "Can't get default voice token."; + } + + // + // Set default voice + // + hr = m_tts->SetVoice(m_voiceToken); + if (FAILED(hr)) { + qDebug() << "Can't set default voice."; + } + + _lastSoundAudioInjectorUpdateTimer.setSingleShot(true); + connect(&_lastSoundAudioInjectorUpdateTimer, &QTimer::timeout, this, &TTSScriptingInterface::updateLastSoundAudioInjector); +#endif +} + +TTSScriptingInterface::~TTSScriptingInterface() { +} + +#ifdef WIN32 +class ReleaseOnExit { +public: + ReleaseOnExit(IUnknown* p) : m_p(p) {} + ~ReleaseOnExit() { + if (m_p) { + m_p->Release(); + } + } + +private: + IUnknown* m_p; +}; +#endif + +const int INJECTOR_INTERVAL_MS = 100; +void TTSScriptingInterface::updateLastSoundAudioInjector() { + if (_lastSoundAudioInjector) { + AudioInjectorOptions options; + options.position = DependencyManager::get()->getMyAvatarPosition(); + _lastSoundAudioInjector->setOptions(options); + _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); + } +} + +void TTSScriptingInterface::speakText(const QString& textToSpeak) { +#ifdef WIN32 + WAVEFORMATEX fmt; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nSamplesPerSec = AudioConstants::SAMPLE_RATE; + fmt.wBitsPerSample = 16; + fmt.nChannels = 1; + fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; + fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; + fmt.cbSize = 0; + + IStream* pStream = NULL; + + ISpStream* pSpStream = nullptr; + HRESULT hr = CoCreateInstance(CLSID_SpStream, nullptr, CLSCTX_ALL, __uuidof(ISpStream), (void**)&pSpStream); + if (FAILED(hr)) { + qDebug() << "CoCreateInstance failed."; + } + ReleaseOnExit rSpStream(pSpStream); + + pStream = SHCreateMemStream(NULL, 0); + if (nullptr == pStream) { + qDebug() << "SHCreateMemStream failed."; + } + + hr = pSpStream->SetBaseStream(pStream, SPDFID_WaveFormatEx, &fmt); + if (FAILED(hr)) { + qDebug() << "Can't set base stream."; + } + + hr = m_tts->SetOutput(pSpStream, true); + if (FAILED(hr)) { + qDebug() << "Can't set output stream."; + } + + ReleaseOnExit rStream(pStream); + + ULONG streamNumber; + hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), SPF_IS_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, + &streamNumber); + if (FAILED(hr)) { + qDebug() << "Speak failed."; + } + + m_tts->WaitUntilDone(-1); + + hr = pSpStream->GetBaseStream(&pStream); + if (FAILED(hr)) { + qDebug() << "Couldn't get base stream."; + } + + hr = IStream_Reset(pStream); + if (FAILED(hr)) { + qDebug() << "Couldn't reset stream."; + } + + ULARGE_INTEGER StreamSize; + StreamSize.LowPart = 0; + hr = IStream_Size(pStream, &StreamSize); + + DWORD dwSize = StreamSize.QuadPart; + _lastSoundByteArray.resize(dwSize); + + hr = IStream_Read(pStream, _lastSoundByteArray.data(), dwSize); + if (FAILED(hr)) { + qDebug() << "Couldn't read from stream."; + } + + AudioInjectorOptions options; + options.position = DependencyManager::get()->getMyAvatarPosition(); + + if (_lastSoundAudioInjector) { + _lastSoundAudioInjector->stop(); + _lastSoundAudioInjectorUpdateTimer.stop(); + } + + _lastSoundAudioInjector = AudioInjector::playSoundAndDelete(_lastSoundByteArray, options); + + _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); +#else + qDebug() << "Text-to-Speech isn't currently supported on non-Windows platforms."; +#endif +} + +void TTSScriptingInterface::stopLastSpeech() { + if (_lastSoundAudioInjector) { + _lastSoundAudioInjector->stop(); + _lastSoundAudioInjector = NULL; + } +} diff --git a/interface/src/scripting/TTSScriptingInterface.h b/interface/src/scripting/TTSScriptingInterface.h new file mode 100644 index 0000000000..0f1e723885 --- /dev/null +++ b/interface/src/scripting/TTSScriptingInterface.h @@ -0,0 +1,88 @@ +// TTSScriptingInterface.h +// libraries/audio-client/src/scripting +// +// Created by Zach Fox on 2018-10-10. +// 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 +// + +#ifndef hifi_SpeechScriptingInterface_h +#define hifi_SpeechScriptingInterface_h + +#include +#include +#include +#ifdef WIN32 +#pragma warning(disable : 4996) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // SAPI +#include // SAPI Helper +#endif +#include +#include + +class TTSScriptingInterface : public QObject, public Dependency { + Q_OBJECT + +public: + TTSScriptingInterface(); + ~TTSScriptingInterface(); + + Q_INVOKABLE void speakText(const QString& textToSpeak); + Q_INVOKABLE void stopLastSpeech(); + +private: +#ifdef WIN32 + class CComAutoInit { + public: + // Initializes COM using CoInitialize. + // On failure, signals error using AtlThrow. + CComAutoInit() { + HRESULT hr = ::CoInitialize(NULL); + if (FAILED(hr)) { + ATLTRACE(TEXT("CoInitialize() failed in CComAutoInit constructor (hr=0x%08X).\n"), hr); + AtlThrow(hr); + } + } + + // Initializes COM using CoInitializeEx. + // On failure, signals error using AtlThrow. + explicit CComAutoInit(__in DWORD dwCoInit) { + HRESULT hr = ::CoInitializeEx(NULL, dwCoInit); + if (FAILED(hr)) { + ATLTRACE(TEXT("CoInitializeEx() failed in CComAutoInit constructor (hr=0x%08X).\n"), hr); + AtlThrow(hr); + } + } + + // Uninitializes COM using CoUninitialize. + ~CComAutoInit() { ::CoUninitialize(); } + + // + // Ban copy + // + private: + CComAutoInit(const CComAutoInit&); + }; + + // COM initialization and cleanup (must precede other COM related data members) + CComAutoInit m_comInit; + + // Text to speech engine + CComPtr m_tts; + + // Default voice token + CComPtr m_voiceToken; +#endif + + QByteArray _lastSoundByteArray; + AudioInjectorPointer _lastSoundAudioInjector; + QTimer _lastSoundAudioInjectorUpdateTimer; + void updateLastSoundAudioInjector(); +}; + +#endif // hifi_SpeechScriptingInterface_h diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index e2158b9fd7..93ee60ba5b 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -10,13 +10,15 @@ // #include "WalletScriptingInterface.h" +#include CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) { Q_ASSERT(QThread::currentThread() == qApp->thread()); } WalletScriptingInterface::WalletScriptingInterface() { - + connect(DependencyManager::get().data(), + &AccountManager::limitedCommerceChanged, this, &WalletScriptingInterface::limitedCommerceChanged); } void WalletScriptingInterface::refreshWalletStatus() { @@ -42,4 +44,4 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui } else { qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; } -} \ No newline at end of file +} diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 25955ca7a3..36ee021b29 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -15,13 +15,13 @@ #include #include -#include "scripting/HMDScriptingInterface.h" #include #include #include #include "Application.h" #include "commerce/Wallet.h" #include "ui/overlays/ContextOverlayInterface.h" +#include class CheckoutProxy : public QmlWrapper { Q_OBJECT @@ -29,7 +29,6 @@ public: CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr); }; - /**jsdoc * @namespace Wallet * @@ -37,28 +36,31 @@ public: * @hifi-client-entity * * @property {number} walletStatus + * @property {bool} limitedCommerce */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT - + SINGLETON_DEPENDENCY Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) + Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce NOTIFY limitedCommerceChanged) public: + WalletScriptingInterface(); /**jsdoc - * @function Wallet.refreshWalletStatus + * @function WalletScriptingInterface.refreshWalletStatus */ Q_INVOKABLE void refreshWalletStatus(); /**jsdoc - * @function Wallet.getWalletStatus + * @function WalletScriptingInterface.getWalletStatus * @returns {number} */ Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } /**jsdoc - * @function Wallet.proveAvatarEntityOwnershipVerification + * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification * @param {Uuid} entityID */ Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); @@ -67,29 +69,38 @@ public: // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); + bool getLimitedCommerce() { return DependencyManager::get()->getLimitedCommerce(); } + void setLimitedCommerce(bool isLimited) { DependencyManager::get()->setLimitedCommerce(isLimited); }; + signals: /**jsdoc - * @function Wallet.walletStatusChanged + * @function WalletScriptingInterface.walletStatusChanged * @returns {Signal} */ void walletStatusChanged(); /**jsdoc - * @function Wallet.walletNotSetup + * @function WalletScriptingInterface.limitedCommerceChanged + * @returns {Signal} + */ + void limitedCommerceChanged(); + + /**jsdoc + * @function WalletScriptingInterface.walletNotSetup * @returns {Signal} */ void walletNotSetup(); /**jsdoc - * @function Wallet.ownershipVerificationSuccess + * @function WalletScriptingInterface.ownershipVerificationSuccess * @param {Uuid} entityID * @returns {Signal} */ void ownershipVerificationSuccess(const QUuid& entityID); /**jsdoc - * @function Wallet.ownershipVerificationFailed + * @function WalletScriptingInterface.ownershipVerificationFailed * @param {Uuid} entityID * @returns {Signal} */ diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 75f17def20..0f3d859093 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -54,6 +54,9 @@ WindowScriptingInterface::WindowScriptingInterface() { }); connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &WindowScriptingInterface::onWindowGeometryChanged); + connect(qApp, &Application::interstitialModeChanged, [this] (bool interstitialMode) { + emit interstitialModeChanged(interstitialMode); + }); } WindowScriptingInterface::~WindowScriptingInterface() { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index f6a5a5ef74..ef3dfcef4b 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -619,6 +619,14 @@ signals: */ void redirectErrorStateChanged(bool isInErrorState); + /**jsdoc + * Triggered when interstitial mode changes. + * @function Window.interstitialModeChanged + * @param {bool} interstitialMode - The mode of the interstitial is changed to. + * @returns {Signal} + */ + void interstitialModeChanged(bool interstitialMode); + /**jsdoc * Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with * includeAnimated = false or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}. diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 108f20b2dd..2fb40c8c30 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -115,6 +115,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { batch.resetViewTransform(); batch.setResourceTexture(0, _uiTexture); geometryCache->renderUnitQuad(batch, glm::vec4(1), _qmlGeometryId); + batch.setResourceTexture(0, nullptr); } void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp new file mode 100644 index 0000000000..773253f85c --- /dev/null +++ b/interface/src/ui/Keyboard.cpp @@ -0,0 +1,882 @@ +// +// Keyboard.cpp +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// 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 +// + +#include "Keyboard.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui/overlays/Overlays.h" +#include "ui/overlays/Overlay.h" +#include "ui/overlays/ModelOverlay.h" +#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Text3DOverlay.h" +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" +#include "avatar/AvatarManager.h" +#include "raypick/PickScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" +#include "scripting/WindowScriptingInterface.h" +#include "scripting/SelectionScriptingInterface.h" +#include "DependencyManager.h" + +#include "raypick/StylusPointer.h" +#include "GLMHelpers.h" +#include "Application.h" + +static const int LEFT_HAND_CONTROLLER_INDEX = 0; +static const int RIGHT_HAND_CONTROLLER_INDEX = 1; + +static const float MALLET_LENGTH = 0.2f; +static const float MALLET_TOUCH_Y_OFFSET = 0.052f; +static const float MALLET_Y_OFFSET = 0.180f; + +static const glm::quat MALLET_ROTATION_OFFSET{0.70710678f, 0.0f, -0.70710678f, 0.0f}; +static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.03f, MALLET_LENGTH, 0.03f}; +static const glm::vec3 MALLET_POSITION_OFFSET{0.0f, -MALLET_Y_OFFSET / 2.0f, 0.0f}; +static const glm::vec3 MALLET_TIP_OFFSET{0.0f, MALLET_LENGTH - MALLET_TOUCH_Y_OFFSET, 0.0f}; + + +static const glm::vec3 Z_AXIS {0.0f, 0.0f, 1.0f}; +static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.28f, -0.3f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_DEGREES_OFFSET{-45.0f, 0.0f, 0.0f}; +static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_OFFSET{-0.2f, -0.27f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET{-45.0f, 0.0f, -90.0f}; +static const glm::vec3 KEYBOARD_AVATAR_OFFSET{-0.6f, 0.3f, -0.7f}; +static const glm::vec3 KEYBOARD_AVATAR_DEGREES_OFFSET{0.0f, 180.0f, 0.0f}; + +static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboard_key.mp3"; +static const QString MALLET_MODEL_URL = PathUtils::resourcesUrl() + "meshes/drumstick.fbx"; + +static const float PULSE_STRENGTH = 0.6f; +static const float PULSE_DURATION = 3.0f; + +static const int KEY_PRESS_TIMEOUT_MS = 100; +static const int LAYER_SWITCH_TIMEOUT_MS = 200; + +static const QString CHARACTER_STRING = "character"; +static const QString CAPS_STRING = "caps"; +static const QString CLOSE_STRING = "close"; +static const QString LAYER_STRING = "layer"; +static const QString BACKSPACE_STRING = "backspace"; +static const QString SPACE_STRING = "space"; +static const QString ENTER_STRING = "enter"; + +static const QString KEY_HOVER_HIGHLIGHT = "keyHoverHiglight"; +static const QString KEY_PRESSED_HIGHLIGHT = "keyPressesHighlight"; +static const QVariantMap KEY_HOVERING_STYLE { + { "isOutlineSmooth", true }, + { "outlineWidth", 3 }, + { "outlineUnoccludedColor", QVariantMap {{"red", 13}, {"green", 152}, {"blue", 186}}}, + { "outlineUnoccludedAlpha", 1.0 }, + { "outlineOccludedAlpha", 0.0 }, + { "fillUnoccludedAlpha", 0.0 }, + { "fillOccludedAlpha", 0.0 } +}; + +static const QVariantMap KEY_PRESSING_STYLE { + { "isOutlineSmooth", true }, + { "outlineWidth", 3 }, + { "fillUnoccludedColor", QVariantMap {{"red", 50}, {"green", 50}, {"blue", 50}}}, + { "outlineUnoccludedAlpha", 0.0 }, + { "outlineOccludedAlpha", 0.0 }, + { "fillUnoccludedAlpha", 0.6 }, + { "fillOccludedAlpha", 0.0 } +}; + +std::pair calculateKeyboardPositionAndOrientation() { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto hmd = DependencyManager::get(); + + std::pair keyboardLocation = std::make_pair(glm::vec3(), glm::quat()); + float sensorToWorldScale = myAvatar->getSensorToWorldScale(); + QUuid tabletID = hmd->getCurrentTabletFrameID(); + if (!tabletID.isNull() && hmd->getShouldShowTablet()) { + Overlays& overlays = qApp->getOverlays(); + auto tabletOverlay = std::dynamic_pointer_cast(overlays.getOverlay(tabletID)); + if (tabletOverlay) { + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + bool landscapeMode = tablet->getLandscape(); + glm::vec3 keyboardOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_OFFSET : KEYBOARD_TABLET_OFFSET; + glm::vec3 keyboardDegreesOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET : KEYBOARD_TABLET_DEGREES_OFFSET; + glm::vec3 tabletWorldPosition = tabletOverlay->getWorldPosition(); + glm::quat tabletWorldOrientation = tabletOverlay->getWorldOrientation(); + glm::vec3 scaledKeyboardTabletOffset = keyboardOffset * sensorToWorldScale; + + keyboardLocation.first = tabletWorldPosition + (tabletWorldOrientation * scaledKeyboardTabletOffset); + keyboardLocation.second = tabletWorldOrientation * glm::quat(glm::radians(keyboardDegreesOffset)); + } + + } else { + glm::vec3 avatarWorldPosition = myAvatar->getWorldPosition(); + glm::quat avatarWorldOrientation = myAvatar->getWorldOrientation(); + glm::vec3 scaledKeyboardAvatarOffset = KEYBOARD_AVATAR_OFFSET * sensorToWorldScale; + + keyboardLocation.first = avatarWorldPosition + (avatarWorldOrientation * scaledKeyboardAvatarOffset); + keyboardLocation.second = avatarWorldOrientation * glm::quat(glm::radians(KEYBOARD_AVATAR_DEGREES_OFFSET)); + } + + return keyboardLocation; +} + +void Key::saveDimensionsAndLocalPosition() { + Overlays& overlays = qApp->getOverlays(); + auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + + if (model3DOverlay) { + _originalLocalPosition = model3DOverlay->getLocalPosition(); + _originalDimensions = model3DOverlay->getDimensions(); + _currentLocalPosition = _originalLocalPosition; + } +} + +void Key::scaleKey(float sensorToWorldScale) { + Overlays& overlays = qApp->getOverlays(); + auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + + if (model3DOverlay) { + glm::vec3 scaledLocalPosition = _originalLocalPosition * sensorToWorldScale; + glm::vec3 scaledDimensions = _originalDimensions * sensorToWorldScale; + _currentLocalPosition = scaledLocalPosition; + + QVariantMap properties { + { "dimensions", vec3toVariant(scaledDimensions) }, + { "localPosition", vec3toVariant(scaledLocalPosition) } + }; + + overlays.editOverlay(_keyID, properties); + } +} + +void Key::startTimer(int time) { + if (_timer) { + _timer->start(time); + _timer->setSingleShot(true); + } +} + +bool Key::timerFinished() { + if (_timer) { + return (_timer->remainingTime() <= 0); + } + return false; +} + +QString Key::getKeyString(bool toUpper) const { + return toUpper ? _keyString.toUpper() : _keyString; +} + +int Key::getScanCode(bool toUpper) const { + QString character = toUpper ? _keyString.toUpper() : _keyString; + auto utf8Key = character.toUtf8(); + return (int)utf8Key[0]; +} + +Key::Type Key::getKeyTypeFromString(const QString& keyTypeString) { + if (keyTypeString == SPACE_STRING) { + return Type::SPACE; + } else if (keyTypeString == BACKSPACE_STRING) { + return Type::BACKSPACE; + } else if (keyTypeString == LAYER_STRING) { + return Type::LAYER; + } else if (keyTypeString == CAPS_STRING) { + return Type::CAPS; + } else if (keyTypeString == CLOSE_STRING) { + return Type::CLOSE; + } else if (keyTypeString == ENTER_STRING) { + return Type::ENTER; + } + + return Type::CHARACTER; +} + +Keyboard::Keyboard() { + auto pointerManager = DependencyManager::get(); + auto windowScriptingInterface = DependencyManager::get(); + auto myAvatar = DependencyManager::get()->getMyAvatar(); + connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Keyboard::handleTriggerBegin, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Keyboard::handleTriggerContinue, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Keyboard::handleTriggerEnd, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Keyboard::handleHoverBegin, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Keyboard::handleHoverEnd, Qt::QueuedConnection); + connect(myAvatar.get(), &MyAvatar::sensorToWorldScaleChanged, this, &Keyboard::scaleKeyboard, Qt::QueuedConnection); + connect(windowScriptingInterface.data(), &WindowScriptingInterface::domainChanged, [&]() { setRaised(false); }); +} + +void Keyboard::registerKeyboardHighlighting() { + auto selection = DependencyManager::get(); + selection->enableListHighlight(KEY_HOVER_HIGHLIGHT, KEY_HOVERING_STYLE); + selection->enableListToScene(KEY_HOVER_HIGHLIGHT); + selection->enableListHighlight(KEY_PRESSED_HIGHLIGHT, KEY_PRESSING_STYLE); + selection->enableListToScene(KEY_PRESSED_HIGHLIGHT); +} + + +void Keyboard::createKeyboard() { + auto pointerManager = DependencyManager::get(); + + QVariantMap modelProperties { + { "url", MALLET_MODEL_URL } + }; + + QVariantMap leftStylusProperties { + { "hand", LEFT_HAND_CONTROLLER_INDEX }, + { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "model", modelProperties }, + { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } + }; + + QVariantMap rightStylusProperties { + { "hand", RIGHT_HAND_CONTROLLER_INDEX }, + { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "model", modelProperties }, + { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } + }; + + _leftHandStylus = pointerManager->addPointer(std::make_shared(leftStylusProperties, StylusPointer::buildStylusOverlay(leftStylusProperties), true, true, + MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); + _rightHandStylus = pointerManager->addPointer(std::make_shared(rightStylusProperties, StylusPointer::buildStylusOverlay(rightStylusProperties), true, true, + MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); + + pointerManager->disablePointer(_rightHandStylus); + pointerManager->disablePointer(_leftHandStylus); + + QString keyboardSvg = PathUtils::resourcesUrl() + "config/keyboard.json"; + loadKeyboardFile(keyboardSvg); + + _keySound = DependencyManager::get()->getSound(SOUND_FILE); +} + +bool Keyboard::isRaised() const { + return resultWithReadLock([&] { return _raised; }); +} + +void Keyboard::setRaised(bool raised) { + + bool isRaised; + withReadLock([&] { isRaised = _raised; }); + if (isRaised != raised) { + raiseKeyboardAnchor(raised); + raiseKeyboard(raised); + raised ? enableStylus() : disableStylus(); + withWriteLock([&] { + _raised = raised; + _layerIndex = 0; + _capsEnabled = false; + _typedCharacters.clear(); + }); + + updateTextDisplay(); + } +} + +void Keyboard::updateTextDisplay() { + Overlays& overlays = qApp->getOverlays(); + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + float sensorToWorldScale = myAvatar->getSensorToWorldScale(); + float textWidth = (float) overlays.textSize(_textDisplay.overlayID, _typedCharacters).width(); + + glm::vec3 scaledDimensions = _textDisplay.dimensions; + scaledDimensions *= sensorToWorldScale; + float leftMargin = (scaledDimensions.x / 2); + scaledDimensions.x += textWidth; + + + QVariantMap textDisplayProperties { + { "dimensions", vec3toVariant(scaledDimensions) }, + { "leftMargin", leftMargin }, + { "text", _typedCharacters }, + { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); +} + +void Keyboard::raiseKeyboardAnchor(bool raise) const { + Overlays& overlays = qApp->getOverlays(); + OverlayID anchorOverlayID = _anchor.overlayID; + auto anchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(anchorOverlayID)); + if (anchorOverlay) { + std::pair keyboardLocation = calculateKeyboardPositionAndOrientation(); + anchorOverlay->setWorldPosition(keyboardLocation.first); + anchorOverlay->setWorldOrientation(keyboardLocation.second); + anchorOverlay->setVisible(raise); + + QVariantMap textDisplayProperties { + { "visible", raise } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); + } +} + +void Keyboard::scaleKeyboard(float sensorToWorldScale) { + Overlays& overlays = qApp->getOverlays(); + + glm::vec3 scaledDimensions = _anchor.originalDimensions * sensorToWorldScale; + auto volume3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_anchor.overlayID)); + + if (volume3DOverlay) { + volume3DOverlay->setDimensions(scaledDimensions); + } + + for (auto& keyboardLayer: _keyboardLayers) { + for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { + iter.value().scaleKey(sensorToWorldScale); + } + } + + + + glm::vec3 scaledLocalPosition = _textDisplay.localPosition * sensorToWorldScale; + glm::vec3 textDisplayScaledDimensions = _textDisplay.dimensions * sensorToWorldScale; + + QVariantMap textDisplayProperties { + { "localPosition", vec3toVariant(scaledLocalPosition) }, + { "dimensions", vec3toVariant(textDisplayScaledDimensions) }, + { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); +} + +void Keyboard::startLayerSwitchTimer() { + if (_layerSwitchTimer) { + _layerSwitchTimer->start(LAYER_SWITCH_TIMEOUT_MS); + _layerSwitchTimer->setSingleShot(true); + } +} + +bool Keyboard::isLayerSwitchTimerFinished() { + if (_layerSwitchTimer) { + return (_layerSwitchTimer->remainingTime() <= 0); + } + return false; +} + +void Keyboard::raiseKeyboard(bool raise) const { + if (_keyboardLayers.empty()) { + return; + } + Overlays& overlays = qApp->getOverlays(); + const auto& keyboardLayer = _keyboardLayers[_layerIndex]; + for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(iter.key())); + if (base3DOverlay) { + base3DOverlay->setVisible(raise); + } + } +} + +bool Keyboard::isPassword() const { + return resultWithReadLock([&] { return _password; }); +} + +void Keyboard::setPassword(bool password) { + if (_password != password) { + withWriteLock([&] { + _password = password; + _typedCharacters.clear(); + }); + } + + updateTextDisplay(); +} + +void Keyboard::switchToLayer(int layerIndex) { + if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { + Overlays& overlays = qApp->getOverlays(); + + OverlayID currentAnchorOverlayID = _anchor.overlayID; + + glm::vec3 currentOverlayPosition; + glm::quat currentOverlayOrientation; + + auto currentAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(currentAnchorOverlayID)); + if (currentAnchorOverlay) { + currentOverlayPosition = currentAnchorOverlay->getWorldPosition(); + currentOverlayOrientation = currentAnchorOverlay->getWorldOrientation(); + } + + raiseKeyboardAnchor(false); + raiseKeyboard(false); + + setLayerIndex(layerIndex); + + raiseKeyboardAnchor(true); + raiseKeyboard(true); + + OverlayID newAnchorOverlayID = _anchor.overlayID; + auto newAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(newAnchorOverlayID)); + if (newAnchorOverlay) { + newAnchorOverlay->setWorldPosition(currentOverlayPosition); + newAnchorOverlay->setWorldOrientation(currentOverlayOrientation); + } + + startLayerSwitchTimer(); + } +} + +void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + auto buttonType = event.getButton(); + + if ((pointerID != _leftHandStylus && pointerID != _rightHandStylus) || buttonType != PointerEvent::PrimaryButton) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + Key& key = search.value(); + + if (key.timerFinished()) { + + auto handIndex = (pointerID == _leftHandStylus) ? controller::Hand::LEFT : controller::Hand::RIGHT; + auto userInputMapper = DependencyManager::get(); + userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex); + + Overlays& overlays = qApp->getOverlays(); + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + glm::vec3 keyWorldPosition; + if (base3DOverlay) { + keyWorldPosition = base3DOverlay->getWorldPosition(); + } + + AudioInjectorOptions audioOptions; + audioOptions.localOnly = true; + audioOptions.position = keyWorldPosition; + audioOptions.volume = 0.1f; + + AudioInjector::playSound(_keySound->getByteArray(), audioOptions); + + int scanCode = key.getScanCode(_capsEnabled); + QString keyString = key.getKeyString(_capsEnabled); + + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + + switch (key.getKeyType()) { + case Key::Type::CLOSE: + setRaised(false); + tablet->unfocus(); + return; + + case Key::Type::CAPS: + _capsEnabled = !_capsEnabled; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::LAYER: + _capsEnabled = false; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::BACKSPACE: + scanCode = Qt::Key_Backspace; + keyString = "\x08"; + _typedCharacters = _typedCharacters.left(_typedCharacters.length() -1); + updateTextDisplay(); + break; + case Key::Type::ENTER: + scanCode = Qt::Key_Return; + keyString = "\x0d"; + _typedCharacters.clear(); + updateTextDisplay(); + break; + case Key::Type::CHARACTER: + if (keyString != " ") { + _typedCharacters.push_back((_password ? "*" : keyString)); + } else { + _typedCharacters.clear(); + } + updateTextDisplay(); + break; + + default: + break; + } + + QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); + QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); + QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent); + QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent); + + key.startTimer(KEY_PRESS_TIMEOUT_MS); + auto selection = DependencyManager::get(); + selection->addToSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID); + } +} + +void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + Key& key = search.value();; + Overlays& overlays = qApp->getOverlays(); + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + base3DOverlay->setLocalPosition(key.getCurrentLocalPosition()); + } + + key.setIsPressed(false); + if (key.timerFinished()) { + key.startTimer(KEY_PRESS_TIMEOUT_MS); + } + + auto selection = DependencyManager::get(); + selection->removeFromSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID); +} + +void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + Key& key = search.value(); + Overlays& overlays = qApp->getOverlays(); + + if (!key.isPressed()) { + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + auto pointerManager = DependencyManager::get(); + auto pickResult = pointerManager->getPrevPickResult(pointerID); + auto stylusPickResult = std::dynamic_pointer_cast(pickResult); + float distance = stylusPickResult->distance; + + static const float PENATRATION_THRESHOLD = 0.025f; + if (distance < PENATRATION_THRESHOLD) { + static const float Z_OFFSET = 0.002f; + glm::quat overlayOrientation = base3DOverlay->getWorldOrientation(); + glm::vec3 overlayYAxis = overlayOrientation * Z_AXIS; + glm::vec3 overlayYOffset = overlayYAxis * Z_OFFSET; + glm::vec3 localPosition = key.getCurrentLocalPosition() - overlayYOffset; + base3DOverlay->setLocalPosition(localPosition); + key.setIsPressed(true); + } + } + } +} + +void Keyboard::handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + auto selection = DependencyManager::get(); + selection->addToSelectedItemsList(KEY_HOVER_HIGHLIGHT, "overlay", overlayID); +} + +void Keyboard::handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + auto selection = DependencyManager::get(); + selection->removeFromSelectedItemsList(KEY_HOVER_HIGHLIGHT, "overlay", overlayID); +} + +void Keyboard::disableStylus() { + auto pointerManager = DependencyManager::get(); + pointerManager->setRenderState(_leftHandStylus, "events off"); + pointerManager->disablePointer(_leftHandStylus); + pointerManager->setRenderState(_rightHandStylus, "events off"); + pointerManager->disablePointer(_rightHandStylus); +} + +void Keyboard::setLayerIndex(int layerIndex) { + if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { + _layerIndex = layerIndex; + } else { + _layerIndex = 0; + } +} + +void Keyboard::loadKeyboardFile(const QString& keyboardFile) { + if (keyboardFile.isEmpty()) { + return; + } + + auto request = DependencyManager::get()->createResourceRequest(this, keyboardFile); + + if (!request) { + qCWarning(interfaceapp) << "Could not create resource for Keyboard file" << keyboardFile; + } + + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() != ResourceRequest::Success) { + qCWarning(interfaceapp) << "Keyboard file failed to download"; + return; + } + + clearKeyboardKeys(); + Overlays& overlays = qApp->getOverlays(); + auto requestData = request->getData(); + + QVector includeItems; + + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(requestData, &parseError); + + if (parseError.error != QJsonParseError::NoError) { + qCWarning(interfaceapp) << "Failed to parse keyboard json file - Error: " << parseError.errorString(); + return; + } + QJsonObject jsonObject = jsonDoc.object(); + QJsonArray layer = jsonObject["Layer1"].toArray(); + QJsonObject anchorObject = jsonObject["anchor"].toObject(); + bool useResourcePath = jsonObject["useResourcesPath"].toBool(); + QString resourcePath = PathUtils::resourcesUrl(); + + + if (anchorObject.isEmpty()) { + qCWarning(interfaceapp) << "No Anchor specified. Not creating keyboard"; + return; + } + + QVariantMap anchorProperties { + { "name", "KeyboardAnchor"}, + { "isSolid", true }, + { "visible", false }, + { "grabbable", true }, + { "ignoreRayIntersection", false }, + { "dimensions", anchorObject["dimensions"].toVariant() }, + { "position", anchorObject["position"].toVariant() }, + { "orientation", anchorObject["rotation"].toVariant() } + }; + + glm::vec3 dimensions = vec3FromVariant(anchorObject["dimensions"].toVariant()); + + Anchor anchor; + anchor.overlayID = overlays.addOverlay("cube", anchorProperties); + anchor.originalDimensions = dimensions; + _anchor = anchor; + + const QJsonArray& keyboardLayers = jsonObject["layers"].toArray(); + int keyboardLayerCount = keyboardLayers.size(); + _keyboardLayers.reserve(keyboardLayerCount); + + + for (int keyboardLayerIndex = 0; keyboardLayerIndex < keyboardLayerCount; keyboardLayerIndex++) { + const QJsonValue& keyboardLayer = keyboardLayers[keyboardLayerIndex].toArray(); + + QHash keyboardLayerKeys; + foreach (const QJsonValue& keyboardKeyValue, keyboardLayer.toArray()) { + + QVariantMap textureMap; + if (!keyboardKeyValue["texture"].isNull()) { + textureMap = keyboardKeyValue["texture"].toObject().toVariantMap(); + + if (useResourcePath) { + for (auto iter = textureMap.begin(); iter != textureMap.end(); iter++) { + QString modifiedPath = resourcePath + iter.value().toString(); + textureMap[iter.key()] = modifiedPath; + } + } + } + + QString modelUrl = keyboardKeyValue["modelURL"].toString(); + QString url = (useResourcePath ? (resourcePath + modelUrl) : modelUrl); + + QVariantMap properties { + { "dimensions", keyboardKeyValue["dimensions"].toVariant() }, + { "position", keyboardKeyValue["position"].toVariant() }, + { "visible", false }, + { "isSolid", true }, + { "emissive", true }, + { "parentID", _anchor.overlayID }, + { "url", url }, + { "textures", textureMap }, + { "grabbable", false }, + { "localOrientation", keyboardKeyValue["localOrientation"].toVariant() } + }; + + OverlayID overlayID = overlays.addOverlay("model", properties); + + QString keyType = keyboardKeyValue["type"].toString(); + QString keyString = keyboardKeyValue["key"].toString(); + + Key key; + if (!keyType.isNull()) { + Key::Type type= Key::getKeyTypeFromString(keyType); + key.setKeyType(type); + + if (type == Key::Type::LAYER || type == Key::Type::CAPS) { + int switchToLayer = keyboardKeyValue["switchToLayer"].toInt(); + key.setSwitchToLayerIndex(switchToLayer); + } + } + key.setID(overlayID); + key.setKeyString(keyString); + key.saveDimensionsAndLocalPosition(); + + includeItems.append(key.getID()); + _itemsToIgnore.append(key.getID()); + keyboardLayerKeys.insert(overlayID, key); + } + + _keyboardLayers.push_back(keyboardLayerKeys); + + } + + TextDisplay textDisplay; + QJsonObject displayTextObject = jsonObject["textDisplay"].toObject(); + + QVariantMap displayTextProperties { + { "dimensions", displayTextObject["dimensions"].toVariant() }, + { "localPosition", displayTextObject["localPosition"].toVariant() }, + { "localOrientation", displayTextObject["localOrientation"].toVariant() }, + { "leftMargin", displayTextObject["leftMargin"].toVariant() }, + { "rightMargin", displayTextObject["rightMargin"].toVariant() }, + { "topMargin", displayTextObject["topMargin"].toVariant() }, + { "bottomMargin", displayTextObject["bottomMargin"].toVariant() }, + { "lineHeight", displayTextObject["lineHeight"].toVariant() }, + { "visible", false }, + { "emissive", true }, + { "grabbable", false }, + { "text", ""}, + { "parentID", _anchor.overlayID } + }; + + textDisplay.overlayID = overlays.addOverlay("text3d", displayTextProperties); + textDisplay.localPosition = vec3FromVariant(displayTextObject["localPosition"].toVariant()); + textDisplay.dimensions = vec3FromVariant(displayTextObject["dimensions"].toVariant()); + textDisplay.lineHeight = (float) displayTextObject["lineHeight"].toDouble(); + + _textDisplay = textDisplay; + + _ignoreItemsLock.withWriteLock([&] { + _itemsToIgnore.push_back(_textDisplay.overlayID); + _itemsToIgnore.push_back(_anchor.overlayID); + }); + _layerIndex = 0; + auto pointerManager = DependencyManager::get(); + pointerManager->setIncludeItems(_leftHandStylus, includeItems); + pointerManager->setIncludeItems(_rightHandStylus, includeItems); + }); + + request->send(); +} + +QVector Keyboard::getKeysID() { + return _ignoreItemsLock.resultWithReadLock>([&] { + return _itemsToIgnore; + }); +} + +void Keyboard::clearKeyboardKeys() { + Overlays& overlays = qApp->getOverlays(); + + for (const auto& keyboardLayer: _keyboardLayers) { + for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { + overlays.deleteOverlay(iter.key()); + } + } + + overlays.deleteOverlay(_anchor.overlayID); + overlays.deleteOverlay(_textDisplay.overlayID); + + _keyboardLayers.clear(); + + _ignoreItemsLock.withWriteLock([&] { + _itemsToIgnore.clear(); + }); +} + +void Keyboard::enableStylus() { + auto pointerManager = DependencyManager::get(); + pointerManager->setRenderState(_leftHandStylus, "events on"); + pointerManager->enablePointer(_leftHandStylus); + pointerManager->setRenderState(_rightHandStylus, "events on"); + pointerManager->enablePointer(_rightHandStylus); + +} diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h new file mode 100644 index 0000000000..18db38b2ae --- /dev/null +++ b/interface/src/ui/Keyboard.h @@ -0,0 +1,155 @@ +// +// Keyboard.h +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2017 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_Keyboard_h +#define hifi_Keyboard_h + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ui/overlays/Overlay.h" + +class PointerEvent; + + +class Key { +public: + Key() = default; + ~Key() = default; + + enum Type { + CHARACTER, + CAPS, + CLOSE, + LAYER, + BACKSPACE, + SPACE, + ENTER + }; + + static Key::Type getKeyTypeFromString(const QString& keyTypeString); + + OverlayID getID() const { return _keyID; } + void setID(OverlayID overlayID) { _keyID = overlayID; } + + void startTimer(int time); + bool timerFinished(); + + void setKeyString(QString keyString) { _keyString = keyString; } + QString getKeyString(bool toUpper) const; + int getScanCode(bool toUpper) const; + + bool isPressed() const { return _pressed; } + void setIsPressed(bool pressed) { _pressed = pressed; } + + void setSwitchToLayerIndex(int layerIndex) { _switchToLayer = layerIndex; } + int getSwitchToLayerIndex() const { return _switchToLayer; } + + Type getKeyType() const { return _type; } + void setKeyType(Type type) { _type = type; } + + glm::vec3 getCurrentLocalPosition() const { return _currentLocalPosition; } + + void saveDimensionsAndLocalPosition(); + + void scaleKey(float sensorToWorldScale); +private: + Type _type { Type::CHARACTER }; + + int _switchToLayer { 0 }; + bool _pressed { false }; + + OverlayID _keyID; + QString _keyString; + + glm::vec3 _originalLocalPosition; + glm::vec3 _originalDimensions; + glm::vec3 _currentLocalPosition; + + std::shared_ptr _timer { std::make_shared() }; +}; + +class Keyboard : public Dependency, public QObject, public ReadWriteLockable { +public: + Keyboard(); + void createKeyboard(); + void registerKeyboardHighlighting(); + bool isRaised() const; + void setRaised(bool raised); + + bool isPassword() const; + void setPassword(bool password); + + void loadKeyboardFile(const QString& keyboardFile); + QVector getKeysID(); + +public slots: + void handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event); + void handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event); + void handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event); + void scaleKeyboard(float sensorToWorldScale); + +private: + struct Anchor { + OverlayID overlayID; + glm::vec3 originalDimensions; + }; + + struct TextDisplay { + float lineHeight; + OverlayID overlayID; + glm::vec3 localPosition; + glm::vec3 dimensions; + }; + + void raiseKeyboard(bool raise) const; + void raiseKeyboardAnchor(bool raise) const; + + void setLayerIndex(int layerIndex); + void enableStylus(); + void disableStylus(); + void clearKeyboardKeys(); + void switchToLayer(int layerIndex); + void updateTextDisplay(); + + void startLayerSwitchTimer(); + bool isLayerSwitchTimerFinished(); + + bool _raised { false }; + bool _password { false }; + bool _capsEnabled { false }; + int _layerIndex { 0 }; + unsigned int _leftHandStylus { 0 }; + unsigned int _rightHandStylus { 0 }; + SharedSoundPointer _keySound { nullptr }; + std::shared_ptr _layerSwitchTimer { std::make_shared() }; + + QString _typedCharacters; + TextDisplay _textDisplay; + Anchor _anchor; + + mutable ReadWriteLockable _ignoreItemsLock; + QVector _itemsToIgnore; + std::vector> _keyboardLayers; +}; + +#endif diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 5eccef5e9d..d71ad9dc82 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -147,7 +147,6 @@ void setupPreferences() { preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter)); } */ - // Snapshots static const QString SNAPSHOTS { "Snapshots" }; { @@ -259,6 +258,39 @@ void setupPreferences() { auto preference = new CheckPreference(VR_MOVEMENT, "Show room boundaries while teleporting", getter, setter); preferences->addPreference(preference); } + { + auto getter = [myAvatar]()->int { + switch (myAvatar->getUserRecenterModel()) { + case MyAvatar::SitStandModelType::Auto: + default: + return 0; + case MyAvatar::SitStandModelType::ForceSit: + return 1; + case MyAvatar::SitStandModelType::DisableHMDLean: + return 2; + } + }; + auto setter = [myAvatar](int value) { + switch (value) { + case 0: + default: + myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::Auto); + break; + case 1: + myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceSit); + break; + case 2: + myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::DisableHMDLean); + break; + } + }; + auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Disable Recenter", getter, setter); + QStringList items; + items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]"; + preference->setHeading("Avatar leaning behavior"); + preference->setItems(items); + preferences->addPreference(preference); + } { auto getter = [=]()->float { return myAvatar->getUserHeight(); }; auto setter = [=](float value) { myAvatar->setUserHeight(value); }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8faa51bc58..495e29f986 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -267,8 +267,8 @@ void Stats::updateStats(bool force) { auto loadingRequests = ResourceCache::getLoadingRequests(); STAT_UPDATE(downloads, loadingRequests.size()); - STAT_UPDATE(downloadLimit, ResourceCache::getRequestLimit()) - STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); + STAT_UPDATE(downloadLimit, (int)ResourceCache::getRequestLimit()) + STAT_UPDATE(downloadsPending, (int)ResourceCache::getPendingRequestCount()); STAT_UPDATE(processing, DependencyManager::get()->getStat("Processing").toInt()); STAT_UPDATE(processingPending, DependencyManager::get()->getStat("PendingProcessing").toInt()); diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 1d8db69e26..8599e05332 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -191,8 +191,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { if (properties["parentID"].isValid()) { setParentID(QUuid(properties["parentID"].toString())); - bool success; - getParentPointer(success); // call this to hook-up the parent's back-pointers to its child overlays needRenderItemUpdate = true; } if (properties["parentJointIndex"].isValid()) { @@ -292,6 +290,7 @@ void Base3DOverlay::locationChanged(bool tellPhysics) { notifyRenderVariableChange(); } +// FIXME: Overlays shouldn't be deleted when their parents are void Base3DOverlay::parentDeleted() { qApp->getOverlays().deleteOverlay(getOverlayID()); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 6f6092a42e..6cc5182b56 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -59,8 +59,6 @@ public: void setIsGrabbable(bool value) { _isGrabbable = value; } virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } - virtual AABox getBounds() const override = 0; - void update(float deltatime) override; void notifyRenderVariableChange() const; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 1d418d966e..a98ea7070e 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -75,7 +75,6 @@ void Circle3DOverlay::render(RenderArgs* args) { const float FULL_CIRCLE = 360.0f; const float SLICES = 180.0f; // The amount of segment to create the circle const float SLICE_ANGLE_RADIANS = glm::radians(FULL_CIRCLE / SLICES); - const float MAX_COLOR = 255.0f; auto geometryCache = DependencyManager::get(); @@ -246,20 +245,15 @@ void Circle3DOverlay::render(RenderArgs* args) { angle += tickMarkAngle; } } - - xColor majorColorX = getMajorTickMarksColor(); - glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha); - + + glm::vec4 majorColor(toGlm(getMajorTickMarksColor()), alpha); geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor); - - xColor minorColorX = getMinorTickMarksColor(); - glm::vec4 minorColor(minorColorX.red / MAX_COLOR, minorColorX.green / MAX_COLOR, minorColorX.blue / MAX_COLOR, alpha); - + glm::vec4 minorColor(toGlm(getMinorTickMarksColor()), alpha); geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor); } - + geometryCache->renderVertices(batch, gpu::LINES, _majorTicksVerticesID); - + geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID); } } @@ -280,8 +274,8 @@ template T fromVariant(const QVariant& v, bool& valid) { return qvariant_cast(v); } -template<> xColor fromVariant(const QVariant& v, bool& valid) { - return xColorFromVariant(v, valid); +template<> glm::u8vec3 fromVariant(const QVariant& v, bool& valid) { + return u8vec3FromVariant(v, valid); } template @@ -344,11 +338,11 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { _dirty |= updateIfValid(properties, "outerStartAlpha", _outerStartAlpha); _dirty |= updateIfValid(properties, "outerEndAlpha", _outerEndAlpha); - _dirty |= updateIfValid(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor }); - _dirty |= updateIfValid(properties, "startColor", { _innerStartColor, _outerStartColor } ); - _dirty |= updateIfValid(properties, "endColor", { _innerEndColor, _outerEndColor } ); - _dirty |= updateIfValid(properties, "innerColor", { _innerStartColor, _innerEndColor } ); - _dirty |= updateIfValid(properties, "outerColor", { _outerStartColor, _outerEndColor } ); + _dirty |= updateIfValid(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor }); + _dirty |= updateIfValid(properties, "startColor", { _innerStartColor, _outerStartColor } ); + _dirty |= updateIfValid(properties, "endColor", { _innerEndColor, _outerEndColor } ); + _dirty |= updateIfValid(properties, "innerColor", { _innerStartColor, _innerEndColor } ); + _dirty |= updateIfValid(properties, "outerColor", { _outerStartColor, _outerEndColor } ); _dirty |= updateIfValid(properties, "innerStartColor", _innerStartColor); _dirty |= updateIfValid(properties, "innerEndColor", _innerEndColor); _dirty |= updateIfValid(properties, "outerStartColor", _outerStartColor); @@ -421,7 +415,7 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees. * @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: radius. * @property {number} innerRadius=0 - The inner radius of the overlay, in meters. - * @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of + * @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of * innerStartColor, innerEndColor, outerStartColor, and outerEndColor. * @property {Color} startColor - Sets the values of innerStartColor and outerStartColor. * Write-only. @@ -478,16 +472,16 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { return _innerRadius; } if (property == "innerStartColor") { - return xColorToVariant(_innerStartColor); + return u8vec3ColortoVariant(_innerStartColor); } if (property == "innerEndColor") { - return xColorToVariant(_innerEndColor); + return u8vec3ColortoVariant(_innerEndColor); } if (property == "outerStartColor") { - return xColorToVariant(_outerStartColor); + return u8vec3ColortoVariant(_outerStartColor); } if (property == "outerEndColor") { - return xColorToVariant(_outerEndColor); + return u8vec3ColortoVariant(_outerEndColor); } if (property == "innerStartAlpha") { return _innerStartAlpha; @@ -517,10 +511,10 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { return _minorTickMarksLength; } if (property == "majorTickMarksColor") { - return xColorToVariant(_majorTickMarksColor); + return u8vec3ColortoVariant(_majorTickMarksColor); } if (property == "minorTickMarksColor") { - return xColorToVariant(_minorTickMarksColor); + return u8vec3ColortoVariant(_minorTickMarksColor); } return Planar3DOverlay::getProperty(property); diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index b3fa24fb16..ca5e05a53b 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -39,8 +39,8 @@ public: float getMinorTickMarksAngle() const { return _minorTickMarksAngle; } float getMajorTickMarksLength() const { return _majorTickMarksLength; } float getMinorTickMarksLength() const { return _minorTickMarksLength; } - xColor getMajorTickMarksColor() const { return _majorTickMarksColor; } - xColor getMinorTickMarksColor() const { return _minorTickMarksColor; } + glm::u8vec3 getMajorTickMarksColor() const { return _majorTickMarksColor; } + glm::u8vec3 getMinorTickMarksColor() const { return _minorTickMarksColor; } void setStartAt(float value) { _startAt = value; } void setEndAt(float value) { _endAt = value; } @@ -51,8 +51,8 @@ public: void setMinorTickMarksAngle(float value) { _minorTickMarksAngle = value; } void setMajorTickMarksLength(float value) { _majorTickMarksLength = value; } void setMinorTickMarksLength(float value) { _minorTickMarksLength = value; } - void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; } - void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; } + void setMajorTickMarksColor(const glm::u8vec3& value) { _majorTickMarksColor = value; } + void setMinorTickMarksColor(const glm::u8vec3& value) { _minorTickMarksColor = value; } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; @@ -67,10 +67,10 @@ protected: float _outerRadius { 1 }; float _innerRadius { 0 }; - xColor _innerStartColor { DEFAULT_OVERLAY_COLOR }; - xColor _innerEndColor { DEFAULT_OVERLAY_COLOR }; - xColor _outerStartColor { DEFAULT_OVERLAY_COLOR }; - xColor _outerEndColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _innerStartColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _innerEndColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _outerStartColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _outerEndColor { DEFAULT_OVERLAY_COLOR }; float _innerStartAlpha { DEFAULT_ALPHA }; float _innerEndAlpha { DEFAULT_ALPHA }; float _outerStartAlpha { DEFAULT_ALPHA }; @@ -81,8 +81,8 @@ protected: float _minorTickMarksAngle { 0 }; float _majorTickMarksLength { 0 }; float _minorTickMarksLength { 0 }; - xColor _majorTickMarksColor { DEFAULT_OVERLAY_COLOR }; - xColor _minorTickMarksColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _majorTickMarksColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _minorTickMarksColor { DEFAULT_OVERLAY_COLOR }; gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN }; int _quadVerticesID { 0 }; int _lineVerticesID { 0 }; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 5de8bb1a2a..11c268dd48 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -83,7 +83,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _challengeOwnershipTimeoutTimer.setSingleShot(true); } -static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const glm::u8vec3 CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_OFFSET_DISTANCE = 0.1f; @@ -142,14 +142,15 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); glm::vec3 entityDimensions = entityProperties.getDimensions(); glm::vec3 entityPosition = entityProperties.getPosition(); + glm::vec3 registrationPoint = entityProperties.getRegistrationPoint(); glm::vec3 contextOverlayPosition = entityProperties.getPosition(); glm::vec2 contextOverlayDimensions; // Update the position of the overlay if the registration point of the entity // isn't default - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + if (registrationPoint != glm::vec3(0.5f)) { + glm::vec3 adjustPos = registrationPoint - glm::vec3(0.5f); + entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityDimensions)); } enableEntityHighlight(entityItemID); @@ -222,12 +223,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - Setting::Handle _settingSwitch{ "commerce", true }; - if (_settingSwitch.get()) { - return (entityProperties.getCertificateID().length() != 0); - } else { - return (entityProperties.getMarketplaceID().length() != 0); - } + return (entityProperties.getCertificateID().length() != 0); } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 38fff5f26f..581db672a3 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -49,11 +49,8 @@ void Cube3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - - + glm::u8vec3 color = getColor(); + glm::vec4 cubeColor(toGlm(color), alpha); auto batch = args->_batch; if (batch) { diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 15eb9eef76..87ab0fb2e8 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -57,11 +57,9 @@ void Grid3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - const float MAX_COLOR = 255.0f; - float alpha = getAlpha(); - xColor color = getColor(); - glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 gridColor(toGlm(color), alpha); auto batch = args->_batch; diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 608e7eb72f..e24e3b3ed8 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -98,8 +98,8 @@ void Image3DOverlay::render(RenderArgs* args) { } float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = fromImage.width() / (2.0f * maxSize); - float y = -fromImage.height() / (2.0f * maxSize); + float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f; + float y = _keepAspectRatio ? -fromImage.height() / (2.0f * maxSize) : -0.5f; glm::vec2 topLeft(-x, -y); glm::vec2 bottomRight(x, y); @@ -107,17 +107,16 @@ void Image3DOverlay::render(RenderArgs* args) { glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); - const float MAX_COLOR = 255.0f; - xColor color = getColor(); float alpha = getAlpha(); + glm::u8vec3 color = getColor(); + glm::vec4 imageColor(toGlm(color), alpha); batch->setModelTransform(getRenderTransform()); batch->setResourceTexture(0, _texture->getGPUTexture()); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha), - _geometryId + imageColor, _geometryId ); batch->setResourceTexture(0, nullptr); // restore default white color after me @@ -177,6 +176,11 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { } } + auto keepAspectRatioValue = properties["keepAspectRatio"]; + if (keepAspectRatioValue.isValid()) { + _keepAspectRatio = keepAspectRatioValue.toBool(); + } + auto emissiveValue = properties["emissive"]; if (emissiveValue.isValid()) { _emissive = emissiveValue.toBool(); @@ -226,6 +230,8 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. * + * @property {bool} keepAspectRatio=true - overlays will maintain the aspect ratio when the subImage is applied. + * * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. * @@ -247,6 +253,9 @@ QVariant Image3DOverlay::getProperty(const QString& property) { if (property == "emissive") { return _emissive; } + if (property == "keepAspectRatio") { + return _keepAspectRatio; + } return Billboard3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Image3DOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h index 1ffa062d45..1000401abb 100644 --- a/interface/src/ui/overlays/Image3DOverlay.h +++ b/interface/src/ui/overlays/Image3DOverlay.h @@ -58,6 +58,7 @@ private: bool _textureIsLoaded { false }; bool _alphaTexture { false }; bool _emissive { false }; + bool _keepAspectRatio { true }; QRect _fromImage; // where from in the image to sample int _geometryId { 0 }; diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index af6c3c2472..e6546686b0 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -128,9 +128,8 @@ void Line3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 colorv4(toGlm(color), alpha); auto batch = args->_batch; if (batch) { batch->setModelTransform(Transform()); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index eee8222051..19bdfce2b3 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -60,6 +60,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : } void ModelOverlay::update(float deltatime) { + Base3DOverlay::update(deltatime); + if (_updateModel) { _updateModel = false; _model->setSnapModelToCenter(true); @@ -446,7 +448,7 @@ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "jointNames") { if (_model && _model->isActive()) { - // note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty + // note: going through Rig because Model::getJointNames() (which proxies to HFMModel) was always empty const Rig* rig = &(_model->getRig()); return mapJoints([rig](int jointIndex) -> QString { return rig->nameOfJoint(jointIndex); @@ -574,7 +576,7 @@ void ModelOverlay::animate() { QVector jointsData; - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); if (frameCount <= 0) { return; @@ -605,11 +607,11 @@ void ModelOverlay::animate() { return; } - QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& fbxJoints = _animation->getGeometry().joints; + QStringList animationJointNames = _animation->getHFMModel().getJointNames(); + auto& hfmJoints = _animation->getHFMModel().joints; - auto& originalFbxJoints = _model->getFBXGeometry().joints; - auto& originalFbxIndices = _model->getFBXGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMModel().joints; + auto& originalHFMIndices = _model->getHFMModel().jointIndices; const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; const QVector& translations = frames[_lastKnownCurrentFrame].translations; @@ -626,23 +628,23 @@ void ModelOverlay::animate() { translationMat = glm::translate(translations[index]); } } else if (index < animationJointNames.size()) { - QString jointName = fbxJoints[index].name; + QString jointName = hfmJoints[index].name; - if (originalFbxIndices.contains(jointName)) { + if (originalHFMIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation. - int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalFbxJoints[remappedIndex].translation); + int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. + translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } } glm::mat4 rotationMat; if (index < rotations.size()) { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); } else { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); } - glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * - rotationMat * fbxJoints[index].postTransform); + glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * + rotationMat * hfmJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); jointData.translationIsDefaultPose = false; @@ -766,4 +768,4 @@ render::ItemKey ModelOverlay::getKey() { builder.withMetaCullGroup(); } return builder.build(); -} \ No newline at end of file +} diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index faa15ee2b4..1bf94adfa0 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -15,7 +15,7 @@ #include "Application.h" -const xColor Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; +const glm::u8vec3 Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; const float Overlay::DEFAULT_ALPHA = 0.7f; Overlay::Overlay() : @@ -57,7 +57,7 @@ Overlay::~Overlay() { void Overlay::setProperties(const QVariantMap& properties) { bool valid; - auto color = xColorFromVariant(properties["color"], valid); + auto color = u8vec3FromVariant(properties["color"], valid); if (valid) { _color = color; } @@ -116,7 +116,7 @@ QVariant Overlay::getProperty(const QString& property) { return QVariant(getType()); } if (property == "color") { - return xColorToVariant(_color); + return u8vec3ColortoVariant(_color); } if (property == "alpha") { return _alpha; @@ -143,21 +143,21 @@ QVariant Overlay::getProperty(const QString& property) { return QVariant(); } -xColor Overlay::getColor() { +glm::u8vec3 Overlay::getColor() { if (_colorPulse == 0.0f) { return _color; } float pulseLevel = updatePulse(); - xColor result = _color; + glm::u8vec3 result = _color; if (_colorPulse < 0.0f) { - result.red *= (1.0f - pulseLevel); - result.green *= (1.0f - pulseLevel); - result.blue *= (1.0f - pulseLevel); + result.x *= (1.0f - pulseLevel); + result.y *= (1.0f - pulseLevel); + result.z *= (1.0f - pulseLevel); } else { - result.red *= pulseLevel; - result.green *= pulseLevel; - result.blue *= pulseLevel; + result.x *= pulseLevel; + result.y *= pulseLevel; + result.z *= pulseLevel; } return result; } @@ -247,7 +247,7 @@ void Overlay::removeMaterial(graphics::MaterialPointer material, const std::stri } render::ItemKey Overlay::getKey() { - auto builder = render::ItemKey::Builder().withTypeShape(); + auto builder = render::ItemKey::Builder().withTypeShape().withTypeMeta(); builder.withViewSpace(); builder.withLayer(render::hifi::LAYER_2D); diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 45fc77a452..8e430f7e85 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -11,8 +11,6 @@ #ifndef hifi_Overlay_h #define hifi_Overlay_h -// include this before QGLWidget, which includes an earlier version of OpenGL -#include // for xColor #include class OverlayID : public QUuid { @@ -59,7 +57,7 @@ public: virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; }; virtual bool getIsVisibleInSecondaryCamera() const { return false; } - xColor getColor(); + glm::u8vec3 getColor(); float getAlpha(); float getPulseMax() const { return _pulseMax; } @@ -73,7 +71,7 @@ public: // setters virtual void setVisible(bool visible) { _visible = visible; } void setDrawHUDLayer(bool drawHUDLayer); - void setColor(const xColor& color) { _color = color; } + void setColor(const glm::u8vec3& color) { _color = color; } void setAlpha(float alpha) { _alpha = alpha; } void setPulseMax(float value) { _pulseMax = value; } @@ -115,12 +113,12 @@ protected: float _alphaPulse; // ratio of the pulse to the alpha float _colorPulse; // ratio of the pulse to the color - xColor _color; + glm::u8vec3 _color; bool _visible; // should the overlay be drawn at all unsigned int _stackOrder { 0 }; - static const xColor DEFAULT_OVERLAY_COLOR; + static const glm::u8vec3 DEFAULT_OVERLAY_COLOR; static const float DEFAULT_ALPHA; std::unordered_map _materials; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index de4ff94719..7593e12e07 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -35,6 +35,7 @@ #include "RectangleOverlay.h" #include "Text3DOverlay.h" #include "Web3DOverlay.h" +#include "ui/Keyboard.h" #include #include @@ -532,6 +533,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay bool visibleOnly, bool collidableOnly) { float bestDistance = std::numeric_limits::max(); bool bestIsFront = false; + bool bestIsTablet = false; + auto tabletIDs = qApp->getTabletIDs(); QMutexLocker locker(&_mutex); RayToOverlayIntersectionResult result; @@ -554,10 +557,11 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) { bool isDrawInFront = thisOverlay->getDrawInFront(); - if ((bestIsFront && isDrawInFront && thisDistance < bestDistance) - || (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) { - + bool isTablet = tabletIDs.contains(thisID); + if ((isDrawInFront && !bestIsFront && !bestIsTablet) + || ((isTablet || isDrawInFront || !bestIsFront) && thisDistance < bestDistance)) { bestIsFront = isDrawInFront; + bestIsTablet = isTablet; bestDistance = thisDistance; result.intersects = true; result.distance = thisDistance; @@ -579,7 +583,7 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con bool visibleOnly, bool collidableOnly) { float bestDistance = std::numeric_limits::max(); bool bestIsFront = false; - + const QVector keyboardKeysToDiscard = DependencyManager::get()->getKeysID(); QMutexLocker locker(&_mutex); ParabolaToOverlayIntersectionResult result; QMapIterator i(_overlaysWorld); @@ -589,7 +593,8 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con auto thisOverlay = std::dynamic_pointer_cast(i.value()); if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) || - (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) { + (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID)) || + (keyboardKeysToDiscard.size() > 0 && keyboardKeysToDiscard.contains(thisID))) { continue; } @@ -629,9 +634,9 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, obj.setProperty("distance", value.distance); obj.setProperty("face", boxFaceToString(value.face)); - QScriptValue intersection = vec3toScriptValue(engine, value.intersection); + QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal); + QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; @@ -828,40 +833,12 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay } -RayToOverlayIntersectionResult Overlays::findRayIntersectionForMouseEvent(PickRay ray) { - QVector overlaysToInclude; - QVector overlaysToDiscard; - RayToOverlayIntersectionResult rayPickResult; - - // first priority is tablet screen - overlaysToInclude << qApp->getTabletScreenID(); - rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard); - if (rayPickResult.intersects) { - return rayPickResult; - } - // then tablet home button - overlaysToInclude.clear(); - overlaysToInclude << qApp->getTabletHomeButtonID(); - rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard); - if (rayPickResult.intersects) { - return rayPickResult; - } - // then tablet frame - overlaysToInclude.clear(); - overlaysToInclude << OverlayID(qApp->getTabletFrameID()); - rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard); - if (rayPickResult.intersects) { - return rayPickResult; - } - // then whatever - return findRayIntersection(ray); -} - bool Overlays::mousePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mousePressEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; @@ -893,15 +870,21 @@ void Overlays::mousePressPointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mousePressOnOverlay(overlayID, event); + + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mousePressOnOverlay(overlayID, event); + } } bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; @@ -926,8 +909,12 @@ void Overlays::hoverEnterPointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "hoverEnterOverlay", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverEnterOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverEnterOverlay(overlayID, event); + } } void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { @@ -941,8 +928,12 @@ void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEv QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverOverOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverOverOverlay(overlayID, event); + } } void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { @@ -956,15 +947,20 @@ void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "hoverLeaveOverlay", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverLeaveOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverLeaveOverlay(overlayID, event); + } } bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseReleaseEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent); @@ -985,15 +981,20 @@ void Overlays::mouseReleasePointerEvent(const OverlayID& overlayID, const Pointe QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mouseReleaseOnOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mouseReleaseOnOverlay(overlayID, event); + } } bool Overlays::mouseMoveEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseMoveEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent); @@ -1036,8 +1037,13 @@ void Overlays::mouseMovePointerEvent(const OverlayID& overlayID, const PointerEv QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mouseMoveOnOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mouseMoveOnOverlay(overlayID, event); + } } QVector Overlays::findOverlays(const glm::vec3& center, float radius) { @@ -1056,8 +1062,8 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { i.next(); OverlayID thisID = i.key(); auto overlay = std::dynamic_pointer_cast(i.value()); - // FIXME: this ignores overlays with ignorePickIntersection == true, which seems wrong - if (overlay && overlay->getVisible() && !overlay->getIgnorePickIntersection() && overlay->isLoaded()) { + + if (overlay && overlay->getVisible() && overlay->isLoaded()) { // get AABox in frame of overlay glm::vec3 dimensions = overlay->getDimensions(); glm::vec3 low = dimensions * -0.5f; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 21b9e93648..208fc8d78d 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -44,8 +44,7 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); /**jsdoc - * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection} or - * {@link Overlays.findRayIntersectionVector|findRayIntersectionVector}. + * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection}. * @typedef {object} Overlays.RayToOverlayIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected with a 3D overlay, otherwise * false. @@ -383,7 +382,11 @@ public slots: OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties); /**jsdoc - * Find the closest 3D overlay intersected by a {@link PickRay}. + * Find the closest 3D overlay intersected by a {@link PickRay}. Overlays with their drawInFront property set + * to true have priority over overlays that don't, except that tablet overlays have priority over any + * drawInFront overlays behind them. I.e., if a drawInFront overlay is behind one that isn't + * drawInFront, the drawInFront overlay is returned, but if a tablet overlay is in front of a + * drawInFront overlay, the tablet overlay is returned. * @function Overlays.findRayIntersection * @param {PickRay} pickRay - The PickRay to use for finding overlays. * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. @@ -750,8 +753,6 @@ private: OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; - RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray); - private slots: void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index cf2691bb13..c33d3a0c39 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -64,7 +64,7 @@ void Planar3DOverlay::setProperties(const QVariantMap& properties) { */ QVariant Planar3DOverlay::getProperty(const QString& property) { if (property == "dimensions" || property == "scale" || property == "size") { - return vec2toVariant(getDimensions()); + return vec2ToVariant(getDimensions()); } return Base3DOverlay::getProperty(property); diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 48d89fab1c..73606c0467 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -51,9 +51,8 @@ void Rectangle3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 rectangleColor(toGlm(color), alpha); auto batch = args->_batch; if (batch) { diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index b0d3cf32af..b424424369 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -30,9 +30,8 @@ void Shape3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 shapeColor(toGlm(color), alpha); auto batch = args->_batch; if (batch) { @@ -44,9 +43,9 @@ void Shape3DOverlay::render(RenderArgs* args) { batch->setModelTransform(getRenderTransform()); if (_isSolid) { - geometryCache->renderSolidShapeInstance(args, *batch, _shape, cubeColor, shapePipeline); + geometryCache->renderSolidShapeInstance(args, *batch, _shape, shapeColor, shapePipeline); } else { - geometryCache->renderWireShapeInstance(args, *batch, _shape, cubeColor, shapePipeline); + geometryCache->renderWireShapeInstance(args, *batch, _shape, shapeColor, shapePipeline); } } } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 00a0dd686c..97294ae871 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -77,9 +77,8 @@ void Sphere3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 sphereColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 sphereColor(toGlm(color), alpha); auto batch = args->_batch; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 40e49ccefd..c8f8550e8e 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -64,21 +64,21 @@ void Text3DOverlay::setText(const QString& text) { _text = text; } -xColor Text3DOverlay::getBackgroundColor() { +glm::u8vec3 Text3DOverlay::getBackgroundColor() { if (_colorPulse == 0.0f) { return _backgroundColor; } float pulseLevel = updatePulse(); - xColor result = _backgroundColor; + glm::u8vec3 result = _backgroundColor; if (_colorPulse < 0.0f) { - result.red *= (1.0f - pulseLevel); - result.green *= (1.0f - pulseLevel); - result.blue *= (1.0f - pulseLevel); + result.x *= (1.0f - pulseLevel); + result.y *= (1.0f - pulseLevel); + result.z *= (1.0f - pulseLevel); } else { - result.red *= pulseLevel; - result.green *= pulseLevel; - result.blue *= pulseLevel; + result.x *= pulseLevel; + result.y *= pulseLevel; + result.z *= pulseLevel; } return result; } @@ -94,10 +94,8 @@ void Text3DOverlay::render(RenderArgs* args) { auto transform = getRenderTransform(); batch.setModelTransform(transform); - const float MAX_COLOR = 255.0f; - xColor backgroundColor = getBackgroundColor(); - glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, - backgroundColor.blue / MAX_COLOR, getBackgroundAlpha()); + glm::u8vec3 backgroundColor = getBackgroundColor(); + glm::vec4 quadColor(toGlm(backgroundColor), getBackgroundAlpha()); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; @@ -122,8 +120,7 @@ void Text3DOverlay::render(RenderArgs* args) { transform.setScale(scaleFactor); batch.setModelTransform(transform); - glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, - _color.blue / MAX_COLOR, getTextAlpha() }; + glm::vec4 textColor = { toGlm(_color), getTextAlpha() }; // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase. _textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), true); @@ -164,7 +161,7 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { bool valid; auto backgroundColor = properties["backgroundColor"]; if (backgroundColor.isValid()) { - auto color = xColorFromVariant(backgroundColor, valid); + auto color = u8vec3FromVariant(backgroundColor, valid); if (valid) { _backgroundColor = color; } @@ -260,7 +257,7 @@ QVariant Text3DOverlay::getProperty(const QString& property) { return _textAlpha; } if (property == "backgroundColor") { - return xColorToVariant(_backgroundColor); + return u8vec3ColortoVariant(_backgroundColor); } if (property == "backgroundAlpha") { return Billboard3DOverlay::getProperty("alpha"); diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 21163101d0..16bbdcb4c4 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -39,7 +39,7 @@ public: float getTopMargin() const { return _topMargin; } float getRightMargin() const { return _rightMargin; } float getBottomMargin() const { return _bottomMargin; } - xColor getBackgroundColor(); + glm::u8vec3 getBackgroundColor(); float getTextAlpha() { return _textAlpha; } float getBackgroundAlpha() { return getAlpha(); } bool isTransparent() override { return Overlay::isTransparent() || _textAlpha < 1.0f; } @@ -65,7 +65,7 @@ private: QString _text; mutable QMutex _mutex; // used to make get/setText threadsafe, mutable so can be used in const functions - xColor _backgroundColor = xColor { 0, 0, 0 }; + glm::u8vec3 _backgroundColor { 0, 0, 0 }; float _textAlpha { 1.0f }; float _lineHeight { 1.0f }; float _leftMargin { 0.1f }; diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index a307d445c0..0cceb44a36 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -20,11 +20,9 @@ Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : } AABox Volume3DOverlay::getBounds() const { - auto extents = Extents{_localBoundingBox}; - extents.rotate(getWorldOrientation()); - extents.shiftBy(getWorldPosition()); - - return AABox(extents); + AABox bounds = _localBoundingBox; + bounds.transform(getTransform()); + return bounds; } void Volume3DOverlay::setDimensions(const glm::vec3& value) { @@ -49,15 +47,7 @@ void Volume3DOverlay::setProperties(const QVariantMap& properties) { glm::vec3 scale = vec3FromVariant(dimensions); // don't allow a zero or negative dimension component to reach the renderTransform const float MIN_DIMENSION = 0.0001f; - if (scale.x < MIN_DIMENSION) { - scale.x = MIN_DIMENSION; - } - if (scale.y < MIN_DIMENSION) { - scale.y = MIN_DIMENSION; - } - if (scale.z < MIN_DIMENSION) { - scale.z = MIN_DIMENSION; - } + scale = glm::max(scale, MIN_DIMENSION); setDimensions(scale); } } diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index e4060ae335..2083f7344a 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -24,7 +24,6 @@ public: virtual AABox getBounds() const override; const glm::vec3& getDimensions() const { return _localBoundingBox.getDimensions(); } - void setDimensions(float value) { setDimensions(glm::vec3(value)); } void setDimensions(const glm::vec3& value); void setProperties(const QVariantMap& properties) override; @@ -37,7 +36,7 @@ public: protected: // Centered local bounding box - AABox _localBoundingBox{ vec3(0.0f), 1.0f }; + AABox _localBoundingBox { vec3(-0.5), 1.0f }; Transform evalRenderTransform() override; }; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 9d55c91ef3..e7a0c5934e 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -41,6 +41,7 @@ #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" +#include "scripting/KeyboardScriptingInterface.h" #include #include #include @@ -53,12 +54,14 @@ #include "ui/AvatarInputs.h" #include "avatar/AvatarManager.h" #include "scripting/AccountServicesScriptingInterface.h" +#include "scripting/WalletScriptingInterface.h" #include #include "ui/Snapshot.h" #include "SoundCacheScriptingInterface.h" #include "raypick/PointerScriptingInterface.h" #include #include "AboutUtil.h" +#include "ResourceRequestObserver.h" static int MAX_WINDOW_SIZE = 4096; static const float METERS_TO_INCHES = 39.3701f; @@ -269,6 +272,9 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) { _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("KeyboardScriptingInterface", DependencyManager::get().data()); // Override min fps for tablet UI, for silky smooth scrolling setMaxFPS(90); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f9195a608b..eeac8fadce 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,8 +101,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const FBXGeometry& geom = _networkAnim->getGeometry(); - AnimSkeleton animSkeleton(geom); + const HFMModel& hfmModel = _networkAnim->getHFMModel(); + AnimSkeleton animSkeleton(hfmModel); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; @@ -115,12 +115,12 @@ void AnimClip::copyFromNetworkAnim() { jointMap.push_back(skeletonJoint); } - const int frameCount = geom.animationFrames.size(); + const int frameCount = hfmModel.animationFrames.size(); _anim.resize(frameCount); for (int frame = 0; frame < frameCount; frame++) { - const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + const HFMAnimationFrame& hfmAnimFrame = hfmModel.animationFrames[frame]; // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. @@ -132,8 +132,8 @@ void AnimClip::copyFromNetworkAnim() { for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; - const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint]; - const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint]; + const glm::vec3& hfmAnimTrans = hfmAnimFrame.translations[animJoint]; + const glm::quat& hfmAnimRot = hfmAnimFrame.rotations[animJoint]; // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { @@ -146,19 +146,19 @@ void AnimClip::copyFromNetworkAnim() { preRot.scale() = glm::vec3(1.0f); postRot.scale() = glm::vec3(1.0f); - AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3()); + AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3()); // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; + const glm::vec3& hfmZeroTrans = hfmModel.animationFrames[0].translations[animJoint]; const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; - if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) { - boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(fbxZeroTrans); + if (fabsf(glm::length(hfmZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans); } - AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); + AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans)); _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index eba361fd4c..92f4c01dcc 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -51,6 +51,8 @@ public: bool getMirrorFlag() const { return _mirrorFlag; } void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; } + float getFrame() const { return _frame; } + void loadURL(const QString& url); protected: diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 71094cc6e1..a1809f3438 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -24,7 +24,7 @@ #include "AnimUtil.h" static const int MAX_TARGET_MARKERS = 30; -static const float JOINT_CHAIN_INTERP_TIME = 0.25f; +static const float JOINT_CHAIN_INTERP_TIME = 0.5f; static void lookupJointInfo(const AnimInverseKinematics::JointChainInfo& jointChainInfo, int indexA, int indexB, @@ -253,11 +253,25 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< if (numLoops == MAX_IK_LOOPS) { for (size_t i = 0; i < _prevJointChainInfoVec.size(); i++) { if (_prevJointChainInfoVec[i].timer > 0.0f) { + float alpha = (JOINT_CHAIN_INTERP_TIME - _prevJointChainInfoVec[i].timer) / JOINT_CHAIN_INTERP_TIME; + + // ease in expo + alpha = 1.0f - powf(2.0f, -10.0f * alpha); + size_t chainSize = std::min(_prevJointChainInfoVec[i].jointInfoVec.size(), jointChainInfoVec[i].jointInfoVec.size()); - for (size_t j = 0; j < chainSize; j++) { - jointChainInfoVec[i].jointInfoVec[j].rot = safeMix(_prevJointChainInfoVec[i].jointInfoVec[j].rot, jointChainInfoVec[i].jointInfoVec[j].rot, alpha); - jointChainInfoVec[i].jointInfoVec[j].trans = lerp(_prevJointChainInfoVec[i].jointInfoVec[j].trans, jointChainInfoVec[i].jointInfoVec[j].trans, alpha); + + if (jointChainInfoVec[i].target.getType() != IKTarget::Type::Unknown) { + // if we are interping into an enabled target type, i.e. not off, lerp the rot and the trans. + for (size_t j = 0; j < chainSize; j++) { + jointChainInfoVec[i].jointInfoVec[j].rot = safeMix(_prevJointChainInfoVec[i].jointInfoVec[j].rot, jointChainInfoVec[i].jointInfoVec[j].rot, alpha); + jointChainInfoVec[i].jointInfoVec[j].trans = lerp(_prevJointChainInfoVec[i].jointInfoVec[j].trans, jointChainInfoVec[i].jointInfoVec[j].trans, alpha); + } + } else { + // if we are interping into a disabled target type, keep the rot & trans the same, but lerp the weight down to zero. + jointChainInfoVec[i].target.setType((int)_prevJointChainInfoVec[i].target.getType()); + jointChainInfoVec[i].target.setWeight(_prevJointChainInfoVec[i].target.getWeight() * (1.0f - alpha)); + jointChainInfoVec[i].jointInfoVec = _prevJointChainInfoVec[i].jointInfoVec; } } } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bed9c590be..73a0891fbe 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,17 +16,17 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { +AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // convert to std::vector of joints - std::vector joints; - joints.reserve(fbxGeometry.joints.size()); - for (auto& joint : fbxGeometry.joints) { + std::vector joints; + joints.reserve(hfmModel.joints.size()); + for (auto& joint : hfmModel.joints) { joints.push_back(joint); } buildSkeletonFromJoints(joints); } -AnimSkeleton::AnimSkeleton(const std::vector& joints) { +AnimSkeleton::AnimSkeleton(const std::vector& joints) { buildSkeletonFromJoints(joints); } @@ -166,7 +166,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } -void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses @@ -177,7 +177,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) _relativePreRotationPoses.reserve(_jointsSize); _relativePostRotationPoses.reserve(_jointsSize); - // iterate over FBXJoints and extract the bind pose information. + // iterate over HFMJoints and extract the bind pose information. for (int i = 0; i < _jointsSize; i++) { // build pre and post transforms @@ -240,7 +240,7 @@ void AnimSkeleton::dump(bool verbose) const { qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); if (verbose) { - qCDebug(animation) << " fbxJoint ="; + qCDebug(animation) << " hfmJoint ="; qCDebug(animation) << " isFree =" << _joints[i].isFree; qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 2ebf3f4f5d..3a384388d0 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,8 +23,8 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const FBXGeometry& fbxGeometry); - explicit AnimSkeleton(const std::vector& joints); + explicit AnimSkeleton(const HFMModel& hfmModel); + explicit AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -64,9 +64,9 @@ public: std::vector lookUpJointIndices(const std::vector& jointNames) const; protected: - void buildSkeletonFromJoints(const std::vector& joints); + void buildSkeletonFromJoints(const std::vector& joints); - std::vector _joints; + std::vector _joints; int _jointsSize { 0 }; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; diff --git a/libraries/animation/src/AnimTwoBoneIK.cpp b/libraries/animation/src/AnimTwoBoneIK.cpp index d68240d176..8960b15940 100644 --- a/libraries/animation/src/AnimTwoBoneIK.cpp +++ b/libraries/animation/src/AnimTwoBoneIK.cpp @@ -201,14 +201,17 @@ const AnimPoseVec& AnimTwoBoneIK::evaluate(const AnimVariantMap& animVars, const if (_interpType != InterpType::None) { _interpAlpha += _interpAlphaVel * dt; + // ease in expo + float easeInAlpha = 1.0f - powf(2.0f, -10.0f * _interpAlpha); + if (_interpAlpha < 1.0f) { AnimChain interpChain; if (_interpType == InterpType::SnapshotToUnderPoses) { interpChain = underChain; - interpChain.blend(_snapshotChain, _interpAlpha); + interpChain.blend(_snapshotChain, easeInAlpha); } else if (_interpType == InterpType::SnapshotToSolve) { interpChain = ikChain; - interpChain.blend(_snapshotChain, _interpAlpha); + interpChain.blend(_snapshotChain, easeInAlpha); } // copy interpChain into _poses interpChain.outputRelativePoses(_poses); diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 9300f1a7a0..cf190e8dbf 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -38,4 +38,94 @@ AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone); // and returns a bodyRot that is also z-forward and y-up glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& up); + +// Uses a approximation of a critically damped spring to smooth full AnimPoses. +// It provides seperate timescales for horizontal, vertical and rotation components. +// The timescale is roughly how much time it will take the spring will reach halfway toward it's target. +class CriticallyDampedSpringPoseHelper { +public: + CriticallyDampedSpringPoseHelper() : _prevPoseValid(false) {} + + void setHorizontalTranslationTimescale(float timescale) { + _horizontalTranslationTimescale = timescale; + } + void setVerticalTranslationTimescale(float timescale) { + _verticalTranslationTimescale = timescale; + } + void setRotationTimescale(float timescale) { + _rotationTimescale = timescale; + } + + AnimPose update(const AnimPose& pose, float deltaTime) { + if (!_prevPoseValid) { + _prevPose = pose; + _prevPoseValid = true; + } + + const float horizontalTranslationAlpha = glm::min(deltaTime / _horizontalTranslationTimescale, 1.0f); + const float verticalTranslationAlpha = glm::min(deltaTime / _verticalTranslationTimescale, 1.0f); + const float rotationAlpha = glm::min(deltaTime / _rotationTimescale, 1.0f); + + const float poseY = pose.trans().y; + AnimPose newPose = _prevPose; + newPose.trans() = lerp(_prevPose.trans(), pose.trans(), horizontalTranslationAlpha); + newPose.trans().y = lerp(_prevPose.trans().y, poseY, verticalTranslationAlpha); + newPose.rot() = safeLerp(_prevPose.rot(), pose.rot(), rotationAlpha); + + _prevPose = newPose; + _prevPoseValid = true; + + return newPose; + } + + void teleport(const AnimPose& pose) { + _prevPoseValid = true; + _prevPose = pose; + } + +protected: + AnimPose _prevPose; + float _horizontalTranslationTimescale { 0.15f }; + float _verticalTranslationTimescale { 0.15f }; + float _rotationTimescale { 0.15f }; + bool _prevPoseValid; +}; + +class SnapshotBlendPoseHelper { +public: + SnapshotBlendPoseHelper() : _snapshotValid(false) {} + + void setBlendDuration(float duration) { + _duration = duration; + } + + void setSnapshot(const AnimPose& pose) { + _snapshotValid = true; + _snapshotPose = pose; + _timer = _duration; + } + + AnimPose update(const AnimPose& targetPose, float deltaTime) { + _timer -= deltaTime; + if (_timer > 0.0f) { + float alpha = (_duration - _timer) / _duration; + + // ease in expo + alpha = 1.0f - powf(2.0f, -10.0f * alpha); + + AnimPose newPose = targetPose; + newPose.blend(_snapshotPose, alpha); + return newPose; + } else { + return targetPose; + } + } + +protected: + AnimPose _snapshotPose; + float _duration { 1.0f }; + float _timer { 0.0f }; + bool _snapshotValid { false }; +}; + #endif diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 21fe234f7b..cf7228b27b 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -40,7 +40,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, target.setProperty(name, value.getString()); break; case AnimVariant::Type::Vec3: - target.setProperty(name, vec3toScriptValue(engine, value.getVec3())); + target.setProperty(name, vec3ToScriptValue(engine, value.getVec3())); break; case AnimVariant::Type::Quat: target.setProperty(name, quatToScriptValue(engine, value.getQuat())); @@ -169,7 +169,8 @@ std::map AnimVariantMap::toDebugMap() const { break; */ default: - assert(("invalid AnimVariant::Type", false)); + // invalid AnimVariant::Type + assert(false); } } return result; diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index f30d5605d7..06dfc0262a 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -32,12 +32,6 @@ AnimationCache::AnimationCache(QObject* parent) : } AnimationPointer AnimationCache::getAnimation(const QUrl& url) { - if (QThread::currentThread() != thread()) { - AnimationPointer result; - BLOCKING_INVOKE_METHOD(this, "getAnimation", - Q_RETURN_ARG(AnimationPointer, result), Q_ARG(const QUrl&, url)); - return result; - } return getResource(url).staticCast(); } @@ -75,14 +69,14 @@ void AnimationReader::run() { if (urlValid) { // Parse the FBX directly from the QNetworkReply - FBXGeometry::Pointer fbxgeo; + HFMModel::Pointer hfmModel; if (_url.path().toLower().endsWith(".fbx")) { - fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path())); + hfmModel.reset(readFBX(_data, QVariantHash(), _url.path())); } else { QString errorStr("usupported format"); emit onError(299, errorStr); } - emit onSuccess(fbxgeo); + emit onSuccess(hfmModel); } else { throw QString("url is invalid"); } @@ -94,7 +88,7 @@ void AnimationReader::run() { } bool Animation::isLoaded() const { - return _loaded && _geometry; + return _loaded && _hfmModel; } QStringList Animation::getJointNames() const { @@ -105,45 +99,45 @@ QStringList Animation::getJointNames() const { return result; } QStringList names; - if (_geometry) { - foreach (const FBXJoint& joint, _geometry->joints) { + if (_hfmModel) { + foreach (const HFMJoint& joint, _hfmModel->joints) { names.append(joint.name); } } return names; } -QVector Animation::getFrames() const { +QVector Animation::getFrames() const { if (QThread::currentThread() != thread()) { - QVector result; + QVector result; BLOCKING_INVOKE_METHOD(const_cast(this), "getFrames", - Q_RETURN_ARG(QVector, result)); + Q_RETURN_ARG(QVector, result)); return result; } - if (_geometry) { - return _geometry->animationFrames; + if (_hfmModel) { + return _hfmModel->animationFrames; } else { - return QVector(); + return QVector(); } } -const QVector& Animation::getFramesReference() const { - return _geometry->animationFrames; +const QVector& Animation::getFramesReference() const { + return _hfmModel->animationFrames; } void Animation::downloadFinished(const QByteArray& data) { // parse the animation/fbx file on a background thread. AnimationReader* animationReader = new AnimationReader(_url, data); - connect(animationReader, SIGNAL(onSuccess(FBXGeometry::Pointer)), SLOT(animationParseSuccess(FBXGeometry::Pointer))); + connect(animationReader, SIGNAL(onSuccess(HFMModel::Pointer)), SLOT(animationParseSuccess(HFMModel::Pointer))); connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString))); QThreadPool::globalInstance()->start(animationReader); } -void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) { +void Animation::animationParseSuccess(HFMModel::Pointer hfmModel) { qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); - _geometry = geometry; + _hfmModel = hfmModel; finishedLoading(true); } diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index ca5ea5b072..4423e8f18d 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -22,7 +22,7 @@ class Animation; -typedef QSharedPointer AnimationPointer; +using AnimationPointer = QSharedPointer; class AnimationCache : public ResourceCache, public Dependency { Q_OBJECT @@ -66,7 +66,7 @@ public: QString getType() const override { return "Animation"; } - const FBXGeometry& getGeometry() const { return *_geometry; } + const HFMModel& getHFMModel() const { return *_hfmModel; } virtual bool isLoaded() const override; @@ -80,20 +80,20 @@ public: * @function AnimationObject.getFrames * @returns {FBXAnimationFrame[]} */ - Q_INVOKABLE QVector getFrames() const; + Q_INVOKABLE QVector getFrames() const; - const QVector& getFramesReference() const; + const QVector& getFramesReference() const; protected: virtual void downloadFinished(const QByteArray& data) override; protected slots: - void animationParseSuccess(FBXGeometry::Pointer geometry); + void animationParseSuccess(HFMModel::Pointer hfmModel); void animationParseError(int error, QString str); private: - FBXGeometry::Pointer _geometry; + HFMModel::Pointer _hfmModel; }; /// Reads geometry in a worker thread. @@ -105,7 +105,7 @@ public: virtual void run() override; signals: - void onSuccess(FBXGeometry::Pointer geometry); + void onSuccess(HFMModel::Pointer hfmModel); void onError(int error, QString str); private: diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp index 7f0f35b104..bcbf497199 100644 --- a/libraries/animation/src/AnimationObject.cpp +++ b/libraries/animation/src/AnimationObject.cpp @@ -19,17 +19,17 @@ QStringList AnimationObject::getJointNames() const { return qscriptvalue_cast(thisObject())->getJointNames(); } -QVector AnimationObject::getFrames() const { +QVector AnimationObject::getFrames() const { return qscriptvalue_cast(thisObject())->getFrames(); } QVector AnimationFrameObject::getRotations() const { - return qscriptvalue_cast(thisObject()).rotations; + return qscriptvalue_cast(thisObject()).rotations; } void registerAnimationTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( + qScriptRegisterSequenceMetaType >(engine); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( new AnimationFrameObject(), QScriptEngine::ScriptOwnership)); engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( new AnimationObject(), QScriptEngine::ScriptOwnership)); diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index aa69e78ceb..83880ed2ab 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -23,13 +23,13 @@ class QScriptEngine; class AnimationObject : public QObject, protected QScriptable { Q_OBJECT Q_PROPERTY(QStringList jointNames READ getJointNames) - Q_PROPERTY(QVector frames READ getFrames) + Q_PROPERTY(QVector frames READ getFrames) public: Q_INVOKABLE QStringList getJointNames() const; - Q_INVOKABLE QVector getFrames() const; + Q_INVOKABLE QVector getFrames() const; }; /// Scriptable wrapper for animation frames. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 91d4e0f9d3..87f03f8bac 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -30,7 +30,9 @@ #include "AnimOverlay.h" #include "AnimSkeleton.h" #include "AnimUtil.h" +#include "AvatarConstants.h" #include "IKTarget.h" +#include "PathUtils.h" static int nextRigId = 1; @@ -144,6 +146,90 @@ void Rig::restoreAnimation() { } } +void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { + + NetworkAnimState::ClipNodeEnum clipNodeEnum = NetworkAnimState::None; + if (_networkAnimState.clipNodeEnum == NetworkAnimState::None || _networkAnimState.clipNodeEnum == NetworkAnimState::B) { + clipNodeEnum = NetworkAnimState::A; + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::A) { + clipNodeEnum = NetworkAnimState::B; + } + + if (_networkNode) { + // find an unused AnimClip clipNode + std::shared_ptr clip; + if (clipNodeEnum == NetworkAnimState::A) { + clip = std::dynamic_pointer_cast(_networkNode->findByName("userNetworkAnimA")); + } else { + clip = std::dynamic_pointer_cast(_networkNode->findByName("userNetworkAnimB")); + } + if (clip) { + // set parameters + clip->setLoopFlag(loop); + clip->setStartFrame(firstFrame); + clip->setEndFrame(lastFrame); + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; + clip->setTimeScale(timeScale); + clip->loadURL(url); + } + } + + // store current user anim state. + _networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; + + // notify the userAnimStateMachine the desired state. + _networkVars.set("transitAnimStateMachine", false); + _networkVars.set("userNetworkAnimA", clipNodeEnum == NetworkAnimState::A); + _networkVars.set("userNetworkAnimB", clipNodeEnum == NetworkAnimState::B); + if (!_computeNetworkAnimation) { + _networkAnimState.blendTime = 0.0f; + _computeNetworkAnimation = true; + } +} + +void Rig::triggerNetworkRole(const QString& role) { + _networkVars.set("transitAnimStateMachine", false); + _networkVars.set("idleAnim", false); + _networkVars.set("userNetworkAnimA", false); + _networkVars.set("userNetworkAnimB", false); + _networkVars.set("preTransitAnim", false); + _networkVars.set("preTransitAnim", false); + _networkVars.set("transitAnim", false); + _networkVars.set("postTransitAnim", false); + _computeNetworkAnimation = true; + if (role == "idleAnim") { + _networkVars.set("idleAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::None; + _computeNetworkAnimation = false; + _networkAnimState.blendTime = 0.0f; + } else if (role == "preTransitAnim") { + _networkVars.set("preTransitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::PreTransit; + _networkAnimState.blendTime = 0.0f; + } else if (role == "transitAnim") { + _networkVars.set("transitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::Transit; + } else if (role == "postTransitAnim") { + _networkVars.set("postTransitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::PostTransit; + } + +} + +void Rig::restoreNetworkAnimation() { + if (_networkAnimState.clipNodeEnum != NetworkAnimState::None) { + if (_computeNetworkAnimation) { + _networkAnimState.blendTime = 0.0f; + _computeNetworkAnimation = false; + } + _networkAnimState.clipNodeEnum = NetworkAnimState::None; + _networkVars.set("transitAnimStateMachine", true); + _networkVars.set("userNetworkAnimA", false); + _networkVars.set("userNetworkAnimB", false); + } +} + QStringList Rig::getAnimationRoles() const { if (_animNode) { QStringList list; @@ -208,57 +294,73 @@ void Rig::restoreRoleAnimation(const QString& role) { void Rig::destroyAnimGraph() { _animSkeleton.reset(); _animLoader.reset(); + _networkLoader.reset(); _animNode.reset(); _internalPoseSet._relativePoses.clear(); _internalPoseSet._absolutePoses.clear(); _internalPoseSet._overridePoses.clear(); _internalPoseSet._overrideFlags.clear(); + _networkNode.reset(); + _networkPoseSet._relativePoses.clear(); + _networkPoseSet._absolutePoses.clear(); + _networkPoseSet._overridePoses.clear(); + _networkPoseSet._overrideFlags.clear(); _numOverrides = 0; _leftEyeJointChildren.clear(); _rightEyeJointChildren.clear(); } -void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { - _geometryOffset = AnimPose(geometry.offset); +void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset) { + _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); - _geometryToRigTransform = modelOffset * geometry.offset; + _geometryToRigTransform = modelOffset * hfmModel.offset; _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + _networkPoseSet._relativePoses.clear(); + _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); _internalPoseSet._overridePoses.clear(); _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); _internalPoseSet._overrideFlags.clear(); _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + + _networkPoseSet._overridePoses.clear(); + _networkPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + + _networkPoseSet._overrideFlags.clear(); + _networkPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + _numOverrides = 0; buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); - _rootJointIndex = geometry.rootJointIndex; - _leftEyeJointIndex = geometry.leftEyeJointIndex; - _rightEyeJointIndex = geometry.rightEyeJointIndex; - _leftHandJointIndex = geometry.leftHandJointIndex; - _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; - _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; - _rightHandJointIndex = geometry.rightHandJointIndex; - _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; - _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + _rootJointIndex = hfmModel.rootJointIndex; + _leftEyeJointIndex = hfmModel.leftEyeJointIndex; + _rightEyeJointIndex = hfmModel.rightEyeJointIndex; + _leftHandJointIndex = hfmModel.leftHandJointIndex; + _leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1; + _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1; + _rightHandJointIndex = hfmModel.rightHandJointIndex; + _rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1; + _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1; - _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex); - _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex); } -void Rig::reset(const FBXGeometry& geometry) { - _geometryOffset = AnimPose(geometry.offset); +void Rig::reset(const HFMModel& hfmModel) { + _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -270,22 +372,34 @@ void Rig::reset(const FBXGeometry& geometry) { _internalPoseSet._overrideFlags.clear(); _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + + _networkPoseSet._relativePoses.clear(); + _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); + + _networkPoseSet._overridePoses.clear(); + _networkPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + + _networkPoseSet._overrideFlags.clear(); + _networkPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + _numOverrides = 0; buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); - _rootJointIndex = geometry.rootJointIndex; - _leftEyeJointIndex = geometry.leftEyeJointIndex; - _rightEyeJointIndex = geometry.rightEyeJointIndex; - _leftHandJointIndex = geometry.leftHandJointIndex; - _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; - _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; - _rightHandJointIndex = geometry.rightHandJointIndex; - _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; - _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + _rootJointIndex = hfmModel.rootJointIndex; + _leftEyeJointIndex = hfmModel.leftEyeJointIndex; + _rightEyeJointIndex = hfmModel.rightEyeJointIndex; + _leftHandJointIndex = hfmModel.leftHandJointIndex; + _leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1; + _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1; + _rightHandJointIndex = hfmModel.rightHandJointIndex; + _rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1; + _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1; - _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex); - _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex); if (!_animGraphURL.isEmpty()) { _animNode.reset(); @@ -629,7 +743,8 @@ bool Rig::getRelativeDefaultJointTranslation(int index, glm::vec3& translationOu } } -void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { +void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, + const glm::quat& worldRotation, CharacterControllerState ccState, float sensorToWorldScale) { glm::vec3 forward = worldRotation * IDENTITY_FORWARD; glm::vec3 workingVelocity = worldVelocity; @@ -924,9 +1039,15 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } _animVars.set("isNotInAir", false); - // compute blend based on velocity - const float JUMP_SPEED = 3.5f; - float alpha = glm::clamp(-workingVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f; + // We want to preserve the apparent jump height in sensor space. + const float jumpHeight = std::max(sensorToWorldScale * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); + + // convert jump height to a initial jump speed with the given gravity. + const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); + + // compute inAirAlpha blend based on velocity + float alpha = glm::clamp((-workingVelocity.y * sensorToWorldScale) / jumpSpeed, -1.0f, 1.0f) + 1.0f; + _animVars.set("inAirAlpha", alpha); } @@ -1049,26 +1170,51 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); - + if (_networkNode) { + _networkVars.setRigToGeometryTransform(_rigToGeometryTransform); + } AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains, getGeometryToRigTransform(), rigToWorldTransform); // evaluate the animation AnimVariantMap triggersOut; - + AnimVariantMap networkTriggersOut; _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); + if (_networkNode) { + // Manually blending networkPoseSet with internalPoseSet. + float alpha = 1.0f; + const float FRAMES_PER_SECOND = 30.0f; + const float TOTAL_BLEND_FRAMES = 6.0f; + const float TOTAL_BLEND_TIME = TOTAL_BLEND_FRAMES / FRAMES_PER_SECOND; + _sendNetworkNode = _computeNetworkAnimation || _networkAnimState.blendTime < TOTAL_BLEND_TIME; + if (_sendNetworkNode) { + _networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut); + _networkAnimState.blendTime += deltaTime; + alpha = _computeNetworkAnimation ? (_networkAnimState.blendTime / TOTAL_BLEND_TIME) : (1.0f - (_networkAnimState.blendTime / TOTAL_BLEND_TIME)); + alpha = glm::clamp(alpha, 0.0f, 1.0f); + for (size_t i = 0; i < _networkPoseSet._relativePoses.size(); i++) { + _networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], alpha); + } + } + } if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); } + if ((int)_networkPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { + // animations haven't fully loaded yet. + _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + } _lastAnimVars = _animVars; _animVars.clearTriggers(); _animVars = triggersOut; + _networkVars.clearTriggers(); + _networkVars = networkTriggersOut; _lastContext = context; } applyOverridePoses(); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); - + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); // copy internal poses to external poses { QWriteLocker writeLock(&_externalPoseSetLock); @@ -1152,7 +1298,7 @@ const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { // returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. // if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward // such that it lies on the surface of the kdop. -static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const FBXJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { +static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { // transform point into local space of jointShape. glm::vec3 localPoint = shapePose.inverse().xformPoint(point); @@ -1198,8 +1344,8 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh } } -glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const { +glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const { glm::vec3 position = handPosition; glm::vec3 displacement; int hipsJoint = indexOfJoint("Hips"); @@ -1248,12 +1394,11 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, - const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo, + const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) { const bool ENABLE_POLE_VECTORS = true; - const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f; if (leftHandEnabled) { @@ -1279,33 +1424,16 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector); if (usePoleVector) { glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector); - - if (_smoothPoleVectors) { - // smooth toward desired pole vector from previous pole vector... to reduce jitter - if (!_prevLeftHandPoleVectorValid) { - _prevLeftHandPoleVectorValid = true; - _prevLeftHandPoleVector = sensorPoleVector; - } - glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, sensorPoleVector); - glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR); - _prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector; - } else { - _prevLeftHandPoleVector = sensorPoleVector; - } _animVars.set("leftHandPoleVectorEnabled", true); _animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X); - _animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftHandPoleVector)); + _animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, sensorPoleVector)); } else { - _prevLeftHandPoleVectorValid = false; _animVars.set("leftHandPoleVectorEnabled", false); } - } else { - _prevLeftHandPoleVectorValid = false; _animVars.set("leftHandPoleVectorEnabled", false); } } else { - _prevLeftHandPoleVectorValid = false; _animVars.set("leftHandPoleVectorEnabled", false); _animVars.unset("leftHandPosition"); @@ -1344,33 +1472,16 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector); if (usePoleVector) { glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector); - - if (_smoothPoleVectors) { - // smooth toward desired pole vector from previous pole vector... to reduce jitter - if (!_prevRightHandPoleVectorValid) { - _prevRightHandPoleVectorValid = true; - _prevRightHandPoleVector = sensorPoleVector; - } - glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, sensorPoleVector); - glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR); - _prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector; - } else { - _prevRightHandPoleVector = sensorPoleVector; - } - _animVars.set("rightHandPoleVectorEnabled", true); _animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X); - _animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightHandPoleVector)); + _animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, sensorPoleVector)); } else { - _prevRightHandPoleVectorValid = false; _animVars.set("rightHandPoleVectorEnabled", false); } } else { - _prevRightHandPoleVectorValid = false; _animVars.set("rightHandPoleVectorEnabled", false); } } else { - _prevRightHandPoleVectorValid = false; _animVars.set("rightHandPoleVectorEnabled", false); _animVars.unset("rightHandPosition"); @@ -1609,6 +1720,7 @@ glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int up void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) { if (!_animSkeleton || !_animNode) { + _previousControllerParameters = params; return; } @@ -1619,7 +1731,9 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled; bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled; bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled; + bool prevHipsEnabled = _previousControllerParameters.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled; bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated; + bool prevHipsEstimated = _previousControllerParameters.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated; bool leftFootEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftFoot] & (uint8_t)ControllerFlags::Enabled; bool rightFootEnabled = params.primaryControllerFlags[PrimaryControllerType_RightFoot] & (uint8_t)ControllerFlags::Enabled; bool spine2Enabled = params.primaryControllerFlags[PrimaryControllerType_Spine2] & (uint8_t)ControllerFlags::Enabled; @@ -1658,9 +1772,26 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo } if (hipsEnabled) { + + // Apply a bit of smoothing when the hips toggle between estimated and non-estimated poses. + // This should help smooth out problems with the vive tracker when the sensor is occluded. + if (prevHipsEnabled && hipsEstimated != prevHipsEstimated) { + // blend from a snapshot of the previous hips. + const float HIPS_BLEND_DURATION = 0.5f; + _hipsBlendHelper.setBlendDuration(HIPS_BLEND_DURATION); + _hipsBlendHelper.setSnapshot(_previousControllerParameters.primaryControllerPoses[PrimaryControllerType_Hips]); + } else if (!prevHipsEnabled) { + // we have no sensible value to blend from. + const float HIPS_BLEND_DURATION = 0.0f; + _hipsBlendHelper.setBlendDuration(HIPS_BLEND_DURATION); + _hipsBlendHelper.setSnapshot(params.primaryControllerPoses[PrimaryControllerType_Hips]); + } + + AnimPose hips = _hipsBlendHelper.update(params.primaryControllerPoses[PrimaryControllerType_Hips], dt); + _animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("hipsPosition", params.primaryControllerPoses[PrimaryControllerType_Hips].trans()); - _animVars.set("hipsRotation", params.primaryControllerPoses[PrimaryControllerType_Hips].rot()); + _animVars.set("hipsPosition", hips.trans()); + _animVars.set("hipsRotation", hips.rot()); } else { _animVars.set("hipsType", (int)IKTarget::Type::Unknown); } @@ -1700,6 +1831,8 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo } } } + + _previousControllerParameters = params; } void Rig::initAnimGraph(const QUrl& url) { @@ -1707,11 +1840,14 @@ void Rig::initAnimGraph(const QUrl& url) { _animGraphURL = url; _animNode.reset(); + _networkNode.reset(); // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); + auto networkUrl = PathUtils::resourcesUrl("avatar/network-animation.json"); + _networkLoader.reset(new AnimNodeLoader(networkUrl)); std::weak_ptr weakSkeletonPtr = _animSkeleton; - connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { + connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, url](AnimNode::Pointer nodeIn) { _animNode = nodeIn; // abort load if the previous skeleton was deleted. @@ -1738,7 +1874,33 @@ void Rig::initAnimGraph(const QUrl& url) { emit onLoadComplete(); }); connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { - qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; + qCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; + }); + + connect(_networkLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, networkUrl](AnimNode::Pointer nodeIn) { + _networkNode = nodeIn; + // abort load if the previous skeleton was deleted. + auto sharedSkeletonPtr = weakSkeletonPtr.lock(); + if (!sharedSkeletonPtr) { + return; + } + _networkNode->setSkeleton(sharedSkeletonPtr); + if (_networkAnimState.clipNodeEnum != NetworkAnimState::None) { + // restore the user animation we had before reset. + NetworkAnimState origState = _networkAnimState; + _networkAnimState = { NetworkAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; + if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) { + triggerNetworkRole("preTransitAnim"); + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::Transit) { + triggerNetworkRole("transitAnim"); + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) { + triggerNetworkRole("postTransitAnim"); + } + } + + }); + connect(_networkLoader.get(), &AnimNodeLoader::error, [networkUrl](int error, QString str) { + qCritical(animation) << "Error loading" << networkUrl.toDisplayString() << "code = " << error << "str =" << str; }); } } @@ -1817,13 +1979,13 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { if (isIndexValid(i)) { // rotations are in absolute rig frame. glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot(); - data.rotation = _internalPoseSet._absolutePoses[i].rot(); + data.rotation = !_sendNetworkNode ? _internalPoseSet._absolutePoses[i].rot() : _networkPoseSet._absolutePoses[i].rot(); data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot); // translations are in relative frame but scaled so that they are in meters, - // instead of geometry units. + // instead of model units. glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans(); - data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans(); + data.translation = _geometryOffset.scale() * (!_sendNetworkNode ? _internalPoseSet._relativePoses[i].trans() : _networkPoseSet._relativePoses[i].trans()); data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans); } else { data.translationIsDefaultPose = true; @@ -1846,7 +2008,7 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { return; } - // make a vector of rotations in absolute-geometry-frame + // make a vector of rotations in absolute-model-frame std::vector rotations; rotations.reserve(numJoints); const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); @@ -1855,7 +2017,7 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { if (data.rotationIsDefaultPose) { rotations.push_back(absoluteDefaultPoses[i].rot()); } else { - // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame + // JointData rotations are in absolute rig-frame so we rotate them to absolute model-frame rotations.push_back(rigToGeometryRot * data.rotation); } } @@ -1891,7 +2053,7 @@ void Rig::computeExternalPoses(const glm::mat4& modelOffsetMat) { } void Rig::computeAvatarBoundingCapsule( - const FBXGeometry& geometry, + const HFMModel& hfmModel, float& radiusOut, float& heightOut, glm::vec3& localOffsetOut) const { @@ -1924,7 +2086,7 @@ void Rig::computeAvatarBoundingCapsule( // from the head to the hips when computing the rest of the bounding capsule. int index = indexOfJoint("Head"); while (index != -1) { - const FBXJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo; + const HFMJointShapeInfo& shapeInfo = hfmModel.joints.at(index).shapeInfo; AnimPose pose = _animSkeleton->getAbsoluteDefaultPose(index); if (shapeInfo.points.size() > 0) { for (auto& point : shapeInfo.points) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 48f00d4e5d..41c25a3c3e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -24,6 +24,7 @@ #include "AnimNode.h" #include "AnimNodeLoader.h" #include "SimpleMovingAverage.h" +#include "AnimUtil.h" class Rig; class AnimInverseKinematics; @@ -85,10 +86,10 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; - FBXJointShapeInfo hipsShapeInfo; - FBXJointShapeInfo spineShapeInfo; - FBXJointShapeInfo spine1ShapeInfo; - FBXJointShapeInfo spine2ShapeInfo; + HFMJointShapeInfo hipsShapeInfo; + HFMJointShapeInfo spineShapeInfo; + HFMJointShapeInfo spine1ShapeInfo; + HFMJointShapeInfo spine2ShapeInfo; }; struct EyeParameters { @@ -114,12 +115,17 @@ public: void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreAnimation(); + + void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void triggerNetworkRole(const QString& role); + void restoreNetworkAnimation(); + QStringList getAnimationRoles() const; void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); - void initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset); - void reset(const FBXGeometry& geometry); + void initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset); + void reset(const HFMModel& hfmModel); bool jointStatesEmpty(); int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; @@ -172,7 +178,8 @@ public: AnimPose getJointPose(int jointIndex) const; // Start or stop animations as needed. - void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState); + void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, + const glm::quat& worldRotation, CharacterControllerState ccState, float sensorToWorldScale); // Regardless of who started the animations or how many, update the joints. void updateAnimations(float deltaTime, const glm::mat4& rootTransform, const glm::mat4& rigToWorldTransform); @@ -205,7 +212,7 @@ public: void copyJointsFromJointData(const QVector& jointDataVec); void computeExternalPoses(const glm::mat4& modelOffsetMat); - void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + void computeAvatarBoundingCapsule(const HFMModel& hfmModel, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; void setEnableInverseKinematics(bool enable); void setEnableAnimations(bool enable); @@ -227,7 +234,6 @@ public: const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } - void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; }; signals: void onLoadComplete(); @@ -241,8 +247,8 @@ protected: void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, - const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo, + const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix); void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose, @@ -253,8 +259,8 @@ protected: bool calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const; glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const; - glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const; + glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const; AnimPose _modelOffset; // model to rig space @@ -270,6 +276,7 @@ protected: // Only accessed by the main thread PoseSet _internalPoseSet; + PoseSet _networkPoseSet; // Copy of the _poseSet for external threads. PoseSet _externalPoseSet; @@ -301,9 +308,12 @@ protected: QUrl _animGraphURL; std::shared_ptr _animNode; + std::shared_ptr _networkNode; std::shared_ptr _animSkeleton; std::unique_ptr _animLoader; + std::unique_ptr _networkLoader; AnimVariantMap _animVars; + AnimVariantMap _networkVars; enum class RigRole { Idle = 0, @@ -316,6 +326,28 @@ protected: RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; float _desiredStateAge { 0.0f }; + + struct NetworkAnimState { + enum ClipNodeEnum { + None = 0, + PreTransit, + Transit, + PostTransit, + A, + B + }; + NetworkAnimState() : clipNodeEnum(NetworkAnimState::None) {} + NetworkAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) : + clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {} + + ClipNodeEnum clipNodeEnum; + QString url; + float fps; + bool loop; + float firstFrame; + float lastFrame; + float blendTime; + }; struct UserAnimState { enum ClipNodeEnum { @@ -350,6 +382,7 @@ protected: }; UserAnimState _userAnimState; + NetworkAnimState _networkAnimState; std::map _roleAnimStates; float _leftHandOverlayAlpha { 0.0f }; @@ -381,19 +414,16 @@ protected: glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z }; // sensor space bool _prevLeftFootPoleVectorValid { false }; - glm::vec3 _prevRightHandPoleVector{ -Vectors::UNIT_Z }; // sensor space - bool _prevRightHandPoleVectorValid{ false }; - - glm::vec3 _prevLeftHandPoleVector{ -Vectors::UNIT_Z }; // sensor space - bool _prevLeftHandPoleVectorValid{ false }; - - bool _smoothPoleVectors { false }; - int _rigId; bool _headEnabled { false }; + bool _computeNetworkAnimation { false }; + bool _sendNetworkNode { false }; AnimContext _lastContext; AnimVariantMap _lastAnimVars; + + SnapshotBlendPoseHelper _hipsBlendHelper; + ControllerParameters _previousControllerParameters; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d00bc29054..cab02e215e 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -244,13 +244,20 @@ AudioClient::AudioClient() : // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash getAvailableDevices(QAudio::AudioInput); getAvailableDevices(QAudio::AudioOutput); - + // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); - connect(_checkDevicesTimer, &QTimer::timeout, this, [this] { - QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); }); - }); const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; + connect(_checkDevicesTimer, &QTimer::timeout, this, [=] { + QtConcurrent::run(QThreadPool::globalInstance(), [=] { + checkDevices(); + // On some systems (Ubuntu) checking all the audio devices can take more than 2 seconds. To + // avoid consuming all of the thread pool, don't start the check interval until the previous + // check has completed. + QMetaObject::invokeMethod(_checkDevicesTimer, "start", Q_ARG(int, DEVICE_CHECK_INTERVAL_MSECS)); + }); + }); + _checkDevicesTimer->setSingleShot(true); _checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS); // start a thread to detect peak value changes @@ -263,7 +270,8 @@ AudioClient::AudioClient() : configureReverb(); - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "processStreamStatsPacket"); packetReceiver.registerListener(PacketType::AudioEnvironment, this, "handleAudioEnvironmentDataPacket"); packetReceiver.registerListener(PacketType::SilentAudioFrame, this, "handleAudioDataPacket"); @@ -271,6 +279,16 @@ AudioClient::AudioClient() : packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket"); packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); + + auto& domainHandler = nodeList->getDomainHandler(); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] { + _solo.reset(); + }); + connect(nodeList.data(), &NodeList::nodeActivated, this, [this](SharedNodePointer node) { + if (node->getType() == NodeType::AudioMixer) { + _solo.resend(); + } + }); } AudioClient::~AudioClient() { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 5e7f1fb8a0..751bddd35d 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -46,7 +46,6 @@ #include #include - #include #include @@ -171,6 +170,7 @@ public: void stopRecording(); void setAudioPaused(bool pause); + AudioSolo& getAudioSolo() override { return _solo; } #ifdef Q_OS_WIN static QString getWinDeviceName(wchar_t* guid); @@ -446,6 +446,8 @@ private: #if defined(Q_OS_ANDROID) bool _shouldRestartInputSetup { true }; // Should we restart the input device because of an unintended stop? #endif + + AudioSolo _solo; Mutex _checkDevicesMutex; QTimer* _checkDevicesTimer { nullptr }; diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index bbfd79d0aa..0f075ab224 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -19,6 +19,7 @@ #include "AudioInjectorOptions.h" #include "AudioInjector.h" +#include "AudioSolo.h" class AudioInjector; class AudioInjectorLocalBuffer; @@ -38,6 +39,8 @@ public: // take care to delete it when ~AudioInjector, as parenting Qt semantics will not work virtual bool outputLocalInjector(const AudioInjectorPointer& injector) = 0; + virtual AudioSolo& getAudioSolo() = 0; + public slots: virtual bool shouldLoopbackInjectors() { return false; } diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 673966ff09..3065de5199 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -14,7 +14,7 @@ #include -#include +#include class AudioInjectorLocalBuffer : public QIODevice { Q_OBJECT diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 0f4ab7ff42..295da1506e 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -34,7 +34,7 @@ AudioInjectorOptions::AudioInjectorOptions() : QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions) { QScriptValue obj = engine->newObject(); - obj.setProperty("position", vec3toScriptValue(engine, injectorOptions.position)); + obj.setProperty("position", vec3ToScriptValue(engine, injectorOptions.position)); obj.setProperty("volume", injectorOptions.volume); obj.setProperty("loop", injectorOptions.loop); obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation)); diff --git a/libraries/audio/src/AudioSolo.cpp b/libraries/audio/src/AudioSolo.cpp new file mode 100644 index 0000000000..9d63f01a8b --- /dev/null +++ b/libraries/audio/src/AudioSolo.cpp @@ -0,0 +1,88 @@ +// +// AudioSolo.cpp +// libraries/audio/src +// +// Created by Clement Brisset on 11/5/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AudioSolo.h" + +#include + +bool AudioSolo::isSoloing() const { + Lock lock(_mutex); + return !_nodesSoloed.empty(); +} + +QVector AudioSolo::getUUIDs() const { + Lock lock(_mutex); + return _nodesSoloed.values().toVector(); +} + +void AudioSolo::addUUIDs(QVector uuidList) { + // create a reliable NLPacket with space for the solo UUIDs + auto soloPacket = NLPacket::create(PacketType::AudioSoloRequest, + uuidList.size() * NUM_BYTES_RFC4122_UUID + sizeof(uint8_t), true); + uint8_t addToSoloList = (uint8_t)true; + soloPacket->writePrimitive(addToSoloList); + + { + Lock lock(_mutex); + for (auto uuid : uuidList) { + if (_nodesSoloed.contains(uuid)) { + qWarning() << "Uuid already in solo list:" << uuid; + } else { + // write the node ID to the packet + soloPacket->write(uuid.toRfc4122()); + _nodesSoloed.insert(uuid); + } + } + } + + // send off this solo packet reliably to the matching node + auto nodeList = DependencyManager::get(); + nodeList->broadcastToNodes(std::move(soloPacket), { NodeType::AudioMixer }); +} + +void AudioSolo::removeUUIDs(QVector uuidList) { + // create a reliable NLPacket with space for the solo UUIDs + auto soloPacket = NLPacket::create(PacketType::AudioSoloRequest, + uuidList.size() * NUM_BYTES_RFC4122_UUID + sizeof(uint8_t), true); + uint8_t addToSoloList = (uint8_t)false; + soloPacket->writePrimitive(addToSoloList); + + { + Lock lock(_mutex); + for (auto uuid : uuidList) { + if (!_nodesSoloed.contains(uuid)) { + qWarning() << "Uuid not in solo list:" << uuid; + } else { + // write the node ID to the packet + soloPacket->write(uuid.toRfc4122()); + _nodesSoloed.remove(uuid); + } + } + } + + // send off this solo packet reliably to the matching node + auto nodeList = DependencyManager::get(); + nodeList->broadcastToNodes(std::move(soloPacket), { NodeType::AudioMixer }); +} + +void AudioSolo::reset() { + Lock lock(_mutex); + removeUUIDs(getUUIDs()); +} + + +void AudioSolo::resend() { + Lock lock(_mutex); + auto uuids = getUUIDs(); + _nodesSoloed.clear(); + addUUIDs(uuids); +} + diff --git a/libraries/audio/src/AudioSolo.h b/libraries/audio/src/AudioSolo.h new file mode 100644 index 0000000000..790280a14b --- /dev/null +++ b/libraries/audio/src/AudioSolo.h @@ -0,0 +1,40 @@ +// +// AudioSolo.h +// libraries/audio/src +// +// Created by Clement Brisset on 11/5/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#ifndef hifi_AudioSolo_h +#define hifi_AudioSolo_h + +#include + +#include +#include + +class AudioSolo { + using Mutex = std::recursive_mutex; + using Lock = std::unique_lock; + +public: + bool isSoloing() const; + QVector getUUIDs() const; + void addUUIDs(QVector uuidList); + void removeUUIDs(QVector uuidList); + void reset(); + + void resend(); + +private: + mutable Mutex _mutex; + QSet _nodesSoloed; +}; + +#endif // hifi_AudioSolo_h diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 162e833da2..2877de3d6f 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -30,12 +30,6 @@ SoundCache::SoundCache(QObject* parent) : } SharedSoundPointer SoundCache::getSound(const QUrl& url) { - if (QThread::currentThread() != thread()) { - SharedSoundPointer result; - BLOCKING_INVOKE_METHOD(this, "getSound", - Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url)); - return result; - } return getResource(url).staticCast(); } diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index e6b6986e7b..89dcc61805 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME avatars-renderer) setup_hifi_library(Network Script) -link_hifi_libraries(shared gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer) +link_hifi_libraries(shared shaders gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer) include_hifi_library_headers(avatars) include_hifi_library_headers(networking) include_hifi_library_headers(fbx) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index f154746707..03938dd3de 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -114,27 +114,29 @@ void Avatar::setShowNamesAboveHeads(bool show) { } AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { - glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition; - float oneFrameDistance = glm::length(currentPosition - _lastPosition); - const float MAX_TRANSIT_DISTANCE = 30.0f; - float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale; - if (oneFrameDistance > config._triggerDistance && !_isTransiting) { - if (oneFrameDistance < scaledMaxTransitDistance) { - start(deltaTime, _lastPosition, currentPosition, config); + float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); + if (oneFrameDistance > (config._minTriggerDistance * _scale)) { + if (oneFrameDistance < (config._maxTriggerDistance * _scale)) { + start(deltaTime, _lastPosition, avatarPosition, config); } else { - _lastPosition = currentPosition; - return Status::ABORT_TRANSIT; + _lastPosition = avatarPosition; + _status = Status::ABORT_TRANSIT; } } - _lastPosition = currentPosition; + _lastPosition = avatarPosition; _status = updatePosition(deltaTime); + + if (_isActive && oneFrameDistance > (config._abortDistance * _scale) && _status == Status::POST_TRANSIT) { + reset(); + _status = Status::ENDED; + } return _status; } void AvatarTransit::reset() { _lastPosition = _endPosition; _currentPosition = _endPosition; - _isTransiting = false; + _isActive = false; } void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) { _startPosition = startPosition; @@ -143,12 +145,14 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const _transitLine = endPosition - startPosition; _totalDistance = glm::length(_transitLine); _easeType = config._easeType; - const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - + + _preTransitTime = AVATAR_PRE_TRANSIT_FRAME_COUNT / AVATAR_TRANSIT_FRAMES_PER_SECOND; + _postTransitTime = AVATAR_POST_TRANSIT_FRAME_COUNT / AVATAR_TRANSIT_FRAMES_PER_SECOND; int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance; - _totalTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; - _currentTime = 0.0f; - _isTransiting = true; + _transitTime = (float)transitFrames / AVATAR_TRANSIT_FRAMES_PER_SECOND; + _totalTime = _transitTime + _preTransitTime + _postTransitTime; + _currentTime = _isActive ? _preTransitTime : 0.0f; + _isActive = true; } float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) { @@ -171,32 +175,37 @@ float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) { AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { Status status = Status::IDLE; - if (_isTransiting) { + if (_isActive) { float nextTime = _currentTime + deltaTime; - glm::vec3 newPosition; - if (nextTime >= _totalTime) { - _currentPosition = _endPosition; - _isTransiting = false; - status = Status::END_TRANSIT; - } else { + if (nextTime < _preTransitTime) { + _currentPosition = _startPosition; + status = Status::PRE_TRANSIT; if (_currentTime == 0) { + status = Status::STARTED; + } + } else if (nextTime < _totalTime - _postTransitTime){ + status = Status::TRANSITING; + if (_currentTime <= _preTransitTime) { status = Status::START_TRANSIT; } else { - status = Status::TRANSITING; + float percentageIntoTransit = (nextTime - _preTransitTime) / _transitTime; + _currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine; + } + } else { + status = Status::POST_TRANSIT; + _currentPosition = _endPosition; + if (nextTime >= _totalTime) { + _isActive = false; + status = Status::ENDED; + } else if (_currentTime < _totalTime - _postTransitTime) { + status = Status::END_TRANSIT; } - float percentageIntoTransit = nextTime / _totalTime; - _currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine; } _currentTime = nextTime; } return status; } -bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { - nextPosition = _currentPosition; - return _isTransiting; -} - Avatar::Avatar(QThread* thread) : _voiceSphereID(GeometryCache::UNKNOWN_ID) { @@ -489,8 +498,8 @@ void Avatar::relayJointDataToChildren() { glm::quat jointRotation; glm::vec3 jointTranslation; if (avatarJointIndex < 0) { - jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex); - jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex); + jointRotation = modelEntity->getLocalJointRotation(jointIndex); + jointTranslation = modelEntity->getLocalJointTranslation(jointIndex); map.push_back(-1); } else { int jointIndex = getJointIndex(jointName); @@ -513,8 +522,8 @@ void Avatar::relayJointDataToChildren() { jointRotation = getJointRotation(avatarJointIndex); jointTranslation = getJointTranslation(avatarJointIndex); } else { - jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex); - jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex); + jointRotation = modelEntity->getLocalJointRotation(jointIndex); + jointTranslation = modelEntity->getLocalJointTranslation(jointIndex); } modelEntity->setLocalJointRotation(jointIndex, jointRotation); modelEntity->setLocalJointTranslation(jointIndex, jointTranslation); @@ -537,16 +546,10 @@ void Avatar::relayJointDataToChildren() { void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); - - if (_transit.isTransiting()) { - glm::vec3 nextPosition; - if (_transit.getNextPosition(nextPosition)) { - _globalPosition = nextPosition; - _globalPositionChanged = usecTimestampNow(); - if (!hasParent()) { - setLocalPosition(nextPosition); - } - } + + _globalPosition = _transit.isActive() ? _transit.getCurrentPosition() : _serverPosition; + if (!hasParent()) { + setLocalPosition(_globalPosition); } _simulationRate.increment(); @@ -559,7 +562,7 @@ void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "updateJoints"); if (inView) { Head* head = getHead(); - if (_hasNewJointData || _transit.isTransiting()) { + if (_hasNewJointData || _transit.isActive()) { _skeletonModel->getRig().copyJointsFromJointData(_jointData); glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); _skeletonModel->getRig().computeExternalPoses(rootTransform); @@ -704,6 +707,19 @@ static TextRenderer3D* textRenderer(TextRendererType type) { return displayNameRenderer; } +void Avatar::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](AvatarData& avatar) { + auto avatarPtr = dynamic_cast(&avatar); + if (avatarPtr) { + avatarPtr->setBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs); + } + }); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} + void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = std::shared_ptr>(avatarPayload); @@ -713,7 +729,8 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc // INitialize the _render bound as we are creating the avatar render item _renderBound = getBounds(); transaction.resetItem(_renderItemID, avatarPayloadPointer); - _skeletonModel->addToScene(scene, transaction); + using namespace std::placeholders; + _skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, _renderItemID, _1, _2, _3, _4)); _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setGroupCulled(true); _skeletonModel->setCanCastShadow(true); @@ -792,7 +809,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) { avatarPtr->_renderBound = renderBound; } } - ); + ); } } @@ -936,7 +953,8 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { render::Transaction transaction; if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) { _skeletonModel->removeFromScene(scene, transaction); - _skeletonModel->addToScene(scene, transaction); + using namespace std::placeholders; + _skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, _renderItemID, _1, _2, _3, _4)); _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setGroupCulled(true); @@ -1293,7 +1311,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::quat rotation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMModel().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation); } @@ -1342,7 +1360,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::vec3 translation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMModel().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation); } @@ -1398,7 +1416,7 @@ void Avatar::withValidJointIndicesCache(std::function const& worker) con if (!_modelJointsCached) { _modelJointIndicesCache.clear(); if (_skeletonModel && _skeletonModel->isActive()) { - _modelJointIndicesCache = _skeletonModel->getFBXGeometry().jointIndices; + _modelJointIndicesCache = _skeletonModel->getHFMModel().jointIndices; _modelJointsCached = true; } } @@ -1706,21 +1724,23 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { } void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) { - // FIXME: this doesn't take into account Avatar rotation ShapeInfo shapeInfo; computeShapeInfo(shapeInfo); - glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight - start = getWorldPosition() - glm::vec3(0, halfExtents.y, 0) + shapeInfo.getOffset(); - end = getWorldPosition() + glm::vec3(0, halfExtents.y, 0) + shapeInfo.getOffset(); + glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = cylinderHalfHeight + radius radius = halfExtents.x; + glm::vec3 halfCylinderAxis(0.0f, halfExtents.y - radius, 0.0f); + Transform transform = getTransform(); + start = transform.getTranslation() + transform.getRotation() * (shapeInfo.getOffset() - halfCylinderAxis); + end = transform.getTranslation() + transform.getRotation() * (shapeInfo.getOffset() + halfCylinderAxis); } glm::vec3 Avatar::getWorldFeetPosition() { ShapeInfo shapeInfo; computeShapeInfo(shapeInfo); - glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight - glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y - halfExtents.x, 0.0f); - return getWorldOrientation() * localFeet + getWorldPosition(); + glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = cylinderHalfHeight + radius + glm::vec3 localFeet(0.0f, shapeInfo.getOffset().y - halfExtents.y, 0.0f); + Transform transform = getTransform(); + return transform.getTranslation() + transform.getRotation() * localFeet; } float Avatar::computeMass() { @@ -1980,22 +2000,12 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { } } -AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { +AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config) { std::lock_guard lock(_transitLock); + _transit.setScale(avatarScale); return _transit.update(deltaTime, avatarPosition, config); } -void Avatar::setTransitScale(float scale) { - std::lock_guard lock(_transitLock); - return _transit.setScale(scale); -} - -void Avatar::overrideNextPackagePositionData(const glm::vec3& position) { - std::lock_guard lock(_transitLock); - _overrideGlobalPosition = true; - _globalPositionOverride = position; -} - void Avatar::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) { std::lock_guard lock(_materialsLock); _materials[parentMaterialName].push(material); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 1087f74c07..9a4b9bb6b6 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -28,6 +28,8 @@ #include "Rig.h" #include +#include "MetaModelPayload.h" + namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar); template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar); @@ -54,9 +56,13 @@ class AvatarTransit { public: enum Status { IDLE = 0, + STARTED, + PRE_TRANSIT, START_TRANSIT, TRANSITING, END_TRANSIT, + POST_TRANSIT, + ENDED, ABORT_TRANSIT }; @@ -70,20 +76,20 @@ public: struct TransitConfig { TransitConfig() {}; int _totalFrames { 0 }; - int _framesPerMeter { 0 }; + float _framesPerMeter { 0.0f }; bool _isDistanceBased { false }; - float _triggerDistance { 0 }; + float _minTriggerDistance { 0.0f }; + float _maxTriggerDistance { 0.0f }; + float _abortDistance{ 0.0f }; EaseType _easeType { EaseType::EASE_OUT }; }; AvatarTransit() {}; Status update(float deltaTime, const glm::vec3& avatarPosition, const TransitConfig& config); Status getStatus() { return _status; } - bool isTransiting() { return _isTransiting; } + bool isActive() { return _isActive; } glm::vec3 getCurrentPosition() { return _currentPosition; } - bool getNextPosition(glm::vec3& nextPosition); glm::vec3 getEndPosition() { return _endPosition; } - float getTransitTime() { return _totalTime; } void setScale(float scale) { _scale = scale; } void reset(); @@ -91,7 +97,7 @@ private: Status updatePosition(float deltaTime); void start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const TransitConfig& config); float getEaseValue(AvatarTransit::EaseType type, float value); - bool _isTransiting { false }; + bool _isActive { false }; glm::vec3 _startPosition; glm::vec3 _endPosition; @@ -101,14 +107,17 @@ private: glm::vec3 _transitLine; float _totalDistance { 0.0f }; + float _preTransitTime { 0.0f }; float _totalTime { 0.0f }; + float _transitTime { 0.0f }; + float _postTransitTime { 0.0f }; float _currentTime { 0.0f }; EaseType _easeType { EaseType::EASE_OUT }; Status _status { Status::IDLE }; float _scale { 1.0f }; }; -class Avatar : public AvatarData, public scriptable::ModelProvider { +class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaModelPayload { Q_OBJECT // This property has JSDoc in MyAvatar.h. @@ -429,11 +438,7 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; std::shared_ptr getTransit() { return std::make_shared(_transit); }; - - AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config); - void setTransitScale(float scale); - - void overrideNextPackagePositionData(const glm::vec3& position); + AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config); signals: void targetScaleChanged(float targetScale); @@ -620,6 +625,9 @@ protected: static const float ATTACHMENT_LOADING_PRIORITY; LoadingStatus _loadingStatus { LoadingStatus::NoModel }; + + static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector& blendshapeOffsets, + const QVector& blendedMeshSizes, const render::ItemIDs& subItemIDs); }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 1ec58fd704..36e37dd3d4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -54,9 +54,9 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { } void SkeletonModel::initJointStates() { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.initJointStates(geometry, modelOffset); + _rig.initJointStates(hfmModel, modelOffset); { // initialize _jointData with proper values for default joints @@ -66,7 +66,7 @@ void SkeletonModel::initJointStates() { } // Determine the default eye position for avatar scale = 1.0 - int headJointIndex = geometry.headJointIndex; + int headJointIndex = hfmModel.headJointIndex; if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } @@ -74,7 +74,7 @@ void SkeletonModel::initJointStates() { getEyeModelPositions(leftEyePosition, rightEyePosition); glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; - int rootJointIndex = geometry.rootJointIndex; + int rootJointIndex = hfmModel.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); @@ -96,7 +96,7 @@ void SkeletonModel::initJointStates() { // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { assert(!_owningAvatar->isMyAvatar()); - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -124,7 +124,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // If the head is not positioned, updateEyeJoints won't get the math right glm::quat headOrientation; - _rig.getJointRotation(geometry.headJointIndex, headOrientation); + _rig.getJointRotation(hfmModel.headJointIndex, headOrientation); glm::vec3 eulers = safeEulerAngles(headOrientation); head->setBasePitch(glm::degrees(-eulers.x)); head->setBaseYaw(glm::degrees(eulers.y)); @@ -135,8 +135,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.eyeSaccade = glm::vec3(0.0f); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); } @@ -259,45 +259,45 @@ bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const { } bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { - return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition); + return isActive() && getJointPositionInWorldFrame(getHFMModel().headJointIndex, headPosition); } bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPositionInWorldFrame(getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPositionInWorldFrame(getHFMModel().neckJointIndex, neckPosition); } bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPosition(getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPosition(getHFMModel().neckJointIndex, neckPosition); } bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); - if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && - getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { + if (getJointPosition(hfmModel.leftEyeJointIndex, firstEyePosition) && + getJointPosition(hfmModel.rightEyeJointIndex, secondEyePosition)) { return true; } // no eye joints; try to estimate based on head/neck joints glm::vec3 neckPosition, headPosition; - if (getJointPosition(geometry.neckJointIndex, neckPosition) && - getJointPosition(geometry.headJointIndex, headPosition)) { + if (getJointPosition(hfmModel.neckJointIndex, neckPosition) && + getJointPosition(hfmModel.headJointIndex, headPosition)) { const float EYE_PROPORTION = 0.6f; glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION); glm::quat headRotation; - getJointRotation(geometry.headJointIndex, headRotation); + getJointRotation(hfmModel.headJointIndex, headRotation); const float EYES_FORWARD = 0.25f; const float EYE_SEPARATION = 0.1f; float headHeight = glm::distance(neckPosition, headPosition); firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; return true; - } else if (getJointPosition(geometry.headJointIndex, headPosition)) { + } else if (getJointPosition(hfmModel.headJointIndex, headPosition)) { glm::vec3 baseEyePosition = headPosition; glm::quat headRotation; - getJointRotation(geometry.headJointIndex, headRotation); + getJointRotation(hfmModel.headJointIndex, headRotation); const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); @@ -330,15 +330,15 @@ void SkeletonModel::computeBoundingShape() { return; } - const FBXGeometry& geometry = getFBXGeometry(); - if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { + const HFMModel& hfmModel = getHFMModel(); + if (hfmModel.joints.isEmpty() || hfmModel.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; } float radius, height; glm::vec3 offset; - _rig.computeAvatarBoundingCapsule(geometry, radius, height, offset); + _rig.computeAvatarBoundingCapsule(hfmModel, radius, height, offset); float invScale = 1.0f / _owningAvatar->getModelScale(); _boundingCapsuleRadius = invScale * radius; _boundingCapsuleHeight = invScale * height; @@ -369,7 +369,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch& } bool SkeletonModel::hasSkeleton() { - return isActive() ? getFBXGeometry().rootJointIndex != -1 : false; + return isActive() ? getHFMModel().rootJointIndex != -1 : false; } void SkeletonModel::onInvalidate() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index d82fce7412..c53cf8d333 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -41,10 +41,10 @@ public: void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. - int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } + int getLeftHandJointIndex() const { return isActive() ? getHFMModel().leftHandJointIndex : -1; } /// Returns the index of the right hand joint, or -1 if not found. - int getRightHandJointIndex() const { return isActive() ? getFBXGeometry().rightHandJointIndex : -1; } + int getRightHandJointIndex() const { return isActive() ? getHFMModel().rightHandJointIndex : -1; } bool getLeftGrabPosition(glm::vec3& position) const; bool getRightGrabPosition(glm::vec3& position) const; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d78d83bc09..c529865b85 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -44,6 +44,7 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" #include "ClientTraitsHandler.h" +#include "ResourceRequestObserver.h" //#define WANT_DEBUG @@ -65,7 +66,7 @@ size_t AvatarDataPacket::maxFaceTrackerInfoSize(size_t numBlendshapeCoefficients } size_t AvatarDataPacket::maxJointDataSize(size_t numJoints, bool hasGrabJoints) { - const size_t validityBitsSize = (size_t)std::ceil(numJoints / (float)BITS_IN_BYTE); + const size_t validityBitsSize = calcBitVectorSize((int)numJoints); size_t totalSize = sizeof(uint8_t); // numJoints @@ -75,8 +76,11 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints, bool hasGrabJoints) totalSize += numJoints * sizeof(SixByteTrans); // Translations size_t NUM_FAUX_JOINT = 2; - size_t num_grab_joints = (hasGrabJoints ? 2 : 0); - totalSize += (NUM_FAUX_JOINT + num_grab_joints) * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints + totalSize += NUM_FAUX_JOINT * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints + + if (hasGrabJoints) { + totalSize += sizeof(AvatarDataPacket::FarGrabJoints); + } return totalSize; } @@ -224,18 +228,18 @@ float AvatarData::getDistanceBasedMinTranslationDistance(glm::vec3 viewerPositio // we want to track outbound data in this case... QByteArray AvatarData::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { - AvatarDataPacket::HasFlags hasFlagsOut; auto lastSentTime = _lastToByteArray; _lastToByteArray = usecTimestampNow(); - return AvatarData::toByteArray(dataDetail, lastSentTime, getLastSentJointData(), - hasFlagsOut, dropFaceTracking, false, glm::vec3(0), nullptr, - &_outboundDataRate); + AvatarDataPacket::SendStatus sendStatus; + auto avatarByteArray = AvatarData::toByteArray(dataDetail, lastSentTime, getLastSentJointData(), + sendStatus, dropFaceTracking, false, glm::vec3(0), nullptr, 0, &_outboundDataRate); + return avatarByteArray; } QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector& lastSentJointData, - AvatarDataPacket::HasFlags& hasFlagsOut, bool dropFaceTracking, bool distanceAdjust, - glm::vec3 viewerPosition, QVector* sentJointDataOut, AvatarDataRate* outboundDataRateOut) const { + AvatarDataPacket::SendStatus& sendStatus, bool dropFaceTracking, bool distanceAdjust, + glm::vec3 viewerPosition, QVector* sentJointDataOut, int maxDataSize, AvatarDataRate* outboundDataRateOut) const { bool cullSmallChanges = (dataDetail == CullSmallData); bool sendAll = (dataDetail == SendAllData); @@ -243,11 +247,23 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent bool sendPALMinimum = (dataDetail == PALMinimum); lazyInitHeadData(); + ASSERT(maxDataSize == 0 || (size_t)maxDataSize >= AvatarDataPacket::MIN_BULK_PACKET_SIZE); + + // Leading flags, to indicate how much data is actually included in the packet... + AvatarDataPacket::HasFlags wantedFlags = 0; + AvatarDataPacket::HasFlags includedFlags = 0; + AvatarDataPacket::HasFlags extraReturnedFlags = 0; // For partial joint data. // special case, if we were asked for no data, then just include the flags all set to nothing if (dataDetail == NoData) { - AvatarDataPacket::HasFlags packetStateFlags = 0; - QByteArray avatarDataByteArray(reinterpret_cast(&packetStateFlags), sizeof(packetStateFlags)); + sendStatus.itemFlags = wantedFlags; + + QByteArray avatarDataByteArray; + if (sendStatus.sendUUID) { + avatarDataByteArray.append(getSessionUUID().toRfc4122().data(), NUM_BYTES_RFC4122_UUID); + } + + avatarDataByteArray.append((char*) &wantedFlags, sizeof wantedFlags); return avatarDataByteArray; } @@ -270,114 +286,141 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent // 3 translations * 6 bytes = 6.48kbps // - auto parentID = getParentID(); - - bool hasAvatarGlobalPosition = true; // always include global position - bool hasAvatarOrientation = false; - bool hasAvatarBoundingBox = false; - bool hasAvatarScale = false; - bool hasLookAtPosition = false; - bool hasAudioLoudness = false; - bool hasSensorToWorldMatrix = false; - bool hasAdditionalFlags = false; - - // local position, and parent info only apply to avatars that are parented. The local position - // and the parent info can change independently though, so we track their "changed since" - // separately - bool hasParentInfo = false; - bool hasAvatarLocalPosition = false; - - bool hasFaceTrackerInfo = false; - bool hasJointData = false; - bool hasJointDefaultPoseFlags = false; - bool hasGrabJoints = false; + QUuid parentID; glm::mat4 leftFarGrabMatrix; glm::mat4 rightFarGrabMatrix; glm::mat4 mouseFarGrabMatrix; - if (sendPALMinimum) { - hasAudioLoudness = true; - } else { - hasAvatarOrientation = sendAll || rotationChangedSince(lastSentTime); - hasAvatarBoundingBox = sendAll || avatarBoundingBoxChangedSince(lastSentTime); - hasAvatarScale = sendAll || avatarScaleChangedSince(lastSentTime); - hasLookAtPosition = sendAll || lookAtPositionChangedSince(lastSentTime); - hasAudioLoudness = sendAll || audioLoudnessChangedSince(lastSentTime); - hasSensorToWorldMatrix = sendAll || sensorToWorldMatrixChangedSince(lastSentTime); - hasAdditionalFlags = sendAll || additionalFlagsChangedSince(lastSentTime); - hasParentInfo = sendAll || parentInfoChangedSince(lastSentTime); - hasAvatarLocalPosition = hasParent() && (sendAll || - tranlationChangedSince(lastSentTime) || - parentInfoChangedSince(lastSentTime)); + if (sendStatus.itemFlags == 0) { + // New avatar ... + bool hasAvatarGlobalPosition = true; // always include global position + bool hasAvatarOrientation = false; + bool hasAvatarBoundingBox = false; + bool hasAvatarScale = false; + bool hasLookAtPosition = false; + bool hasAudioLoudness = false; + bool hasSensorToWorldMatrix = false; + bool hasJointData = false; + bool hasJointDefaultPoseFlags = false; + bool hasAdditionalFlags = false; - hasFaceTrackerInfo = !dropFaceTracking && (hasFaceTracker() || getHasScriptedBlendshapes()) && - (sendAll || faceTrackerInfoChangedSince(lastSentTime)); - hasJointData = sendAll || !sendMinimum; - hasJointDefaultPoseFlags = hasJointData; - if (hasJointData) { - bool leftValid; - leftFarGrabMatrix = _farGrabLeftMatrixCache.get(leftValid); - if (!leftValid) { - leftFarGrabMatrix = glm::mat4(); - } - bool rightValid; - rightFarGrabMatrix = _farGrabRightMatrixCache.get(rightValid); - if (!rightValid) { - rightFarGrabMatrix = glm::mat4(); - } - bool mouseValid; - mouseFarGrabMatrix = _farGrabMouseMatrixCache.get(mouseValid); - if (!mouseValid) { - mouseFarGrabMatrix = glm::mat4(); - } - hasGrabJoints = (leftValid || rightValid || mouseValid); + // local position, and parent info only apply to avatars that are parented. The local position + // and the parent info can change independently though, so we track their "changed since" + // separately + bool hasParentInfo = false; + bool hasAvatarLocalPosition = false; + + bool hasFaceTrackerInfo = false; + + if (sendPALMinimum) { + hasAudioLoudness = true; + } else { + hasAvatarOrientation = sendAll || rotationChangedSince(lastSentTime); + hasAvatarBoundingBox = sendAll || avatarBoundingBoxChangedSince(lastSentTime); + hasAvatarScale = sendAll || avatarScaleChangedSince(lastSentTime); + hasLookAtPosition = sendAll || lookAtPositionChangedSince(lastSentTime); + hasAudioLoudness = sendAll || audioLoudnessChangedSince(lastSentTime); + hasSensorToWorldMatrix = sendAll || sensorToWorldMatrixChangedSince(lastSentTime); + hasAdditionalFlags = sendAll || additionalFlagsChangedSince(lastSentTime); + hasParentInfo = sendAll || parentInfoChangedSince(lastSentTime); + hasAvatarLocalPosition = hasParent() && (sendAll || + tranlationChangedSince(lastSentTime) || + parentInfoChangedSince(lastSentTime)); + + hasFaceTrackerInfo = !dropFaceTracking && (hasFaceTracker() || getHasScriptedBlendshapes()) && + (sendAll || faceTrackerInfoChangedSince(lastSentTime)); + hasJointData = !sendMinimum; + hasJointDefaultPoseFlags = hasJointData; + } + + wantedFlags = + (hasAvatarGlobalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION : 0) + | (hasAvatarBoundingBox ? AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX : 0) + | (hasAvatarOrientation ? AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION : 0) + | (hasAvatarScale ? AvatarDataPacket::PACKET_HAS_AVATAR_SCALE : 0) + | (hasLookAtPosition ? AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION : 0) + | (hasAudioLoudness ? AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS : 0) + | (hasSensorToWorldMatrix ? AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX : 0) + | (hasAdditionalFlags ? AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS : 0) + | (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0) + | (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0) + | (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0) + | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0) + | (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0) + | (hasJointData ? AvatarDataPacket::PACKET_HAS_GRAB_JOINTS : 0); + + sendStatus.itemFlags = wantedFlags; + sendStatus.rotationsSent = 0; + sendStatus.translationsSent = 0; + } else { // Continuing avatar ... + wantedFlags = sendStatus.itemFlags; + if (wantedFlags & AvatarDataPacket::PACKET_HAS_GRAB_JOINTS) { + // Must send joints for grab joints - + wantedFlags |= AvatarDataPacket::PACKET_HAS_JOINT_DATA; } } + if (wantedFlags & AvatarDataPacket::PACKET_HAS_GRAB_JOINTS) { + bool leftValid; + leftFarGrabMatrix = _farGrabLeftMatrixCache.get(leftValid); + if (!leftValid) { + leftFarGrabMatrix = glm::mat4(); + } + bool rightValid; + rightFarGrabMatrix = _farGrabRightMatrixCache.get(rightValid); + if (!rightValid) { + rightFarGrabMatrix = glm::mat4(); + } + bool mouseValid; + mouseFarGrabMatrix = _farGrabMouseMatrixCache.get(mouseValid); + if (!mouseValid) { + mouseFarGrabMatrix = glm::mat4(); + } + if (!(leftValid || rightValid || mouseValid)) { + wantedFlags &= ~AvatarDataPacket::PACKET_HAS_GRAB_JOINTS; + } + } + if (wantedFlags & (AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS | AvatarDataPacket::PACKET_HAS_PARENT_INFO)) { + parentID = getParentID(); + } - const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + - (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) : 0) + - (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size(), hasGrabJoints) : 0) + - (hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0); + const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + NUM_BYTES_RFC4122_UUID + + AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) + + AvatarDataPacket::maxJointDataSize(_jointData.size(), true) + + AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()); + + if (maxDataSize == 0) { + maxDataSize = (int)byteArraySize; + } QByteArray avatarDataByteArray((int)byteArraySize, 0); unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); - unsigned char* startPosition = destinationBuffer; - - // Leading flags, to indicate how much data is actually included in the packet... - AvatarDataPacket::HasFlags packetStateFlags = - (hasAvatarGlobalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION : 0) - | (hasAvatarBoundingBox ? AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX : 0) - | (hasAvatarOrientation ? AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION : 0) - | (hasAvatarScale ? AvatarDataPacket::PACKET_HAS_AVATAR_SCALE : 0) - | (hasLookAtPosition ? AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION : 0) - | (hasAudioLoudness ? AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS : 0) - | (hasSensorToWorldMatrix ? AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX : 0) - | (hasAdditionalFlags ? AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS : 0) - | (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0) - | (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0) - | (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0) - | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0) - | (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0) - | (hasGrabJoints ? AvatarDataPacket::PACKET_HAS_GRAB_JOINTS : 0); - - memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); - destinationBuffer += sizeof(packetStateFlags); + const unsigned char* const startPosition = destinationBuffer; + const unsigned char* const packetEnd = destinationBuffer + maxDataSize; #define AVATAR_MEMCPY(src) \ memcpy(destinationBuffer, &(src), sizeof(src)); \ destinationBuffer += sizeof(src); - if (hasAvatarGlobalPosition) { - auto startSection = destinationBuffer; - if (_overrideGlobalPosition) { - AVATAR_MEMCPY(_globalPositionOverride); - } else { - AVATAR_MEMCPY(_globalPosition); - } - +// If we want an item and there's sufficient space: +#define IF_AVATAR_SPACE(flag, space) \ + if ((wantedFlags & AvatarDataPacket::flag) \ + && (packetEnd - destinationBuffer) >= (ptrdiff_t)(space) \ + && (includedFlags |= AvatarDataPacket::flag)) + if (sendStatus.sendUUID) { + memcpy(destinationBuffer, getSessionUUID().toRfc4122(), NUM_BYTES_RFC4122_UUID); + destinationBuffer += NUM_BYTES_RFC4122_UUID; + } + + unsigned char * packetFlagsLocation = destinationBuffer; + destinationBuffer += sizeof(wantedFlags); + + IF_AVATAR_SPACE(PACKET_HAS_AVATAR_GLOBAL_POSITION, sizeof _globalPosition) { + auto startSection = destinationBuffer; + AVATAR_MEMCPY(_globalPosition); + int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -385,7 +428,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasAvatarBoundingBox) { + IF_AVATAR_SPACE(PACKET_HAS_AVATAR_BOUNDING_BOX, sizeof _globalBoundingBoxDimensions + sizeof _globalBoundingBoxOffset) { auto startSection = destinationBuffer; AVATAR_MEMCPY(_globalBoundingBoxDimensions); AVATAR_MEMCPY(_globalBoundingBoxOffset); @@ -396,7 +439,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasAvatarOrientation) { + IF_AVATAR_SPACE(PACKET_HAS_AVATAR_ORIENTATION, sizeof(AvatarDataPacket::SixByteQuat)) { auto startSection = destinationBuffer; auto localOrientation = getOrientationOutbound(); destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, localOrientation); @@ -407,7 +450,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasAvatarScale) { + IF_AVATAR_SPACE(PACKET_HAS_AVATAR_SCALE, sizeof(AvatarDataPacket::AvatarScale)) { auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); auto scale = getDomainLimitedScale(); @@ -420,7 +463,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasLookAtPosition) { + IF_AVATAR_SPACE(PACKET_HAS_LOOK_AT_POSITION, sizeof(_headData->getLookAtPosition()) ) { auto startSection = destinationBuffer; AVATAR_MEMCPY(_headData->getLookAtPosition()); int numBytes = destinationBuffer - startSection; @@ -429,7 +472,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasAudioLoudness) { + IF_AVATAR_SPACE(PACKET_HAS_AUDIO_LOUDNESS, sizeof(AvatarDataPacket::AudioLoudness)) { auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); data->audioLoudness = packFloatGainToByte(getAudioLoudness() / AUDIO_LOUDNESS_SCALE); @@ -441,7 +484,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasSensorToWorldMatrix) { + IF_AVATAR_SPACE(PACKET_HAS_SENSOR_TO_WORLD_MATRIX, sizeof(AvatarDataPacket::SensorToWorldMatrix)) { auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); @@ -459,7 +502,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasAdditionalFlags) { + IF_AVATAR_SPACE(PACKET_HAS_ADDITIONAL_FLAGS, sizeof (uint16_t)) { auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); @@ -507,7 +550,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasParentInfo) { + IF_AVATAR_SPACE(PACKET_HAS_PARENT_INFO, sizeof(AvatarDataPacket::ParentInfo)) { auto startSection = destinationBuffer; auto parentInfo = reinterpret_cast(destinationBuffer); QByteArray referentialAsBytes = parentID.toRfc4122(); @@ -521,7 +564,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } - if (hasAvatarLocalPosition) { + IF_AVATAR_SPACE(PACKET_HAS_AVATAR_LOCAL_POSITION, sizeof(getLocalPosition()) ) { auto startSection = destinationBuffer; const auto localPosition = getLocalPosition(); AVATAR_MEMCPY(localPosition); @@ -532,11 +575,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } + const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients(); // If it is connected, pack up the data - if (hasFaceTrackerInfo) { + IF_AVATAR_SPACE(PACKET_HAS_FACE_TRACKER_INFO, sizeof(AvatarDataPacket::FaceTrackerInfo) + (size_t)blendshapeCoefficients.size() * sizeof(float)) { auto startSection = destinationBuffer; auto faceTrackerInfo = reinterpret_cast(destinationBuffer); - const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients(); // note: we don't use the blink and average loudness, we just use the numBlendShapes and // compute the procedural info on the client side. faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; @@ -556,125 +599,125 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } QVector jointData; - if (hasJointData || hasJointDefaultPoseFlags) { + if (wantedFlags & (AvatarDataPacket::PACKET_HAS_JOINT_DATA | AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS)) { QReadLocker readLock(&_jointDataLock); jointData = _jointData; } + const int numJoints = jointData.size(); + assert(numJoints <= 255); + const int jointBitVectorSize = calcBitVectorSize(numJoints); - // If it is connected, pack up the data - if (hasJointData) { + // Start joints if room for at least the faux joints. + IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, 1 + 2 * jointBitVectorSize + AvatarDataPacket::FAUX_JOINTS_SIZE) { + // Allow for faux joints + translation bit-vector: + const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + + jointBitVectorSize + AvatarDataPacket::FAUX_JOINTS_SIZE; auto startSection = destinationBuffer; // joint rotation data - int numJoints = jointData.size(); *destinationBuffer++ = (uint8_t)numJoints; unsigned char* validityPosition = destinationBuffer; - unsigned char validity = 0; - int validityBit = 0; - int numValidityBytes = calcBitVectorSize(numJoints); + memset(validityPosition, 0, jointBitVectorSize); #ifdef WANT_DEBUG int rotationSentCount = 0; unsigned char* beforeRotations = destinationBuffer; #endif - destinationBuffer += numValidityBytes; // Move pointer past the validity bytes + destinationBuffer += jointBitVectorSize; // Move pointer past the validity bytes // sentJointDataOut and lastSentJointData might be the same vector if (sentJointDataOut) { sentJointDataOut->resize(numJoints); // Make sure the destination is resized before using it } + const JointData *const joints = jointData.data(); + JointData *const sentJoints = sentJointDataOut ? sentJointDataOut->data() : nullptr; float minRotationDOT = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinRotationDOT(viewerPosition) : AVATAR_MIN_ROTATION_DOT; - for (int i = 0; i < jointData.size(); i++) { - const JointData& data = jointData[i]; + int i = sendStatus.rotationsSent; + for (; i < numJoints; ++i) { + const JointData& data = joints[i]; const JointData& last = lastSentJointData[i]; - if (!data.rotationIsDefaultPose) { - // The dot product for larger rotations is a lower number. - // So if the dot() is less than the value, then the rotation is a larger angle of rotation - if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation) - || (cullSmallChanges && fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT) ) { - validity |= (1 << validityBit); + if (packetEnd - destinationBuffer >= minSizeForJoint) { + if (!data.rotationIsDefaultPose) { + // The dot product for larger rotations is a lower number, + // so if the dot() is less than the value, then the rotation is a larger angle of rotation + if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation) + || (cullSmallChanges && fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT)) { + validityPosition[i / BITS_IN_BYTE] |= 1 << (i % BITS_IN_BYTE); #ifdef WANT_DEBUG - rotationSentCount++; + rotationSentCount++; #endif - destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); + destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); - if (sentJointDataOut) { - (*sentJointDataOut)[i].rotation = data.rotation; + if (sentJoints) { + sentJoints[i].rotation = data.rotation; + } } } + } else { + break; } - if (sentJointDataOut) { - (*sentJointDataOut)[i].rotationIsDefaultPose = data.rotationIsDefaultPose; + if (sentJoints) { + sentJoints[i].rotationIsDefaultPose = data.rotationIsDefaultPose; } - if (++validityBit == BITS_IN_BYTE) { - *validityPosition++ = validity; - validityBit = validity = 0; - } - } - if (validityBit != 0) { - *validityPosition++ = validity; } + sendStatus.rotationsSent = i; // joint translation data validityPosition = destinationBuffer; - validity = 0; - validityBit = 0; #ifdef WANT_DEBUG int translationSentCount = 0; unsigned char* beforeTranslations = destinationBuffer; #endif - destinationBuffer += numValidityBytes; // Move pointer past the validity bytes + memset(destinationBuffer, 0, jointBitVectorSize); + destinationBuffer += jointBitVectorSize; // Move pointer past the validity bytes float minTranslation = (distanceAdjust && cullSmallChanges) ? getDistanceBasedMinTranslationDistance(viewerPosition) : AVATAR_MIN_TRANSLATION; float maxTranslationDimension = 0.0; - for (int i = 0; i < jointData.size(); i++) { - const JointData& data = jointData[i]; + i = sendStatus.translationsSent; + for (; i < numJoints; ++i) { + const JointData& data = joints[i]; const JointData& last = lastSentJointData[i]; - if (!data.translationIsDefaultPose) { - if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation) - || (cullSmallChanges && glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation)) { - - validity |= (1 << validityBit); + if (packetEnd - destinationBuffer >= minSizeForJoint) { + if (!data.translationIsDefaultPose) { + if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation) + || (cullSmallChanges && glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation)) { + validityPosition[i / BITS_IN_BYTE] |= 1 << (i % BITS_IN_BYTE); #ifdef WANT_DEBUG - translationSentCount++; + translationSentCount++; #endif - maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); - destinationBuffer += - packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); + destinationBuffer += + packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); - if (sentJointDataOut) { - (*sentJointDataOut)[i].translation = data.translation; + if (sentJoints) { + sentJoints[i].translation = data.translation; + } } } + } else { + break; } - if (sentJointDataOut) { - (*sentJointDataOut)[i].translationIsDefaultPose = data.translationIsDefaultPose; + if (sentJoints) { + sentJoints[i].translationIsDefaultPose = data.translationIsDefaultPose; } - if (++validityBit == BITS_IN_BYTE) { - *validityPosition++ = validity; - validityBit = validity = 0; - } - } - - if (validityBit != 0) { - *validityPosition++ = validity; } + sendStatus.translationsSent = i; // faux joints Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); @@ -687,10 +730,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(), TRANSLATION_COMPRESSION_RADIX); - if (hasGrabJoints) { + IF_AVATAR_SPACE(PACKET_HAS_GRAB_JOINTS, sizeof (AvatarDataPacket::FarGrabJoints)) { // the far-grab joints may range further than 3 meters, so we can't use packFloatVec3ToSignedTwoByteFixed etc auto startSection = destinationBuffer; - auto data = reinterpret_cast(destinationBuffer); + glm::vec3 leftFarGrabPosition = extractTranslation(leftFarGrabMatrix); glm::quat leftFarGrabRotation = extractRotation(leftFarGrabMatrix); glm::vec3 rightFarGrabPosition = extractTranslation(rightFarGrabMatrix); @@ -698,28 +741,17 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix); glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix); - AVATAR_MEMCPY(leftFarGrabPosition); - // Can't do block copy as struct order is x, y, z, w. - data->leftFarGrabRotation[0] = leftFarGrabRotation.w; - data->leftFarGrabRotation[1] = leftFarGrabRotation.x; - data->leftFarGrabRotation[2] = leftFarGrabRotation.y; - data->leftFarGrabRotation[3] = leftFarGrabRotation.z; - destinationBuffer += sizeof(data->leftFarGrabPosition); - - AVATAR_MEMCPY(rightFarGrabPosition); - data->rightFarGrabRotation[0] = rightFarGrabRotation.w; - data->rightFarGrabRotation[1] = rightFarGrabRotation.x; - data->rightFarGrabRotation[2] = rightFarGrabRotation.y; - data->rightFarGrabRotation[3] = rightFarGrabRotation.z; - destinationBuffer += sizeof(data->rightFarGrabRotation); - - AVATAR_MEMCPY(mouseFarGrabPosition); - data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w; - data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x; - data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y; - data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z; - destinationBuffer += sizeof(data->mouseFarGrabRotation); + AvatarDataPacket::FarGrabJoints farGrabJoints = { + { leftFarGrabPosition.x, leftFarGrabPosition.y, leftFarGrabPosition.z }, + { leftFarGrabRotation.w, leftFarGrabRotation.x, leftFarGrabRotation.y, leftFarGrabRotation.z }, + { rightFarGrabPosition.x, rightFarGrabPosition.y, rightFarGrabPosition.z }, + { rightFarGrabRotation.w, rightFarGrabRotation.x, rightFarGrabRotation.y, rightFarGrabRotation.z }, + { mouseFarGrabPosition.x, mouseFarGrabPosition.y, mouseFarGrabPosition.z }, + { mouseFarGrabRotation.w, mouseFarGrabRotation.x, mouseFarGrabRotation.y, mouseFarGrabRotation.z } + }; + memcpy(destinationBuffer, &farGrabJoints, sizeof(farGrabJoints)); + destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -740,18 +772,20 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } #endif + if (sendStatus.rotationsSent != numJoints || sendStatus.translationsSent != numJoints) { + extraReturnedFlags |= AvatarDataPacket::PACKET_HAS_JOINT_DATA; + } + int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { outboundDataRateOut->jointDataRate.increment(numBytes); } - } - - if (hasJointDefaultPoseFlags) { + + IF_AVATAR_SPACE(PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS, 1 + 2 * jointBitVectorSize) { auto startSection = destinationBuffer; // write numJoints - int numJoints = jointData.size(); *destinationBuffer++ = (uint8_t)numJoints; // write rotationIsDefaultPose bits @@ -770,6 +804,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } } + memcpy(packetFlagsLocation, &includedFlags, sizeof(includedFlags)); + // Return dropped items. + sendStatus.itemFlags = (wantedFlags & ~includedFlags) | extraReturnedFlags; + int avatarDataSize = destinationBuffer - startPosition; if (avatarDataSize > (int)byteArraySize) { @@ -778,6 +816,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } return avatarDataByteArray.left(avatarDataSize); + +#undef AVATAR_MEMCPY +#undef IF_AVATAR_SPACE } // NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation @@ -894,20 +935,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); } - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; - if (_globalPosition != newValue) { - _globalPosition = newValue; - _globalPositionChanged = now; + _serverPosition = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; + if (_isClientAvatar) { + auto oneStepDistance = glm::length(_globalPosition - _serverPosition); + if (oneStepDistance <= AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE || oneStepDistance >= AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE) { + _globalPosition = _serverPosition; + // if we don't have a parent, make sure to also set our local position + if (!hasParent()) { + setLocalPosition(_serverPosition); + } + } + if (_globalPosition != _serverPosition) { + _globalPositionChanged = now; + } + } else { + if (_globalPosition != _serverPosition) { + _globalPosition = _serverPosition; + _globalPositionChanged = now; + } + if (!hasParent()) { + setLocalPosition(_serverPosition); + } } sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); int numBytesRead = sourceBuffer - startSection; _globalPositionRate.increment(numBytesRead); _globalPositionUpdateRate.increment(); - - // if we don't have a parent, make sure to also set our local position - if (!hasParent()) { - setLocalPosition(newValue); - } } if (hasAvatarBoundingBox) { @@ -928,6 +981,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _avatarBoundingBoxChanged = now; } + _defaultBubbleBox = computeBubbleBox(); + sourceBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox); int numBytesRead = sourceBuffer - startSection; _avatarBoundingBoxRate.increment(numBytesRead); @@ -1250,25 +1305,37 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto startSection = sourceBuffer; PACKET_READ_CHECK(FarGrabJoints, sizeof(AvatarDataPacket::FarGrabJoints)); - auto data = reinterpret_cast(sourceBuffer); - glm::vec3 leftFarGrabPosition = glm::vec3(data->leftFarGrabPosition[0], data->leftFarGrabPosition[1], - data->leftFarGrabPosition[2]); - glm::quat leftFarGrabRotation = glm::quat(data->leftFarGrabRotation[0], data->leftFarGrabRotation[1], - data->leftFarGrabRotation[2], data->leftFarGrabRotation[3]); - glm::vec3 rightFarGrabPosition = glm::vec3(data->rightFarGrabPosition[0], data->rightFarGrabPosition[1], - data->rightFarGrabPosition[2]); - glm::quat rightFarGrabRotation = glm::quat(data->rightFarGrabRotation[0], data->rightFarGrabRotation[1], - data->rightFarGrabRotation[2], data->rightFarGrabRotation[3]); - glm::vec3 mouseFarGrabPosition = glm::vec3(data->mouseFarGrabPosition[0], data->mouseFarGrabPosition[1], - data->mouseFarGrabPosition[2]); - glm::quat mouseFarGrabRotation = glm::quat(data->mouseFarGrabRotation[0], data->mouseFarGrabRotation[1], - data->mouseFarGrabRotation[2], data->mouseFarGrabRotation[3]); + + AvatarDataPacket::FarGrabJoints farGrabJoints; + memcpy(&farGrabJoints, sourceBuffer, sizeof(farGrabJoints)); // to avoid misaligned floats + + glm::vec3 leftFarGrabPosition = glm::vec3(farGrabJoints.leftFarGrabPosition[0], + farGrabJoints.leftFarGrabPosition[1], + farGrabJoints.leftFarGrabPosition[2]); + glm::quat leftFarGrabRotation = glm::quat(farGrabJoints.leftFarGrabRotation[0], + farGrabJoints.leftFarGrabRotation[1], + farGrabJoints.leftFarGrabRotation[2], + farGrabJoints.leftFarGrabRotation[3]); + glm::vec3 rightFarGrabPosition = glm::vec3(farGrabJoints.rightFarGrabPosition[0], + farGrabJoints.rightFarGrabPosition[1], + farGrabJoints.rightFarGrabPosition[2]); + glm::quat rightFarGrabRotation = glm::quat(farGrabJoints.rightFarGrabRotation[0], + farGrabJoints.rightFarGrabRotation[1], + farGrabJoints.rightFarGrabRotation[2], + farGrabJoints.rightFarGrabRotation[3]); + glm::vec3 mouseFarGrabPosition = glm::vec3(farGrabJoints.mouseFarGrabPosition[0], + farGrabJoints.mouseFarGrabPosition[1], + farGrabJoints.mouseFarGrabPosition[2]); + glm::quat mouseFarGrabRotation = glm::quat(farGrabJoints.mouseFarGrabRotation[0], + farGrabJoints.mouseFarGrabRotation[1], + farGrabJoints.mouseFarGrabRotation[2], + farGrabJoints.mouseFarGrabRotation[3]); _farGrabLeftMatrixCache.set(createMatFromQuatAndPos(leftFarGrabRotation, leftFarGrabPosition)); _farGrabRightMatrixCache.set(createMatFromQuatAndPos(rightFarGrabRotation, rightFarGrabPosition)); _farGrabMouseMatrixCache.set(createMatFromQuatAndPos(mouseFarGrabRotation, mouseFarGrabPosition)); - sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); + sourceBuffer += sizeof(AvatarDataPacket::FarGrabJoints); int numBytesRead = sourceBuffer - startSection; _farGrabJointRate.increment(numBytesRead); _farGrabJointUpdateRate.increment(); @@ -1725,11 +1792,9 @@ glm::quat AvatarData::getOrientationOutbound() const { return (getLocalOrientation()); } -void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, +void AvatarData::processAvatarIdentity(QDataStream& packetStream, bool& identityChanged, bool& displayNameChanged) { - QDataStream packetStream(identityData); - QUuid avatarSessionID; // peek the sequence number, this will tell us if we should be processing this identity packet at all @@ -1744,17 +1809,18 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide << (udt::SequenceNumber::Type) incomingSequenceNumber; } - if (incomingSequenceNumber > _identitySequenceNumber) { - Identity identity; + Identity identity; - packetStream - >> identity.attachmentData - >> identity.displayName - >> identity.sessionDisplayName - >> identity.isReplicated - >> identity.lookAtSnappingEnabled + packetStream + >> identity.attachmentData + >> identity.displayName + >> identity.sessionDisplayName + >> identity.isReplicated + >> identity.lookAtSnappingEnabled ; + if (incomingSequenceNumber > _identitySequenceNumber) { + // set the store identity sequence number to match the incoming identity _identitySequenceNumber = incomingSequenceNumber; @@ -2110,10 +2176,6 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) { } } - if (_overrideGlobalPosition) { - _overrideGlobalPosition = false; - } - doneEncoding(cullSmallData); static AvatarDataSequenceNumber sequenceNumber = 0; @@ -2157,11 +2219,21 @@ void AvatarData::updateJointMappings() { } if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) { + //// + // TODO: Should we rely upon HTTPResourceRequest for ResourceRequestObserver instead? + // HTTPResourceRequest::doSend() covers all of the following and + // then some. It doesn't cover the connect() call, so we may + // want to add a HTTPResourceRequest::doSend() method that does + // connects. QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL); networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + DependencyManager::get()->update( + _skeletonModelURL, -1, "AvatarData::updateJointMappings"); QNetworkReply* networkReply = networkAccessManager.get(networkRequest); + // + //// connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply); } } @@ -2828,10 +2900,10 @@ QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, c obj.setProperty("avatarID", avatarIDValue); obj.setProperty("distance", value.distance); obj.setProperty("face", boxFaceToString(value.face)); + QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); - QScriptValue intersection = vec3toScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal); + QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; @@ -2894,3 +2966,21 @@ void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value[EntityID] = binaryEntityProperties; } } + +const float AvatarData::DEFAULT_BUBBLE_SCALE = 2.4f; // magic number determined empirically + +AABox AvatarData::computeBubbleBox(float bubbleScale) const { + AABox box = AABox(_globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); + glm::vec3 size = box.getScale(); + size *= bubbleScale; + const glm::vec3 MIN_BUBBLE_SCALE(0.3f, 1.3f, 0.3); + size= glm::max(size, MIN_BUBBLE_SCALE); + box.setScaleStayCentered(size); + return box; +} + +AABox AvatarData::getDefaultBubbleBox() const { + AABox bubbleBox(_defaultBubbleBox); + bubbleBox.translate(_globalPosition); + return bubbleBox; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 46489451f7..36c6ed6c50 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -296,6 +296,17 @@ namespace AvatarDataPacket { } PACKED_END; const size_t FAR_GRAB_JOINTS_SIZE = 84; static_assert(sizeof(FarGrabJoints) == FAR_GRAB_JOINTS_SIZE, "AvatarDataPacket::FarGrabJoints size doesn't match."); + + static const size_t MIN_BULK_PACKET_SIZE = NUM_BYTES_RFC4122_UUID + HEADER_SIZE; + static const size_t FAUX_JOINTS_SIZE = 2 * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); + + struct SendStatus { + HasFlags itemFlags { 0 }; + bool sendUUID { false }; + int rotationsSent { 0 }; // ie: index of next unsent joint + int translationsSent { 0 }; + operator bool() { return itemFlags == 0; } + }; } const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation @@ -327,6 +338,17 @@ const float AVATAR_DISTANCE_LEVEL_5 = 200.0f; // meters // This is the start location in the Sandbox (xyz: 6270, 211, 6000). const glm::vec3 START_LOCATION(6270, 211, 6000); +// Avatar Transit Constants +const float AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE = 1.0f; +const float AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE = 30.0f; +const int AVATAR_TRANSIT_FRAME_COUNT = 5; +const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; +const float AVATAR_TRANSIT_ABORT_DISTANCE = 0.1f; +const bool AVATAR_TRANSIT_DISTANCE_BASED = false; +const float AVATAR_TRANSIT_FRAMES_PER_SECOND = 30.0f; +const float AVATAR_PRE_TRANSIT_FRAME_COUNT = 10.0f; +const float AVATAR_POST_TRANSIT_FRAME_COUNT = 27.0f; + enum KeyState { NO_KEY_DOWN = 0, INSERT_KEY_DOWN, @@ -452,8 +474,8 @@ public: virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false); virtual QByteArray toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector& lastSentJointData, - AvatarDataPacket::HasFlags& hasFlagsOut, bool dropFaceTracking, bool distanceAdjust, glm::vec3 viewerPosition, - QVector* sentJointDataOut, AvatarDataRate* outboundDataRateOut = nullptr) const; + AvatarDataPacket::SendStatus& sendStatus, bool dropFaceTracking, bool distanceAdjust, glm::vec3 viewerPosition, + QVector* sentJointDataOut, int maxDataSize = 0, AvatarDataRate* outboundDataRateOut = nullptr) const; virtual void doneEncoding(bool cullSmallChanges); @@ -960,7 +982,7 @@ public: // identityChanged returns true if identity has changed, false otherwise. // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. - void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + void processAvatarIdentity(QDataStream& packetStream, bool& identityChanged, bool& displayNameChanged); qint64 packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); @@ -1101,6 +1123,7 @@ public: glm::vec3 getClientGlobalPosition() const { return _globalPosition; } AABox getGlobalBoundingBox() const { return AABox(_globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); } + AABox getDefaultBubbleBox() const; /**jsdoc * @function MyAvatar.getAvatarEntityData @@ -1193,8 +1216,12 @@ public: void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; } int getReplicaIndex() { return _replicaIndex; } + static const float DEFAULT_BUBBLE_SCALE; /* = 2.4 */ + AABox computeBubbleBox(float bubbleScale = DEFAULT_BUBBLE_SCALE) const; + void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; } bool getIsNewAvatar() { return _isNewAvatar; } + void setIsClientAvatar(bool isClientAvatar) { _isClientAvatar = isClientAvatar; } signals: @@ -1378,8 +1405,7 @@ protected: // where Entities are located. This is currently only used by the mixer to decide how often to send // updates about one avatar to another. glm::vec3 _globalPosition { 0, 0, 0 }; - glm::vec3 _globalPositionOverride { 0, 0, 0 }; - bool _overrideGlobalPosition { false }; + glm::vec3 _serverPosition { 0, 0, 0 }; quint64 _globalPositionChanged { 0 }; quint64 _avatarBoundingBoxChanged { 0 }; @@ -1430,6 +1456,8 @@ protected: glm::vec3 _globalBoundingBoxDimensions; glm::vec3 _globalBoundingBoxOffset; + AABox _defaultBubbleBox; + mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar AvatarEntityIDs _avatarEntityForRecording; // create new entities id for avatar recording @@ -1459,6 +1487,7 @@ protected: float _density; int _replicaIndex { 0 }; bool _isNewAvatar { true }; + bool _isClientAvatar { false }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index b9c6899e09..41ca950b3b 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -85,8 +85,9 @@ std::vector AvatarReplicas::takeReplicas(const QUuid& paren void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; + QDataStream identityDataStream(identityData); for (auto avatar : replicas) { - avatar->processAvatarIdentity(identityData, identityChanged, displayNameChanged); + avatar->processAvatarIdentity(identityDataStream, identityChanged, displayNameChanged); } } } @@ -258,7 +259,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointersetIsNewAvatar(true); auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); for (auto replicaID : replicaIDs) { @@ -285,40 +285,45 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { + QDataStream avatarIdentityStream(message->getMessage()); - // peek the avatar UUID from the incoming packet - QUuid identityUUID = QUuid::fromRfc4122(message->peek(NUM_BYTES_RFC4122_UUID)); + while (!avatarIdentityStream.atEnd()) { + // peek the avatar UUID from the incoming packet + avatarIdentityStream.startTransaction(); + QUuid identityUUID; + avatarIdentityStream >> identityUUID; + avatarIdentityStream.rollbackTransaction(); - if (identityUUID.isNull()) { - qCDebug(avatars) << "Refusing to process identity packet for null avatar ID"; - return; - } - - // make sure this isn't for an ignored avatar - auto nodeList = DependencyManager::get(); - static auto EMPTY = QUuid(); - - { - QReadLocker locker(&_hashLock); - _pendingAvatars.remove(identityUUID); - auto me = _avatarHash.find(EMPTY); - if ((me != _avatarHash.end()) && (identityUUID == me.value()->getSessionUUID())) { - // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an - // identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining), - // we make things match here. - identityUUID = EMPTY; + if (identityUUID.isNull()) { + qCDebug(avatars) << "Refusing to process identity packet for null avatar ID"; + return; + } + + // make sure this isn't for an ignored avatar + auto nodeList = DependencyManager::get(); + static auto EMPTY = QUuid(); + + { + QReadLocker locker(&_hashLock); + auto me = _avatarHash.find(EMPTY); + if ((me != _avatarHash.end()) && (identityUUID == me.value()->getSessionUUID())) { + // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an + // identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining), + // we make things match here. + identityUUID = EMPTY; + } + } + + if (!nodeList->isIgnoringNode(identityUUID) || nodeList->getRequestsDomainListData()) { + // mesh URL for a UUID, find avatar in our list + bool isNewAvatar; + auto avatar = newOrExistingAvatar(identityUUID, sendingNode, isNewAvatar); + bool identityChanged = false; + bool displayNameChanged = false; + // In this case, the "sendingNode" is the Avatar Mixer. + avatar->processAvatarIdentity(avatarIdentityStream, identityChanged, displayNameChanged); + _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); } - } - - if (!nodeList->isIgnoringNode(identityUUID) || nodeList->getRequestsDomainListData()) { - // mesh URL for a UUID, find avatar in our list - bool isNewAvatar; - auto avatar = newOrExistingAvatar(identityUUID, sendingNode, isNewAvatar); - bool identityChanged = false; - bool displayNameChanged = false; - // In this case, the "sendingNode" is the Avatar Mixer. - avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); } } @@ -419,7 +424,6 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo } } - _pendingAvatars.remove(sessionUUID); auto removedAvatar = _avatarHash.take(sessionUUID); if (removedAvatar) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index c2cb448e52..3bb38dd081 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -161,6 +161,11 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); + /**jsdoc + * @function AvatarList.processBulkAvatarTraits + * @param {} message + * @param {} sendingNode + */ void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -183,15 +188,8 @@ protected: virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); - AvatarHash _avatarHash; - struct PendingAvatar { - std::chrono::steady_clock::time_point creationTime; - int transmits; - AvatarSharedPointer avatar; - }; - using AvatarPendingHash = QHash; - AvatarPendingHash _pendingAvatars; mutable QReadWriteLock _hashLock; + AvatarHash _avatarHash; std::unordered_map _processedTraitVersions; AvatarReplicas _replicas; diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index a06b53da7c..f8247d9e52 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -31,7 +31,27 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) : nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride"); } +void ClientTraitsHandler::markTraitUpdated(AvatarTraits::TraitType updatedTrait) { + Lock lock(_traitLock); + _traitStatuses[updatedTrait] = Updated; + _hasChangedTraits = true; +} + +void ClientTraitsHandler::markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID) { + Lock lock(_traitLock); + _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); + _hasChangedTraits = true; +} + +void ClientTraitsHandler::markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID) { + Lock lock(_traitLock); + _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); + _hasChangedTraits = true; +} + void ClientTraitsHandler::resetForNewMixer() { + Lock lock(_traitLock); + // re-set the current version to 0 _currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION; @@ -46,6 +66,8 @@ void ClientTraitsHandler::resetForNewMixer() { } void ClientTraitsHandler::sendChangedTraitsToMixer() { + Lock lock(_traitLock); + if (hasChangedTraits() || _shouldPerformInitialSend) { // we have at least one changed trait to send @@ -113,6 +135,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { void ClientTraitsHandler::processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode) { if (sendingNode->getType() == NodeType::AvatarMixer) { + Lock lock(_traitLock); while (message->getBytesLeftToRead()) { AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 27ba58d46b..3900268101 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -26,14 +26,11 @@ public: void sendChangedTraitsToMixer(); - bool hasChangedTraits() { return _hasChangedTraits; } + bool hasChangedTraits() const { return _hasChangedTraits; } - void markTraitUpdated(AvatarTraits::TraitType updatedTrait) - { _traitStatuses[updatedTrait] = Updated; _hasChangedTraits = true; } - void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID) - { _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); _hasChangedTraits = true; } - void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID) - { _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); _hasChangedTraits = true; } + void markTraitUpdated(AvatarTraits::TraitType updatedTrait); + void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID); + void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID); void resetForNewMixer(); @@ -41,17 +38,21 @@ public slots: void processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode); private: + using Mutex = std::recursive_mutex; + using Lock = std::lock_guard; + enum ClientTraitStatus { Unchanged, Updated, Deleted }; - AvatarData* _owningAvatar; + AvatarData* const _owningAvatar; + Mutex _traitLock; AvatarTraits::AssociatedTraitValues _traitStatuses; - AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; + AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; AvatarTraits::TraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; bool _shouldPerformInitialSend { false }; diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index ec7caf574b..9041c3a4f1 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -4,7 +4,4 @@ setup_hifi_library(Concurrent) link_hifi_libraries(shared graphics networking ktx image fbx) include_hifi_library_headers(gpu) -add_dependency_external_projects(draco) -find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) +target_draco() diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b90082d969..cef6c9b900 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -206,7 +206,7 @@ void FBXBaker::importScene() { } #endif - _geometry = reader.extractFBXGeometry({}, _modelURL.toString()); + _hfmModel = reader.extractHFMModel({}, _modelURL.toString()); _textureContentMap = reader._textureContent; } @@ -231,7 +231,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { for (FBXNode& objectChild : rootChild.children) { if (objectChild.name == "Geometry") { - // TODO Pull this out of _geometry instead so we don't have to reprocess it + // TODO Pull this out of _hfmModel instead so we don't have to reprocess it auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false); // Callback to get MaterialID @@ -293,7 +293,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QHash textureTypes; // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID - for (const auto& material : _geometry->materials) { + for (const auto& material : _hfmModel->materials) { if (material.normalTexture.isBumpmap) { textureTypes[material.normalTexture.id] = BUMP_TEXTURE; } else { @@ -329,7 +329,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { for (FBXNode& textureChild : object->children) { if (textureChild.name == "RelativeFilename") { - QString fbxTextureFileName { textureChild.properties.at(0).toString() }; + QString hfmTextureFileName { textureChild.properties.at(0).toString() }; // grab the ID for this texture so we can figure out the // texture type from the loaded materials @@ -337,7 +337,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto textureType = textureTypes[textureID]; // Compress the texture information and return the new filename to be added into the FBX scene - auto bakedTextureFile = compressTexture(fbxTextureFileName, textureType); + auto bakedTextureFile = compressTexture(hfmTextureFileName, textureType); // If no errors or warnings have occurred during texture compression add the filename to the FBX scene if (!bakedTextureFile.isNull()) { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 9d41209d4c..2af51b2190 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -53,7 +53,7 @@ private: void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); - FBXGeometry* _geometry; + HFMModel* _hfmModel; QHash _textureNameMatchCount; QHash _remappedTexturePaths; diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 75e10c54ab..ca352cebae 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -75,7 +75,7 @@ void ModelBaker::abort() { } } -bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { +bool ModelBaker::compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { if (mesh.wasCompressed) { handleError("Cannot re-bake a file that contains compressed mesh"); return false; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index 1fd77ab761..cda4478b1d 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -39,7 +39,7 @@ public: const QString& bakedOutputDirectory, const QString& originalOutputDirectory = ""); virtual ~ModelBaker(); - bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr); + bool compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr); QString compressTexture(QString textureFileName, image::TextureUsage::Type = image::TextureUsage::Type::DEFAULT_TEXTURE); virtual void setWasAborted(bool wasAborted) override; diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index cf62bc4fa8..d9f56b393e 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -153,7 +153,7 @@ void OBJBaker::bakeOBJ() { checkIfTexturesFinished(); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& hfmModel) { // Generating FBX Header Node FBXNode headerNode; headerNode.name = FBX_HEADER_EXTENSION; @@ -199,7 +199,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { // Compress the mesh information and store in dracoNode bool hasDeformers = false; // No concept of deformers for an OBJ FBXNode dracoNode; - compressMesh(geometry.meshes[0], hasDeformers, dracoNode); + compressMesh(hfmModel.meshes[0], hasDeformers, dracoNode); geometryNode.children.append(dracoNode); // Generating Object node's child - Model node @@ -214,17 +214,17 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { objectNode.children = { geometryNode, modelNode }; // Generating Objects node's child - Material node - auto& meshParts = geometry.meshes[0].parts; + auto& meshParts = hfmModel.meshes[0].parts; for (auto& meshPart : meshParts) { FBXNode materialNode; materialNode.name = MATERIAL_NODE_NAME; - if (geometry.materials.size() == 1) { + if (hfmModel.materials.size() == 1) { // case when no material information is provided, OBJReader considers it as a single default material - for (auto& materialID : geometry.materials.keys()) { - setMaterialNodeProperties(materialNode, materialID, geometry); + for (auto& materialID : hfmModel.materials.keys()) { + setMaterialNodeProperties(materialNode, materialID, hfmModel); } } else { - setMaterialNodeProperties(materialNode, meshPart.materialID, geometry); + setMaterialNodeProperties(materialNode, meshPart.materialID, hfmModel); } objectNode.children.append(materialNode); @@ -235,7 +235,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { auto size = meshParts.size(); for (int i = 0; i < size; i++) { QString material = meshParts[i].materialID; - FBXMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = hfmModel.materials[material]; if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) { auto textureID = nextNodeID(); _mapTextureMaterial.emplace_back(textureID, i); @@ -325,12 +325,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { } // Set properties for material nodes -void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry) { +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& hfmModel) { auto materialID = nextNodeID(); _materialIDs.push_back(materialID); materialNode.properties = { materialID, material, MESH }; - FBXMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = hfmModel.materials[material]; // Setting the hierarchy: Material -> Properties70 -> P -> Properties FBXNode properties70Node; diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 8e49692d35..5aaae49d4a 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -39,8 +39,8 @@ private slots: private: void loadOBJ(); - void createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry); - void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry); + void createFBXNodeTree(FBXNode& rootNode, HFMModel& hfmModel); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& hfmModel); NodeID nextNodeID() { return _nodeID++; } diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 6c5cc3a065..7479ef7b75 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -98,7 +98,7 @@ enum Hand { class InputDevice { public: InputDevice(const QString& name) : _name(name) {} - virtual ~InputDevice() {} + virtual ~InputDevice() = default; using Pointer = std::shared_ptr; diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 967838ef84..6df4b4af81 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -41,10 +41,10 @@ namespace controller { */ QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) { QScriptValue obj = engine->newObject(); - obj.setProperty("translation", vec3toScriptValue(engine, pose.translation)); + obj.setProperty("translation", vec3ToScriptValue(engine, pose.translation)); obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation)); - obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity)); - obj.setProperty("angularVelocity", vec3toScriptValue(engine, pose.angularVelocity)); + obj.setProperty("velocity", vec3ToScriptValue(engine, pose.velocity)); + obj.setProperty("angularVelocity", vec3ToScriptValue(engine, pose.angularVelocity)); obj.setProperty("valid", pose.valid); return obj; } diff --git a/libraries/controllers/src/controllers/impl/Conditional.h b/libraries/controllers/src/controllers/impl/Conditional.h index a216c8789f..844a6037be 100644 --- a/libraries/controllers/src/controllers/impl/Conditional.h +++ b/libraries/controllers/src/controllers/impl/Conditional.h @@ -30,6 +30,8 @@ namespace controller { using Factory = hifi::SimpleFactory; using Lambda = std::function; + virtual ~Conditional() = default; + virtual bool satisfied() = 0; virtual bool parseParameters(const QJsonValue& parameters) { return true; } diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index 6e6dc816d0..f230fb83dc 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -31,6 +31,7 @@ #include "filters/RotateFilter.h" #include "filters/LowVelocityFilter.h" #include "filters/ExponentialSmoothingFilter.h" +#include "filters/AccelerationLimiterFilter.h" using namespace controller; @@ -51,6 +52,7 @@ REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform") REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate") REGISTER_FILTER_CLASS_INSTANCE(LowVelocityFilter, "lowVelocity") REGISTER_FILTER_CLASS_INSTANCE(ExponentialSmoothingFilter, "exponentialSmoothing") +REGISTER_FILTER_CLASS_INSTANCE(AccelerationLimiterFilter, "accelerationLimiter") const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index cde8f991b7..7afabb4bcb 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -35,6 +35,8 @@ namespace controller { using Lambda = std::function; using Factory = hifi::SimpleFactory; + virtual ~Filter() = default; + virtual float apply(float value) const = 0; virtual Pose apply(Pose value) const = 0; diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h index 7d8f08ca1c..2299843a24 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h @@ -24,8 +24,6 @@ public: AndConditional(Conditional::Pointer& first, Conditional::Pointer& second) : _children({ first, second }) {} - virtual ~AndConditional() {} - virtual bool satisfied() override; private: diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index a6bd7d468d..0ba1347087 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -18,7 +18,6 @@ namespace controller { class EndpointConditional : public Conditional { public: EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} - virtual ~EndpointConditional() {} virtual bool satisfied() override { return _endpoint && _endpoint->peek() != 0.0f; } private: Endpoint::Pointer _endpoint; diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h index 3fcd5f49fc..6b19cf9505 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h @@ -19,7 +19,6 @@ namespace controller { using Pointer = std::shared_ptr; NotConditional(Conditional::Pointer operand) : _operand(operand) { } - virtual ~NotConditional() {} virtual bool satisfied() override; diff --git a/libraries/controllers/src/controllers/impl/filters/AccelerationLimiterFilter.cpp b/libraries/controllers/src/controllers/impl/filters/AccelerationLimiterFilter.cpp new file mode 100644 index 0000000000..3db1a9fba6 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/AccelerationLimiterFilter.cpp @@ -0,0 +1,192 @@ +// +// Created by Anthony Thibault 2018/11/09 +// 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 +// + + +#include "AccelerationLimiterFilter.h" + +#include +#include +#include "../../UserInputMapper.h" +#include "../../Input.h" +#include +#include +#include + +static const QString JSON_ROTATION_ACCELERATION_LIMIT = QStringLiteral("rotationAccelerationLimit"); +static const QString JSON_TRANSLATION_ACCELERATION_LIMIT = QStringLiteral("translationAccelerationLimit"); +static const QString JSON_TRANSLATION_SNAP_THRESHOLD = QStringLiteral("translationSnapThreshold"); +static const QString JSON_ROTATION_SNAP_THRESHOLD = QStringLiteral("rotationSnapThreshold"); + +static glm::vec3 angularVelFromDeltaRot(const glm::quat& deltaQ, float dt) { + // Measure the angular velocity of a delta rotation quaternion by using quaternion logarithm. + // The logarithm of a unit quternion returns the axis of rotation with a length of one half the angle of rotation in the imaginary part. + // The real part will be 0. Then we multiply it by 2 / dt. turning it into the angular velocity, (except for the extra w = 0 part). + glm::quat omegaQ((2.0f / dt) * glm::log(deltaQ)); + return glm::vec3(omegaQ.x, omegaQ.y, omegaQ.z); +} + +static glm::quat deltaRotFromAngularVel(const glm::vec3& omega, float dt) { + // Convert angular velocity into a delta quaternion by using quaternion exponent. + // The exponent of quaternion will return a delta rotation around the axis of the imaginary part, by twice the angle as determined by the length of that imaginary part. + // It is the inverse of the logarithm step in angularVelFromDeltaRot + glm::quat omegaQ(0.0f, omega.x, omega.y, omega.z); + return glm::exp((dt / 2.0f) * omegaQ); +} + +static glm::vec3 filterTranslation(const glm::vec3& x0, const glm::vec3& x1, const glm::vec3& x2, const glm::vec3& x3, + float dt, float accLimit, float snapThreshold) { + + // measure the linear velocities of this step and the previoius step + glm::vec3 v1 = (x3 - x1) / (2.0f * dt); + glm::vec3 v0 = (x2 - x0) / (2.0f * dt); + + // compute the acceleration + const glm::vec3 a = (v1 - v0) / dt; + + // clamp the acceleration if it is over the limit + float aLen = glm::length(a); + + // pick limit based on if we are moving faster then our target + float distToTarget = glm::length(x3 - x2); + if (aLen > accLimit && distToTarget > snapThreshold) { + // Solve for a new `v1`, such that `a` does not exceed `aLimit` + // This combines two steps: + // 1) Computing a limited accelration in the direction of `a`, but with a magnitute of `aLimit`: + // `newA = a * (aLimit / aLen)` + // 2) Computing new `v1` + // `v1 = newA * dt + v0` + // We combine the scalars from step 1 and step 2 into a single term to avoid having to do multiple scalar-vec3 multiplies. + v1 = a * ((accLimit * dt) / aLen) + v0; + + // apply limited v1 to compute filtered x3 + return v1 * dt + x2; + } else { + // did not exceed limit, no filtering necesary + return x3; + } +} + +static glm::quat filterRotation(const glm::quat& q0In, const glm::quat& q1In, const glm::quat& q2In, const glm::quat& q3In, + float dt, float accLimit, float snapThreshold) { + + // ensure quaternions have the same polarity + glm::quat q0 = q0In; + glm::quat q1 = glm::dot(q0In, q1In) < 0.0f ? -q1In : q1In; + glm::quat q2 = glm::dot(q1In, q2In) < 0.0f ? -q2In : q2In; + glm::quat q3 = glm::dot(q2In, q3In) < 0.0f ? -q3In : q3In; + + // measure the angular velocities of this step and the previous step + glm::vec3 w1 = angularVelFromDeltaRot(q3 * glm::inverse(q1), 2.0f * dt); + glm::vec3 w0 = angularVelFromDeltaRot(q2 * glm::inverse(q0), 2.0f * dt); + + const glm::vec3 a = (w1 - w0) / dt; + float aLen = glm::length(a); + + // clamp the acceleration if it is over the limit + float angleToTarget = glm::angle(q3 * glm::inverse(q2)); + if (aLen > accLimit && angleToTarget > snapThreshold) { + // solve for a new w1, such that a does not exceed the accLimit + w1 = a * ((accLimit * dt) / aLen) + w0; + + // apply limited w1 to compute filtered q3 + return deltaRotFromAngularVel(w1, dt) * q2; + } else { + // did not exceed limit, no filtering necesary + return q3; + } +} + +namespace controller { + + Pose AccelerationLimiterFilter::apply(Pose value) const { + + if (value.isValid()) { + + // to perform filtering in sensor space, we need to compute the transformations. + auto userInputMapper = DependencyManager::get(); + const InputCalibrationData calibrationData = userInputMapper->getInputCalibrationData(); + glm::mat4 sensorToAvatarMat = glm::inverse(calibrationData.avatarMat) * calibrationData.sensorToWorldMat; + glm::mat4 avatarToSensorMat = glm::inverse(calibrationData.sensorToWorldMat) * calibrationData.avatarMat; + + // transform pose into sensor space. + Pose sensorValue = value.transform(avatarToSensorMat); + + if (_prevValid) { + + const float DELTA_TIME = 0.01111111f; + + glm::vec3 unfilteredTranslation = sensorValue.translation; + sensorValue.translation = filterTranslation(_prevPos[0], _prevPos[1], _prevPos[2], sensorValue.translation, + DELTA_TIME, _translationAccelerationLimit, _translationSnapThreshold); + glm::quat unfilteredRot = sensorValue.rotation; + sensorValue.rotation = filterRotation(_prevRot[0], _prevRot[1], _prevRot[2], sensorValue.rotation, + DELTA_TIME, _rotationAccelerationLimit, _rotationSnapThreshold); + + // remember previous values. + _prevPos[0] = _prevPos[1]; + _prevPos[1] = _prevPos[2]; + _prevPos[2] = sensorValue.translation; + _prevRot[0] = _prevRot[1]; + _prevRot[1] = _prevRot[2]; + _prevRot[2] = sensorValue.rotation; + + _unfilteredPrevPos[0] = _unfilteredPrevPos[1]; + _unfilteredPrevPos[1] = _unfilteredPrevPos[2]; + _unfilteredPrevPos[2] = unfilteredTranslation; + _unfilteredPrevRot[0] = _unfilteredPrevRot[1]; + _unfilteredPrevRot[1] = _unfilteredPrevRot[2]; + _unfilteredPrevRot[2] = unfilteredRot; + + // transform back into avatar space + return sensorValue.transform(sensorToAvatarMat); + } else { + // initialize previous values with the current sample. + _prevPos[0] = sensorValue.translation; + _prevPos[1] = sensorValue.translation; + _prevPos[2] = sensorValue.translation; + _prevRot[0] = sensorValue.rotation; + _prevRot[1] = sensorValue.rotation; + _prevRot[2] = sensorValue.rotation; + + _unfilteredPrevPos[0] = sensorValue.translation; + _unfilteredPrevPos[1] = sensorValue.translation; + _unfilteredPrevPos[2] = sensorValue.translation; + _unfilteredPrevRot[0] = sensorValue.rotation; + _unfilteredPrevRot[1] = sensorValue.rotation; + _unfilteredPrevRot[2] = sensorValue.rotation; + + _prevValid = true; + + // no previous value to smooth with, so return value unchanged + return value; + } + } else { + // mark previous poses as invalid. + _prevValid = false; + + // return invalid value unchanged + return value; + } + } + + bool AccelerationLimiterFilter::parseParameters(const QJsonValue& parameters) { + if (parameters.isObject()) { + auto obj = parameters.toObject(); + if (obj.contains(JSON_ROTATION_ACCELERATION_LIMIT) && obj.contains(JSON_TRANSLATION_ACCELERATION_LIMIT) && + obj.contains(JSON_ROTATION_SNAP_THRESHOLD) && obj.contains(JSON_TRANSLATION_SNAP_THRESHOLD)) { + _rotationAccelerationLimit = (float)obj[JSON_ROTATION_ACCELERATION_LIMIT].toDouble(); + _translationAccelerationLimit = (float)obj[JSON_TRANSLATION_ACCELERATION_LIMIT].toDouble(); + _rotationSnapThreshold = (float)obj[JSON_ROTATION_SNAP_THRESHOLD].toDouble(); + _translationSnapThreshold = (float)obj[JSON_TRANSLATION_SNAP_THRESHOLD].toDouble(); + return true; + } + } + return false; + } + +} diff --git a/libraries/controllers/src/controllers/impl/filters/AccelerationLimiterFilter.h b/libraries/controllers/src/controllers/impl/filters/AccelerationLimiterFilter.h new file mode 100644 index 0000000000..269fd54102 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/AccelerationLimiterFilter.h @@ -0,0 +1,41 @@ +// +// Created by Anthony Thibault 2018/11/09 +// 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 +// + +#ifndef hifi_Controllers_Filters_Acceleration_Limiter_h +#define hifi_Controllers_Filters_Acceleration_Limiter_h + +#include "../Filter.h" + +namespace controller { + + class AccelerationLimiterFilter : public Filter { + REGISTER_FILTER_CLASS(AccelerationLimiterFilter); + + public: + AccelerationLimiterFilter() {} + + float apply(float value) const override { return value; } + Pose apply(Pose value) const override; + bool parseParameters(const QJsonValue& parameters) override; + + private: + float _rotationAccelerationLimit { FLT_MAX }; + float _translationAccelerationLimit { FLT_MAX }; + float _rotationSnapThreshold { 0.0f }; + float _translationSnapThreshold { 0.0f }; + + mutable glm::vec3 _prevPos[3]; // sensor space + mutable glm::quat _prevRot[3]; // sensor space + mutable glm::vec3 _unfilteredPrevPos[3]; // sensor space + mutable glm::quat _unfilteredPrevRot[3]; // sensor space + mutable bool _prevValid { false }; + }; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h index 6eca00fbe2..b06a43515f 100644 --- a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h @@ -18,7 +18,6 @@ class ClampFilter : public Filter { REGISTER_FILTER_CLASS(ClampFilter); public: ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; - virtual ~ClampFilter() {} virtual float apply(float value) const override { return glm::clamp(value, _min, _max); } diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h index bc90121ab0..129e08741d 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h @@ -17,16 +17,13 @@ namespace controller { class ConstrainToIntegerFilter : public Filter { REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); public: - ConstrainToIntegerFilter() {}; - virtual ~ConstrainToIntegerFilter() {} + ConstrainToIntegerFilter() = default; virtual float apply(float value) const override { return glm::sign(value); } virtual Pose apply(Pose value) const override { return value; } - -protected: }; } diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h index accebef851..8f2140721a 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h @@ -17,16 +17,13 @@ namespace controller { class ConstrainToPositiveIntegerFilter : public Filter { REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); public: - ConstrainToPositiveIntegerFilter() {}; - virtual ~ConstrainToPositiveIntegerFilter() {}; + ConstrainToPositiveIntegerFilter() = default; virtual float apply(float value) const override { return (value <= 0.0f) ? 0.0f : 1.0f; } virtual Pose apply(Pose value) const override { return value; } - -protected: }; } diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h index 96c60198e2..d898647126 100644 --- a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h @@ -18,7 +18,6 @@ class DeadZoneFilter : public Filter { REGISTER_FILTER_CLASS(DeadZoneFilter); public: DeadZoneFilter(float min = 0.0) : _min(min) {}; - virtual ~DeadZoneFilter() {} virtual float apply(float value) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp index 9cf2673d55..0f204ce15f 100644 --- a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp @@ -35,7 +35,7 @@ namespace controller { if (_prevSensorValue.isValid()) { // exponential smoothing filter sensorValue.translation = _translationConstant * sensorValue.getTranslation() + (1.0f - _translationConstant) * _prevSensorValue.getTranslation(); - sensorValue.rotation = safeMix(sensorValue.getRotation(), _prevSensorValue.getRotation(), _rotationConstant); + sensorValue.rotation = safeMix(sensorValue.getRotation(), _prevSensorValue.getRotation(), (1.0f - _rotationConstant)); // remember previous sensor space value. _prevSensorValue = sensorValue; diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h index 5b29b6681a..134f57243e 100644 --- a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h @@ -20,7 +20,6 @@ namespace controller { ExponentialSmoothingFilter() {} ExponentialSmoothingFilter(float rotationConstant, float translationConstant) : _translationConstant(translationConstant), _rotationConstant(rotationConstant) {} - virtual ~ExponentialSmoothingFilter() {} float apply(float value) const override { return value; } Pose apply(Pose value) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h index 4a607d0d5f..4eb563754f 100644 --- a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h @@ -18,7 +18,6 @@ class HysteresisFilter : public Filter { REGISTER_FILTER_CLASS(HysteresisFilter); public: HysteresisFilter(float min = 0.25, float max = 0.75); - virtual ~HysteresisFilter() {} virtual float apply(float value) const override; virtual Pose apply(Pose value) const override { return value; } diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h index 03b6e9fcb0..9361dfc60b 100644 --- a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h @@ -19,11 +19,8 @@ class InvertFilter : public ScaleFilter { public: using ScaleFilter::parseParameters; InvertFilter() : ScaleFilter(-1.0f) {} - virtual ~InvertFilter() {} virtual bool parseParameters(const QJsonArray& parameters) { return true; } - -private: }; } diff --git a/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h b/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h index fa75473edf..ac5299dc6f 100644 --- a/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h @@ -17,10 +17,9 @@ namespace controller { REGISTER_FILTER_CLASS(LowVelocityFilter); public: - LowVelocityFilter() {} + LowVelocityFilter() = default; LowVelocityFilter(float rotationConstant, float translationConstant) : _translationConstant(translationConstant), _rotationConstant(rotationConstant) {} - virtual ~LowVelocityFilter() {} float apply(float value) const override { return value; } Pose apply(Pose newPose) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/NotFilter.h b/libraries/controllers/src/controllers/impl/filters/NotFilter.h index fa52b8e212..ceb7d29de3 100644 --- a/libraries/controllers/src/controllers/impl/filters/NotFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/NotFilter.h @@ -10,7 +10,6 @@ class NotFilter : public Filter { REGISTER_FILTER_CLASS(NotFilter); public: NotFilter(); - virtual ~NotFilter() {} virtual float apply(float value) const override; virtual Pose apply(Pose value) const override { return value; } diff --git a/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h b/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h index 1cb9c0a1bd..3c1cb4f80c 100644 --- a/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h @@ -19,9 +19,8 @@ namespace controller { class PostTransformFilter : public Filter { REGISTER_FILTER_CLASS(PostTransformFilter); public: - PostTransformFilter() { } + PostTransformFilter() = default; PostTransformFilter(glm::mat4 transform) : _transform(transform) {} - virtual ~PostTransformFilter() {} virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { return value.postTransform(_transform); } virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); } diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h index 37cfe34b86..2e0da0efa9 100644 --- a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h @@ -18,9 +18,8 @@ namespace controller { class PulseFilter : public Filter { REGISTER_FILTER_CLASS(PulseFilter); public: - PulseFilter() {} + PulseFilter() = default; PulseFilter(float interval) : _interval(interval) {} - virtual ~PulseFilter() {} virtual float apply(float value) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h index aecf4f7b7c..c6735f6aa9 100644 --- a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h @@ -19,9 +19,8 @@ namespace controller { class RotateFilter : public Filter { REGISTER_FILTER_CLASS(RotateFilter); public: - RotateFilter() { } + RotateFilter() = default; RotateFilter(glm::quat rotation) : _rotation(rotation) {} - virtual ~RotateFilter() {} virtual float apply(float value) const override { return value; } diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h index 84f7cb7e47..936498a7a1 100644 --- a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h @@ -19,9 +19,8 @@ namespace controller { class ScaleFilter : public Filter { REGISTER_FILTER_CLASS(ScaleFilter); public: - ScaleFilter() {} + ScaleFilter() = default; ScaleFilter(float scale) : _scale(scale) {} - virtual ~ScaleFilter() {} virtual float apply(float value) const override { return value * _scale; diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h index ccfa9c6c25..a34edaa337 100644 --- a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h @@ -19,9 +19,8 @@ namespace controller { class TransformFilter : public Filter { REGISTER_FILTER_CLASS(TransformFilter); public: - TransformFilter() { } + TransformFilter() = default; TransformFilter(glm::mat4 transform) : _transform(transform) {} - virtual ~TransformFilter() {} virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { return value.transform(_transform); } diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h index a66e1eb4a4..ced9cbc689 100644 --- a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h @@ -19,9 +19,8 @@ namespace controller { class TranslateFilter : public Filter { REGISTER_FILTER_CLASS(TranslateFilter); public: - TranslateFilter() { } + TranslateFilter() = default; TranslateFilter(glm::vec3 translate) : _translate(translate) {} - virtual ~TranslateFilter() {} virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); } diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index a0d5cb0920..d3cb602e5b 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -494,7 +494,7 @@ glm::mat4 CompositorHelper::getPoint2DTransform(const glm::vec2& point, float si QVariant ReticleInterface::getPosition() const { - return vec2toVariant(_compositor->getReticlePosition()); + return vec2ToVariant(_compositor->getReticlePosition()); } void ReticleInterface::setPosition(QVariant position) { diff --git a/libraries/display-plugins/src/display-plugins/InterleavedSrgbToLinear.slf b/libraries/display-plugins/src/display-plugins/InterleavedSrgbToLinear.slf index 17dedce7f9..e70053dcd9 100644 --- a/libraries/display-plugins/src/display-plugins/InterleavedSrgbToLinear.slf +++ b/libraries/display-plugins/src/display-plugins/InterleavedSrgbToLinear.slf @@ -2,11 +2,11 @@ struct TextureData { ivec2 textureSize; }; -layout(std140, binding=0) uniform textureDataBuffer { +LAYOUT_STD140(binding=0) uniform textureDataBuffer { TextureData textureData; }; -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 580bea254a..190d4d4104 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -88,6 +88,7 @@ public: // Move the OpenGL context to the present thread // Extra code because of the widget 'wrapper' context _context = context; + _context->doneCurrent(); _context->moveToThread(this); } @@ -179,7 +180,9 @@ public: _context->makeCurrent(); { PROFILE_RANGE(render, "PluginPresent") + gl::globalLock(); currentPlugin->present(); + gl::globalRelease(false); CHECK_GL_ERROR(); } _context->doneCurrent(); @@ -363,56 +366,35 @@ void OpenGLDisplayPlugin::customizeContext() { } if (!_presentPipeline) { + gpu::StatePointer blendState = gpu::StatePointer(new gpu::State()); + blendState->setDepthTest(gpu::State::DepthTest(false)); + blendState->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + gpu::StatePointer scissorState = gpu::StatePointer(new gpu::State()); + scissorState->setDepthTest(gpu::State::DepthTest(false)); + scissorState->setScissorEnable(true); + { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::drawTexture); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false)); - state->setScissorEnable(true); - _simplePipeline = gpu::Pipeline::create(program, state); + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); + _simplePipeline = gpu::Pipeline::create(program, scissorState); + _hudPipeline = gpu::Pipeline::create(program, blendState); } { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::display_plugins::program::SrgbToLinear); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false)); - state->setScissorEnable(true); - _presentPipeline = gpu::Pipeline::create(program, state); + _presentPipeline = gpu::Pipeline::create(program, scissorState); } { - auto vs = gpu::Shader::createVertex(shader::gpu::vertex::DrawUnitQuadTexcoord); - auto ps = gpu::Shader::createPixel(shader::gpu::fragment::DrawTexture); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false)); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _hudPipeline = gpu::Pipeline::create(program, state); + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); + _mirrorHUDPipeline = gpu::Pipeline::create(program, blendState); } { - auto vs = gpu::Shader::createVertex(shader::gpu::vertex::DrawUnitQuadTexcoord); - auto ps = gpu::Shader::createPixel(shader::gpu::fragment::DrawTextureMirroredX); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false)); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _mirrorHUDPipeline = gpu::Pipeline::create(program, state); - } - - { - auto vs = gpu::Shader::createVertex(shader::gpu::vertex::DrawTransformUnitQuad); - auto ps = gpu::Shader::createPixel(shader::gpu::fragment::DrawTexture); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false)); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _cursorPipeline = gpu::Pipeline::create(program, state); + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTransformedTexture); + _cursorPipeline = gpu::Pipeline::create(program, blendState); } } updateCompositeFramebuffer(); diff --git a/libraries/display-plugins/src/display-plugins/SrgbToLinear.slf b/libraries/display-plugins/src/display-plugins/SrgbToLinear.slf index c2bcfb5cb3..aad9e71e0e 100644 --- a/libraries/display-plugins/src/display-plugins/SrgbToLinear.slf +++ b/libraries/display-plugins/src/display-plugins/SrgbToLinear.slf @@ -1,10 +1,10 @@ // OpenGLDisplayPlugin_present.frag -layout(binding = 0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; -layout(location = 0) in vec2 varTexCoord0; +layout(location=0) in vec2 varTexCoord0; -layout(location = 0) out vec4 outFragColor; +layout(location=0) out vec4 outFragColor; float sRGBFloatToLinear(float value) { const float SRGB_ELBOW = 0.04045; diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 4ea293a3da..d746ec1ea9 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -41,7 +41,7 @@ void LightEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint float largestDiameter = glm::compMax(dimensions); light->setMaximumRadius(largestDiameter / 2.0f); - light->setColor(toGlm(entity->getXColor())); + light->setColor(toGlm(entity->getColor())); float intensity = entity->getIntensity();//* entity->getFadingRatio(); light->setIntensity(intensity); diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 9ac7e9921f..6a472cab1e 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -37,7 +37,7 @@ void LineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { _lineVerticesID = geometryCache->allocateID(); } - glm::vec4 lineColor(toGlm(entity->getXColor()), entity->getLocalRenderAlpha()); + glm::vec4 lineColor(toGlm(entity->getColor()), entity->getLocalRenderAlpha()); geometryCache->updateVertices(_lineVerticesID, _linePoints, lineColor); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index d7a0cfd18d..c607f678b6 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -24,12 +24,18 @@ bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityP if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) { return true; } + if (!_texturesLoaded) { + return true; + } return false; } void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { withWriteLock([&] { - _drawMaterial = entity->getMaterial(); + if (_drawMaterial != entity->getMaterial()) { + _texturesLoaded = false; + _drawMaterial = entity->getMaterial(); + } _parentID = entity->getParentID(); _materialMappingPos = entity->getMaterialMappingPos(); _materialMappingScale = entity->getMaterialMappingScale(); @@ -38,6 +44,12 @@ void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& const float MATERIAL_ENTITY_SCALE = 0.5f; _renderTransform.postScale(MATERIAL_ENTITY_SCALE); _renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS); + + bool newTexturesLoaded = _drawMaterial ? !_drawMaterial->isMissingTexture() : false; + if (!_texturesLoaded && newTexturesLoaded) { + _drawMaterial->checkResetOpacityMap(); + } + _texturesLoaded = newTexturesLoaded; }); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index 168041a842..c90048ecf5 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -35,6 +35,7 @@ private: glm::vec2 _materialMappingPos; glm::vec2 _materialMappingScale; float _materialMappingRot; + bool _texturesLoaded { false }; std::shared_ptr _drawMaterial; }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 095283b1e5..9a68f81b66 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -268,7 +268,7 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper if (model->isLoaded()) { // TODO: improve naturalDimensions in the future, // for now we've added this hack for setting natural dimensions of models - Extents meshExtents = model->getFBXGeometry().getUnscaledMeshExtents(); + Extents meshExtents = model->getHFMModel().getUnscaledMeshExtents(); properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } @@ -308,7 +308,7 @@ bool RenderableModelEntityItem::findDetailedParabolaIntersection(const glm::vec3 } void RenderableModelEntityItem::getCollisionGeometryResource() { - QUrl hullURL(getCompoundShapeURL()); + QUrl hullURL(getCollisionShapeURL()); QUrlQuery queryArgs(hullURL); queryArgs.addQueryItem("collision-hull", ""); hullURL.setQuery(queryArgs); @@ -325,8 +325,9 @@ bool RenderableModelEntityItem::computeShapeFailedToLoad() { void RenderableModelEntityItem::setShapeType(ShapeType type) { ModelEntityItem::setShapeType(type); - if (getShapeType() == SHAPE_TYPE_COMPOUND) { - if (!_compoundShapeResource && !getCompoundShapeURL().isEmpty()) { + auto shapeType = getShapeType(); + if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) { + if (!_compoundShapeResource && !getCollisionShapeURL().isEmpty()) { getCollisionGeometryResource(); } } else if (_compoundShapeResource && !getCompoundShapeURL().isEmpty()) { @@ -352,18 +353,21 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { bool RenderableModelEntityItem::isReadyToComputeShape() const { ShapeType type = getShapeType(); auto model = getModel(); - if (type == SHAPE_TYPE_COMPOUND) { - if (!model || getCompoundShapeURL().isEmpty()) { + auto shapeType = getShapeType(); + if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) { + auto shapeURL = getCollisionShapeURL(); + + if (!model || shapeURL.isEmpty()) { return false; } - if (model->getURL().isEmpty()) { + if (model->getURL().isEmpty() || !_dimensionsInitialized) { // we need a render geometry with a scale to proceed, so give up. return false; } if (model->isLoaded()) { - if (!getCompoundShapeURL().isEmpty() && !_compoundShapeResource) { + if (!shapeURL.isEmpty() && !_compoundShapeResource) { const_cast(this)->getCollisionGeometryResource(); } @@ -399,7 +403,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // 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 FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); + const HFMModel& collisionGeometry = _compoundShapeResource->getHFMModel(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -407,15 +411,15 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // 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 FBXMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull - foreach (const FBXMeshPart &meshPart, mesh.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 uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // 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 FBXReader @@ -436,7 +440,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // 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 FBXMesh higher up + // 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 FBXReader @@ -474,7 +478,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / model->getFBXGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / model->getHFMModel().getUnscaledMeshExtents().size(); // 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()); @@ -494,19 +498,18 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // compute meshPart local transforms QVector localTransforms; - const FBXGeometry& fbxGeometry = model->getFBXGeometry(); - int numFbxMeshes = fbxGeometry.meshes.size(); + const HFMModel& hfmModel = model->getHFMModel(); + int numHFMMeshes = hfmModel.meshes.size(); int totalNumVertices = 0; glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); - for (int i = 0; i < numFbxMeshes; i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int i = 0; i < numHFMMeshes; i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); if (mesh.clusters.size() > 0) { - const FBXCluster& cluster = mesh.clusters.at(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 { - glm::mat4 identity; localTransforms.push_back(invRegistraionOffset); } totalNumVertices += mesh.vertices.size(); @@ -518,7 +521,16 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { return; } - auto& meshes = model->getGeometry()->getMeshes(); + std::vector> meshes; + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + auto& hfmMeshes = _compoundShapeResource->getHFMModel().meshes; + meshes.reserve(hfmMeshes.size()); + for (auto& hfmMesh : hfmMeshes) { + meshes.push_back(hfmMesh._mesh); + } + } else { + meshes = model->getGeometry()->getMeshes(); + } int32_t numMeshes = (int32_t)(meshes.size()); const int MAX_ALLOWED_MESH_COUNT = 1000; @@ -581,7 +593,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { while (partItr != parts.cend()) { auto numIndices = partItr->_numIndices; if (partItr->_topology == graphics::Mesh::TRIANGLES) { - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // 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 FBXReader @@ -592,7 +604,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ++indexItr; } } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { - // TODO: resurrect assert after we start sanitizing FBXMesh higher up + // TODO: resurrect assert after we start sanitizing HFMMesh higher up //assert(numIndices > 2); uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices; @@ -638,7 +650,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { std::set uniqueIndices; auto numIndices = partItr->_numIndices; if (partItr->_topology == graphics::Mesh::TRIANGLES) { - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // 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 FBXReader @@ -649,7 +661,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ++indexItr; } } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { - // TODO: resurrect assert after we start sanitizing FBXMesh higher up + // TODO: resurrect assert after we start sanitizing HFMMesh higher up //assert(numIndices > TRIANGLE_STRIDE - 1); auto indexItr = indices.cbegin() + partItr->_startIndex; @@ -742,7 +754,7 @@ int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) { bool RenderableModelEntityItem::contains(const glm::vec3& point) const { auto model = getModel(); if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { - return _compoundShapeResource->getFBXGeometry().convexHullContains(worldToEntity(point)); + return _compoundShapeResource->getHFMModel().convexHullContains(worldToEntity(point)); } return false; @@ -752,7 +764,7 @@ bool RenderableModelEntityItem::shouldBePhysical() const { auto model = getModel(); // If we have a model, make sure it hasn't failed to download. // If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready. - if (model && getShapeType() == SHAPE_TYPE_COMPOUND && model->didCollisionGeometryRequestFail()) { + if (model && (getShapeType() == SHAPE_TYPE_COMPOUND || getShapeType() == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) { return false; } else if (model && getShapeType() != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) { return false; @@ -962,14 +974,21 @@ QStringList RenderableModelEntityItem::getJointNames() const { return result; } -// FIXME: deprecated; remove >= RC67 -bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { - auto model = getModel(); - if (!model || !model->isLoaded()) { - return false; +void RenderableModelEntityItem::setAnimationURL(const QString& url) { + QString oldURL = getAnimationURL(); + ModelEntityItem::setAnimationURL(url); + if (oldURL != getAnimationURL()) { + _needsAnimationReset = true; } - BLOCKING_INVOKE_METHOD(model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result)); - return !result.isEmpty(); +} + +bool RenderableModelEntityItem::needsAnimationReset() const { + return _needsAnimationReset; +} + +QString RenderableModelEntityItem::getAnimationURLAndReset() { + _needsAnimationReset = false; + return getAnimationURL(); } scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() { @@ -1055,6 +1074,13 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { } } +bool RenderableModelEntityItem::readyToAnimate() const { + return resultWithReadLock([&] { + float firstFrame = _animationProperties.getFirstFrame(); + return (firstFrame >= 0.0f) && (firstFrame <= _animationProperties.getLastFrame()); + }); +} + using namespace render; using namespace render::entities; @@ -1108,7 +1134,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { QVector jointsData; - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); if (frameCount <= 0) { return; @@ -1132,17 +1158,17 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& fbxJoints = _animation->getGeometry().joints; + QStringList animationJointNames = _animation->getHFMModel().getJointNames(); + auto& hfmJoints = _animation->getHFMModel().joints; - auto& originalFbxJoints = _model->getFBXGeometry().joints; - auto& originalFbxIndices = _model->getFBXGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMModel().joints; + auto& originalHFMIndices = _model->getHFMModel().jointIndices; bool allowTranslation = entity->getAnimationAllowTranslation(); const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; const QVector& translations = frames[_lastKnownCurrentFrame].translations; - + jointsData.resize(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { int index = _jointMapping[j]; @@ -1155,23 +1181,22 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { translationMat = glm::translate(translations[index]); } } else if (index < animationJointNames.size()) { - QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation - - if (originalFbxIndices.contains(jointName)) { + QString jointName = hfmJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation + if (originalHFMIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get it's translation. - int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalFbxJoints[remappedIndex].translation); + int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. + translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } - } + } glm::mat4 rotationMat; if (index < rotations.size()) { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); } else { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); } - glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * - rotationMat * fbxJoints[index].postTransform); + glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * + rotationMat * hfmJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); jointData.translationSet = true; @@ -1464,14 +1489,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); - if (!jointsMapped()) { - mapJoints(entity, model->getJointNames()); - //else the joint have been mapped before but we have a new animation to load - } else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) { + if (_animation && entity->needsAnimationReset()) { + //(_animation->getURL().toString() != entity->getAnimationURL())) { // bad check + // the joints have been mapped before but we have a new animation to load + _animation.reset(); _jointMappingCompleted = false; - mapJoints(entity, model->getJointNames()); } - if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) { + + if (!_jointMappingCompleted) { + mapJoints(entity, model); + } + if (entity->readyToAnimate()) { animate(entity); } emit requestRenderUpdate(); @@ -1505,19 +1533,20 @@ void ModelEntityRenderer::doRender(RenderArgs* args) { #endif } -void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) { +void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const ModelPointer& model) { // if we don't have animation, or we're already joint mapped then bail early - if (!entity->hasAnimation() || jointsMapped()) { + if (!entity->hasAnimation()) { return; } - if (!_animation || _animation->getURL().toString() != entity->getAnimationURL()) { - _animation = DependencyManager::get()->getAnimation(entity->getAnimationURL()); + if (!_animation) { + _animation = DependencyManager::get()->getAnimation(entity->getAnimationURLAndReset()); } if (_animation && _animation->isLoaded()) { QStringList animationJointNames = _animation->getJointNames(); + auto modelJointNames = model->getJointNames(); if (modelJointNames.size() > 0 && animationJointNames.size() > 0) { _jointMapping.resize(modelJointNames.size()); for (int i = 0; i < modelJointNames.size(); i++) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b5f9d9f833..79e56d7a76 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -24,8 +24,6 @@ #include "RenderableEntityItem.h" - - class Model; class EntityTreeRenderer; @@ -114,21 +112,25 @@ public: virtual int getJointIndex(const QString& name) const override; virtual QStringList getJointNames() const override; - bool getMeshes(MeshProxyList& result) override; // deprecated + void setAnimationURL(const QString& url) override; + bool needsAnimationReset() const; + QString getAnimationURLAndReset(); private: bool needsUpdateModelBounds() const; void autoResizeJointArrays(); void copyAnimationJointDataToModel(); - + bool readyToAnimate() const; void getCollisionGeometryResource(); + GeometryResource::Pointer _compoundShapeResource; - bool _jointMapCompleted { false }; - bool _originalTexturesRead { false }; std::vector _jointMap; QVariantMap _originalTextures; + bool _jointMapCompleted { false }; + bool _originalTexturesRead { false }; bool _dimensionsInitialized { true }; bool _needsJointSimulation { false }; + bool _needsAnimationReset { false }; }; namespace render { namespace entities { @@ -169,8 +171,7 @@ protected: private: void animate(const TypedEntityPointer& entity); - void mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames); - bool jointsMapped() const { return _jointMappingCompleted; } + void mapJoints(const TypedEntityPointer& entity, const ModelPointer& model); // Transparency is handled in ModelMeshPartPayload virtual bool isTransparent() const override { return false; } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index f51a3f7740..38027a80ed 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -320,7 +320,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { } void ParticleEffectEntityRenderer::doRender(RenderArgs* args) { - if (!_visible) { + if (!_visible || !(_networkTexture && _networkTexture->isLoaded())) { return; } @@ -328,11 +328,7 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) { stepSimulation(); gpu::Batch& batch = *args->_batch; - if (_networkTexture && _networkTexture->isLoaded()) { - batch.setResourceTexture(0, _networkTexture->getGPUTexture()); - } else { - batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); - } + batch.setResourceTexture(0, _networkTexture->getGPUTexture()); Transform transform; // The particles are in world space, so the transform is unused, except for the rotation, which we use diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 743df477ac..0d9e948db8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -142,7 +142,7 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo } if (strokeColorsChanged) { _lastStrokeColors = entity->getStrokeColors(); - _lastStrokeColors = _lastNormals.size() == _lastStrokeColors.size() ? _lastStrokeColors : QVector({ toGlm(entity->getXColor()) }); + _lastStrokeColors = _lastNormals.size() == _lastStrokeColors.size() ? _lastStrokeColors : QVector({ toGlm(entity->getColor()) }); } if (pointsChanged || strokeWidthsChanged || normalsChanged || strokeColorsChanged) { _empty = std::min(_lastPoints.size(), std::min(_lastNormals.size(), _lastStrokeWidths.size())) < 2; @@ -161,10 +161,10 @@ void PolyLineEntityRenderer::updateGeometry(const std::vector& vertices) _verticesBuffer->setSubData(0, vertices); } -std::vector PolyLineEntityRenderer::updateVertices(const QVector& points, - const QVector& normals, +std::vector PolyLineEntityRenderer::updateVertices(const QVector& points, + const QVector& normals, const QVector& strokeWidths, - const QVector& strokeColors, + const QVector& strokeColors, const bool isUVModeStretch, const float textureAspectRatio) { // Calculate the minimum vector size out of normals, points, and stroke widths diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 3ba26c74df..8130171da8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -52,10 +52,10 @@ protected: }; void updateGeometry(const std::vector& vertices); - static std::vector updateVertices(const QVector& points, - const QVector& normals, + static std::vector updateVertices(const QVector& points, + const QVector& normals, const QVector& strokeWidths, - const QVector& strokeColors, + const QVector& strokeColors, const bool isUVModeStretch, const float textureAspectRatio); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5003e36e86..a705b61cd3 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -30,12 +30,14 @@ using namespace render::entities; // is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down. static const float SPHERE_ENTITY_SCALE = 0.5f; +static_assert(shader::render_utils::program::simple != 0, "Validate simple program exists"); +static_assert(shader::render_utils::program::simple_transparent != 0, "Validate simple transparent program exists"); ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { _procedural._vertexSource = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple); // FIXME: Setup proper uniform slots and use correct pipelines for forward rendering - _procedural._opaquefragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple); - _procedural._transparentfragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_transparent); + _procedural._opaqueFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple); + _procedural._transparentFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple_transparent); _procedural._opaqueState->setCullMode(gpu::State::CULL_NONE); _procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL); PrepareStencil::testMaskDrawShape(*_procedural._opaqueState); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 08a3b585e4..ce9e7ab764 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -47,11 +47,11 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint return true; } - if (_textColor != toGlm(entity->getTextColorX())) { + if (_textColor != toGlm(entity->getTextColor())) { return true; } - if (_backgroundColor != toGlm(entity->getBackgroundColorX())) { + if (_backgroundColor != toGlm(entity->getBackgroundColor())) { return true; } @@ -77,8 +77,8 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen } void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { - _textColor = toGlm(entity->getTextColorX()); - _backgroundColor = toGlm(entity->getBackgroundColorX()); + _textColor = toGlm(entity->getTextColor()); + _backgroundColor = toGlm(entity->getBackgroundColor()); _faceCamera = entity->getFaceCamera(); _lineHeight = entity->getLineHeight(); _text = entity->getText(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bc9ac84c91..476372160e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -54,7 +54,7 @@ WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& const QUrl url(urlString); auto scheme = url.scheme(); - if (scheme == URL_SCHEME_ABOUT || scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || + if (scheme == HIFI_URL_SCHEME_ABOUT || scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) { return ContentType::HtmlContent; } @@ -108,10 +108,6 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe return true; } - if (_lastLocked != entity->getLocked()) { - return true; - } - return false; } @@ -203,7 +199,6 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene } _lastDPI = entity->getDPI(); - _lastLocked = entity->getLocked(); glm::vec2 windowSize = getWindowSize(entity); _webSurface->resize(QSize(windowSize.x, windowSize.y)); @@ -261,6 +256,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { DependencyManager::get()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId); batch.popProjectionJitter(); + batch.setResourceTexture(0, nullptr); } bool WebEntityRenderer::hasWebSurface() { @@ -361,7 +357,7 @@ glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) con } void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { - if (!_lastLocked && _webSurface) { + if (_webSurface) { PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); _webSurface->hoverBeginEvent(webEvent, _touchDevice); @@ -369,7 +365,7 @@ void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { } void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { - if (!_lastLocked && _webSurface) { + if (_webSurface) { PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); _webSurface->hoverEndEvent(webEvent, _touchDevice); @@ -377,8 +373,7 @@ void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { } void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { - // Ignore mouse interaction if we're locked - if (!_lastLocked && _webSurface) { + if (_webSurface) { PointerEvent webEvent = event; webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); _webSurface->handlePointerEvent(webEvent, _touchDevice); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 1ba8ed0ec7..12640f697d 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -65,7 +65,6 @@ private: gpu::TexturePointer _texture; QString _lastSourceUrl; uint16_t _lastDPI; - bool _lastLocked; QTimer _timer; uint64_t _lastRenderTime { 0 }; }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index cf452c9cf7..2c017da71d 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -413,10 +413,10 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setAltitudeBased(_hazeProperties.getHazeAltitudeEffect()); haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange())); - xColor hazeColor = _hazeProperties.getHazeColor(); - haze->setHazeColor(glm::vec3(hazeColor.red / 255.0, hazeColor.green / 255.0, hazeColor.blue / 255.0)); - xColor hazeGlareColor = _hazeProperties.getHazeGlareColor(); - haze->setHazeGlareColor(glm::vec3(hazeGlareColor.red / 255.0, hazeGlareColor.green / 255.0, hazeGlareColor.blue / 255.0)); + glm::u8vec3 hazeColor = _hazeProperties.getHazeColor(); + haze->setHazeColor(toGlm(hazeColor)); + glm::u8vec3 hazeGlareColor = _hazeProperties.getHazeGlareColor(); + haze->setHazeGlareColor(toGlm(hazeGlareColor)); haze->setHazeEnableGlare(_hazeProperties.getHazeEnableGlare()); haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(_hazeProperties.getHazeGlareAngle())); @@ -447,7 +447,7 @@ void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& setSkyboxMode((ComponentMode)entity->getSkyboxMode()); editBackground(); - setSkyboxColor(_skyboxProperties.getColorVec3()); + setSkyboxColor(toGlm(_skyboxProperties.getColor())); setProceduralUserData(entity->getUserData()); setSkyboxURL(_skyboxProperties.getURL()); } diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 211685a9ba..ea54637b91 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -15,7 +15,7 @@ <@include DeferredBufferWrite.slh@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=0) in vec3 interpolatedNormal; diff --git a/libraries/entities-renderer/src/paintStroke_fade.slf b/libraries/entities-renderer/src/paintStroke_fade.slf index e5f70c8038..1ace04f7b3 100644 --- a/libraries/entities-renderer/src/paintStroke_fade.slf +++ b/libraries/entities-renderer/src/paintStroke_fade.slf @@ -18,7 +18,7 @@ <$declareFadeFragment()$> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=0) in vec3 interpolatedNormal; @@ -30,7 +30,7 @@ struct PolyLineUniforms { vec3 color; }; -layout(binding=0) uniform polyLineBuffer { +LAYOUT(binding=0) uniform polyLineBuffer { PolyLineUniforms polyline; }; diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index 441dfc11e5..6b1aa25a25 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -20,15 +20,15 @@ layout(location=RENDER_UTILS_ATTR_NORMAL_MS) in vec3 _normal; layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _position; layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _worldPosition; -layout(binding=ENTITIES_TEXTURE_POLYVOX_XMAP) uniform sampler2D xMap; -layout(binding=ENTITIES_TEXTURE_POLYVOX_YMAP) uniform sampler2D yMap; -layout(binding=ENTITIES_TEXTURE_POLYVOX_ZMAP) uniform sampler2D zMap; +LAYOUT(binding=ENTITIES_TEXTURE_POLYVOX_XMAP) uniform sampler2D xMap; +LAYOUT(binding=ENTITIES_TEXTURE_POLYVOX_YMAP) uniform sampler2D yMap; +LAYOUT(binding=ENTITIES_TEXTURE_POLYVOX_ZMAP) uniform sampler2D zMap; struct PolyvoxParams { vec4 voxelVolumeSize; }; -layout(binding=0) uniform polyvoxParamsBuffer { +LAYOUT(binding=0) uniform polyvoxParamsBuffer { PolyvoxParams params; }; diff --git a/libraries/entities-renderer/src/polyvox_fade.slf b/libraries/entities-renderer/src/polyvox_fade.slf index 6e236193aa..ae2e05c3dc 100644 --- a/libraries/entities-renderer/src/polyvox_fade.slf +++ b/libraries/entities-renderer/src/polyvox_fade.slf @@ -23,15 +23,15 @@ layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _position; layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _worldPosition; layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _worldFadePosition; -layout(binding=ENTITIES_TEXTURE_POLYVOX_XMAP) uniform sampler2D xMap; -layout(binding=ENTITIES_TEXTURE_POLYVOX_YMAP) uniform sampler2D yMap; -layout(binding=ENTITIES_TEXTURE_POLYVOX_ZMAP) uniform sampler2D zMap; +LAYOUT(binding=ENTITIES_TEXTURE_POLYVOX_XMAP) uniform sampler2D xMap; +LAYOUT(binding=ENTITIES_TEXTURE_POLYVOX_YMAP) uniform sampler2D yMap; +LAYOUT(binding=ENTITIES_TEXTURE_POLYVOX_ZMAP) uniform sampler2D zMap; struct PolyvoxParams { vec4 voxelVolumeSize; }; -layout(binding=0) uniform polyvoxParamsBuffer { +LAYOUT(binding=0) uniform polyvoxParamsBuffer { PolyvoxParams params; }; diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 7a0cedf011..7dadb6fc49 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -10,7 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=0) in vec4 varColor; layout(location=1) in vec2 varTexcoord; diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index 3eacaec3b5..4d17fe132b 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -43,7 +43,7 @@ struct ParticleUniforms { vec2 spare; }; -layout(std140, binding=0) uniform particleBuffer { +LAYOUT_STD140(binding=0) uniform particleBuffer { ParticleUniforms particle; }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index dca495ee03..c547708ffa 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -5,4 +5,4 @@ include_hifi_library_headers(fbx) include_hifi_library_headers(gpu) include_hifi_library_headers(image) include_hifi_library_headers(ktx) -link_hifi_libraries(shared networking octree avatars graphics model-networking) \ No newline at end of file +link_hifi_libraries(shared shaders networking octree avatars graphics model-networking) \ No newline at end of file diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 94df7eb465..bc20824d73 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -104,10 +104,10 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper AABox aaBox = zoneEntity->getAABox(success); if (success) { QScriptValue boundingBox = filterData.engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(filterData.engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(filterData.engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(filterData.engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(filterData.engine, aaBox.getDimensions()); + QScriptValue bottomRightNear = vec3ToScriptValue(filterData.engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3ToScriptValue(filterData.engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3ToScriptValue(filterData.engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3ToScriptValue(filterData.engine, aaBox.getDimensions()); boundingBox.setProperty("brn", bottomRightNear); boundingBox.setProperty("tfl", topFarLeft); boundingBox.setProperty("center", center); @@ -183,7 +183,7 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) { } // The following should be abstracted out for use in Agent.cpp (and maybe later AvatarMixer.cpp) - if (scriptURL.scheme().isEmpty() || (scriptURL.scheme() == URL_SCHEME_FILE)) { + if (scriptURL.scheme().isEmpty() || (scriptURL.scheme() == HIFI_URL_SCHEME_FILE)) { qWarning() << "Cannot load script from local filesystem, because assignment may be on a different computer."; scriptRequestFinished(entityID); return; @@ -200,7 +200,8 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) { _filterDataMap.insert(entityID, filterData); _lock.unlock(); - auto scriptRequest = DependencyManager::get()->createResourceRequest(this, scriptURL); + auto scriptRequest = DependencyManager::get()->createResourceRequest( + this, scriptURL, true, -1, "EntityEditFilters::addFilter"); if (!scriptRequest) { qWarning() << "Could not create ResourceRequest for Entity Edit filter script at" << scriptURL.toString(); scriptRequestFinished(entityID); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 184bb054ea..36ffc68fd3 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -131,6 +131,10 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_CLONE_AVATAR_ENTITY; requestedProperties += PROP_CLONE_ORIGIN_ID; + withReadLock([&] { + requestedProperties += _grabProperties.getEntityProperties(params); + }); + return requestedProperties; } @@ -168,6 +172,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); + requestedProperties -= PROP_CLIENT_ONLY; + requestedProperties -= PROP_OWNING_AVATAR_ID; + // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) { @@ -300,7 +307,12 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit()); APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic()); APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); + + withReadLock([&] { + _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + }); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -661,7 +673,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef const QUuid& myNodeID = nodeList->getSessionUUID(); bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); - // pack SimulationOwner and terse update properties near each other + // pack SimulationOwner, transform, and velocity properties near each other // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. @@ -892,7 +904,15 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit); READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); - READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); + READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); + + withWriteLock([&] { + int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, + somethingChanged); + bytesRead += bytesFromGrab; + dataAt += bytesFromGrab; + }); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -1329,13 +1349,16 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID); + withReadLock([&] { + _grabProperties.getProperties(properties); + }); + properties._defaultSettings = false; return properties; } -void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const { - // a TerseUpdate includes the transform and its derivatives +void EntityItem::getTransformAndVelocityProperties(EntityItemProperties& properties) const { if (!properties._positionChanged) { properties._position = getLocalPosition(); } @@ -1359,8 +1382,11 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c properties._accelerationChanged = true; } -void EntityItem::setScriptSimulationPriority(uint8_t priority) { - uint8_t newPriority = stillHasGrabActions() ? glm::max(priority, SCRIPT_GRAB_SIMULATION_PRIORITY) : priority; +void EntityItem::upgradeScriptSimulationPriority(uint8_t priority) { + uint8_t newPriority = glm::max(priority, _scriptSimulationPriority); + if (newPriority < SCRIPT_GRAB_SIMULATION_PRIORITY && stillHasGrabActions()) { + newPriority = SCRIPT_GRAB_SIMULATION_PRIORITY; + } if (newPriority != _scriptSimulationPriority) { // set the dirty flag to trigger a bid or ownership update markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY); @@ -1395,7 +1421,7 @@ bool EntityItem::stillWaitingToTakeOwnership(uint64_t timestamp) const { bool EntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - // these affect TerseUpdate properties + // these affect transform and velocity properties SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation); @@ -1465,6 +1491,11 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID); + withWriteLock([&] { + bool grabPropertiesChanged = _grabProperties.setProperties(properties); + somethingChanged |= grabPropertiesChanged; + }); + if (updateQueryAACube()) { somethingChanged = true; } @@ -1784,6 +1815,12 @@ void EntityItem::setUnscaledDimensions(const glm::vec3& value) { } } +glm::vec3 EntityItem::getUnscaledDimensions() const { + return resultWithReadLock([&] { + return _unscaledDimensions; + }); +} + void EntityItem::setRotation(glm::quat rotation) { if (getLocalOrientation() != rotation) { setLocalOrientation(rotation); @@ -3221,3 +3258,26 @@ void EntityItem::setScriptHasFinishedPreload(bool value) { bool EntityItem::isScriptPreloadFinished() { return _scriptPreloadFinished; } + +void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority) { + if (dynamicDataNeedsTransmit()) { + setDynamicDataNeedsTransmit(false); + properties.setActionData(getDynamicData()); + } + + if (updateQueryAACube()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + properties.setQueryAACube(getQueryAACube()); + } + + // set the LastEdited of the properties but NOT the entity itself + properties.setLastEdited(now); + + clearScriptSimulationPriority(); + properties.setSimulationOwner(Physics::getSessionUUID(), priority); + setPendingOwnershipPriority(priority); + + properties.setClientOnly(getClientOnly()); + properties.setOwningAvatarID(getOwningAvatarID()); + setLastBroadcast(now); // for debug/physics status icons +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 0e5cae66b6..c49017b2e0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -35,6 +35,7 @@ #include "SimulationOwner.h" #include "SimulationFlags.h" #include "EntityDynamicInterface.h" +#include "GrabPropertyGroup.h" #include "graphics/Material.h" @@ -191,7 +192,7 @@ public: virtual void setScaledDimensions(const glm::vec3& value); virtual glm::vec3 getRaycastDimensions() const { return getScaledDimensions(); } - inline const glm::vec3 getUnscaledDimensions() const { return _unscaledDimensions; } + glm::vec3 getUnscaledDimensions() const; virtual void setUnscaledDimensions(const glm::vec3& value); float getLocalRenderAlpha() const; @@ -263,9 +264,8 @@ public: void setCollisionSoundURL(const QString& value); glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity - /// registration point as ratio of entity - virtual void setRegistrationPoint(const glm::vec3& value); // FIXME: this is suspicious! + virtual void setRegistrationPoint(const glm::vec3& value); // FIXME: this is suspicious! bool hasAngularVelocity() const { return getWorldAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; } bool hasLocalAngularVelocity() const { return getLocalAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; } @@ -325,7 +325,7 @@ public: // TODO: move this "ScriptSimulationPriority" and "PendingOwnership" stuff into EntityMotionState // but first would need to do some other cleanup. In the meantime these live here as "scratch space" // to allow libs that don't know about each other to communicate. - void setScriptSimulationPriority(uint8_t priority); + void upgradeScriptSimulationPriority(uint8_t priority); void clearScriptSimulationPriority(); uint8_t getScriptSimulationPriority() const { return _scriptSimulationPriority; } void setPendingOwnershipPriority(uint8_t priority); @@ -420,7 +420,7 @@ public: quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; } void updateLastEditedFromRemote() { _lastEditedFromRemote = usecTimestampNow(); } - void getAllTerseUpdateProperties(EntityItemProperties& properties) const; + void getTransformAndVelocityProperties(EntityItemProperties& properties) const; void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; } @@ -533,6 +533,10 @@ public: void setCloneIDs(const QVector& cloneIDs); void setVisuallyReady(bool visuallyReady) { _visuallyReady = visuallyReady; } + const GrabPropertyGroup& getGrabProperties() const { return _grabProperties; } + + void prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority); + signals: void requestRenderUpdate(); void spaceUpdate(std::pair data); @@ -711,6 +715,8 @@ protected: QUuid _cloneOriginID; QVector _cloneIDs; + GrabPropertyGroup _grabProperties; + private: std::unordered_map _materials; std::mutex _materialsLock; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7e404afa3f..c243f772e2 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -40,6 +40,7 @@ HazePropertyGroup EntityItemProperties::_staticHaze; BloomPropertyGroup EntityItemProperties::_staticBloom; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; +GrabPropertyGroup EntityItemProperties::_staticGrab; EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); @@ -61,8 +62,8 @@ EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties } -void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { - glm::vec3 halfDimension = (max - min) / 2.0f; +void EntityItemProperties::calculateNaturalPosition(const vec3& min, const vec3& max) { + vec3 halfDimension = (max - min) / 2.0f; _naturalPosition = max - halfDimension; } @@ -86,6 +87,7 @@ void EntityItemProperties::debugDump() const { getKeyLight().debugDump(); getAmbientLight().debugDump(); getBloom().debugDump(); + getGrab().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -471,6 +473,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); changedProperties += _bloom.getChangedProperties(); + changedProperties += _grab.getChangedProperties(); return changedProperties; } @@ -628,6 +631,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * avatar entities: their clientOnly property will be set to true. * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. * + * @property {Entities.Grab} grab - The grab-related properties. + * * @property {string} itemName="" - Certifiable name of the Marketplace item. * @property {string} itemDescription="" - Certifiable description of the Marketplace item. * @property {string} itemCategories="" - Certifiable category of the Marketplace item. @@ -872,9 +877,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @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. If emitSpeed == 5 * and speedSpread == 1, particles will be emitted with speeds in the range 4m/s – 6m/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. If + * @property {Vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. If * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == * {x: 0, y: 1, z: 0}, each particle will have an acceleration in the range {x: 0, y: -10.8, z: 0} * – {x: 0, y: -8.8, z: 0}. @@ -887,7 +892,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * 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 {vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted. + * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted. * @property {number} emitRadiusStart=1 - The starting radius within the ellipsoid at which particles start being emitted; * range 0.01.0 for the ellipsoid center to the ellipsoid surface, respectively. * Particles are emitted from the portion of the ellipsoid that lies between emitRadiusStart and the @@ -916,9 +921,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * and radiusSpread == 0.25, each particle will have a radius in the range 0.25 – * 0.75. * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. - * @property {Color} colorStart={} - The color of each particle at the start of its life. If any of the component values are + * @property {ColorFloat} colorStart={} - 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 {Color} colorFinish={} - The color of each particle at the end of its life. If any of the component values are + * @property {ColorFloat} colorFinish={} - 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. If * color == {red: 100, green: 100, blue: 100} and colorSpread == @@ -1314,9 +1319,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); - // Light, Line, Model, ParticleEffect, PolyLine, Shape - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR, color); - // Particles only if (_type == EntityTypes::ParticleEffect) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTING_PARTICLES, isEmitting); @@ -1338,9 +1340,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_SPREAD, radiusSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_START, radiusStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_FINISH, radiusFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_SPREAD, colorSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_START, colorStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_FINISH, colorFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_SPREAD, colorSpread, u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_START, colorStart, vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_FINISH, colorFinish, vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); @@ -1363,6 +1366,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); } if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) { @@ -1379,6 +1383,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); } // FIXME - it seems like ParticleEffect should also support this @@ -1393,6 +1398,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Lights only if (_type == EntityTypes::Light) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_SPOTLIGHT, isSpotlight); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_INTENSITY, intensity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FALLOFF_RADIUS, falloffRadius); @@ -1404,8 +1410,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Text) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_COLOR, textColor, getTextColor()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), u8vec3Color); } // Zones only @@ -1459,10 +1465,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Lines & PolyLines if (_type == EntityTypes::Line || _type == EntityTypes::PolyLine) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_WIDTH, lineWidth); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); // Polyline only. - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_COLORS, strokeColors); // Polyline only. + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_STROKE_COLORS, strokeColors, qVectorVec3Color); // Polyline only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); // Polyline only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); // Polyline only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); // Polyline only. @@ -1493,10 +1500,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool AABox aaBox = getAABox(); QScriptValue boundingBox = engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); + QScriptValue bottomRightNear = vec3ToScriptValue(engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3ToScriptValue(engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3ToScriptValue(engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3ToScriptValue(engine, aaBox.getDimensions()); boundingBox.setProperty("brn", bottomRightNear); boundingBox.setProperty("tfl", topFarLeft); boundingBox.setProperty("center", center); @@ -1530,6 +1537,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID); + _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + // Rendering info if (!skipDefaults && !strictSemantics && (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::RenderInfo))) { @@ -1598,10 +1607,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); COPY_PROPERTY_FROM_QSCRIPTVALUE(visible, bool, setVisible); COPY_PROPERTY_FROM_QSCRIPTVALUE(canCastShadow, bool, setCanCastShadow); - COPY_PROPERTY_FROM_QSCRIPTVALUE(color, xColor, setColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, xColor, setColorSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, vec3, setColorStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorFinish, vec3, setColorFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(color, u8vec3Color, setColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, u8vec3Color, setColorSpread); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, vec3Color, setColorStart); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorFinish, vec3Color, setColorFinish); COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); @@ -1626,8 +1635,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData); COPY_PROPERTY_FROM_QSCRIPTVALUE(text, QString, setText); COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, xColor, setBackgroundColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, u8vec3Color, setTextColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, u8vec3Color, setBackgroundColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, ShapeType); COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles); COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan); @@ -1700,7 +1709,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeColors, qVectorVec3, setStrokeColors); COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths,qVectorFloat, setStrokeWidths); COPY_PROPERTY_FROM_QSCRIPTVALUE(isUVModeStretch, bool, setIsUVModeStretch); - + if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. @@ -1718,6 +1727,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _skybox.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); _bloom.copyFromScriptValue(object, _defaultSettings); + _grab.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); @@ -1883,6 +1893,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _skybox.merge(other._skybox); _haze.merge(other._haze); _bloom.merge(other._bloom); + _grab.merge(other._grab); COPY_PROPERTY_IF_CHANGED(xTextureURL); COPY_PROPERTY_IF_CHANGED(yTextureURL); @@ -1973,13 +1984,13 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue std::call_once(initMap, [](){ ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool); ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool); - ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, glm::quat); + ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); + ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, vec3); + ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, quat); ADD_PROPERTY_TO_MAP(PROP_DENSITY, Density, density, float); - ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, vec3); + ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, vec3); + ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, vec3); ADD_PROPERTY_TO_MAP(PROP_DAMPING, Damping, damping, float); ADD_PROPERTY_TO_MAP(PROP_RESTITUTION, Restitution, restitution, float); ADD_PROPERTY_TO_MAP(PROP_FRICTION, Friction, friction, float); @@ -1988,10 +1999,10 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); ADD_PROPERTY_TO_MAP(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString); ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); - ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, xColor); - ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor); - ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, vec3); - ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3); + ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, u8vec3Color); + 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(PROP_ALPHA, Alpha, alpha, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); @@ -1999,8 +2010,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); - ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3); + ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, vec3); ADD_PROPERTY_TO_MAP(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, unused); // legacy support @@ -2019,24 +2030,24 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner); ADD_PROPERTY_TO_MAP(PROP_TEXT, Text, text, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); - ADD_PROPERTY_TO_MAP(PROP_TEXT_COLOR, TextColor, textColor, xColor); - ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color); + ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType); ADD_PROPERTY_TO_MAP(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32); ADD_PROPERTY_TO_MAP(PROP_LIFESPAN, Lifespan, lifespan, float); ADD_PROPERTY_TO_MAP(PROP_EMITTING_PARTICLES, IsEmitting, isEmitting, bool); ADD_PROPERTY_TO_MAP(PROP_EMIT_RATE, EmitRate, emitRate, float); - ADD_PROPERTY_TO_MAP(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat); - ADD_PROPERTY_TO_MAP(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3); + ADD_PROPERTY_TO_MAP(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, quat); + ADD_PROPERTY_TO_MAP(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3); ADD_PROPERTY_TO_MAP(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float); ADD_PROPERTY_TO_MAP(PROP_POLAR_START, EmitPolarStart, polarStart, float); ADD_PROPERTY_TO_MAP(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float); ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float); ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float); - ADD_PROPERTY_TO_MAP(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3); + ADD_PROPERTY_TO_MAP(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3); ADD_PROPERTY_TO_MAP(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); @@ -2072,24 +2083,24 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString); ADD_PROPERTY_TO_MAP(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32); - ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); - ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, vec3); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_CAST_SHADOW, KeyLightCastShadows, keyLightCastShadows, bool); - ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); - ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); + ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); ADD_PROPERTY_TO_MAP(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool); ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); - ADD_PROPERTY_TO_MAP(PROP_NORMALS, Normals, normals, QVector); - ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); + ADD_PROPERTY_TO_MAP(PROP_NORMALS, Normals, normals, QVector); + ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); ADD_PROPERTY_TO_MAP(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector); ADD_PROPERTY_TO_MAP(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, QVector); ADD_PROPERTY_TO_MAP(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); @@ -2105,16 +2116,16 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_PARENT_ID, ParentID, parentID, QUuid); ADD_PROPERTY_TO_MAP(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_POSITION, LocalPosition, localPosition, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glm::quat); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_POSITION, LocalPosition, localPosition, vec3); + 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(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3); ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); + ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); + 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_SHAPE, Shape, shape, QString); @@ -2172,6 +2183,26 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool); ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableLeftPosition, equippableLeftPosition); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableLeftRotation, equippableLeftRotation); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableRightPosition, equippableRightPosition); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableRightRotation, equippableRightRotation); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, + EquippableIndicatorURL, equippableIndicatorURL); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, + EquippableIndicatorScale, equippableIndicatorScale); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, + EquippableIndicatorOffset, equippableIndicatorOffset); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2224,7 +2255,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy // Always include the root octcode. This is only because the OctreeEditPacketSender will check these octcodes // to determine which server to send the changes to in the case of multiple jurisdictions. The root will be sent // to all servers. - glm::vec3 rootPosition(0); + vec3 rootPosition(0); float rootScale = 0.5f; unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale); @@ -2309,7 +2340,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, properties.getServerScripts()); - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, properties.getAngularDamping()); @@ -2344,6 +2374,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); _staticAnimation.setProperties(properties); _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -2385,6 +2416,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, properties.getRadiusSpread()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, properties.getRadiusStart()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, properties.getRadiusFinish()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, properties.getColorSpread()); APPEND_ENTITY_PROPERTY(PROP_COLOR_START, properties.getColorStart()); APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, properties.getColorFinish()); @@ -2445,11 +2477,13 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy } if (properties.getType() == EntityTypes::Line) { + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, properties.getLineWidth()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); } if (properties.getType() == EntityTypes::PolyLine) { + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, properties.getLineWidth()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); APPEND_ENTITY_PROPERTY(PROP_NORMALS, properties.getPackedNormals()); @@ -2464,6 +2498,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy properties.getType() == EntityTypes::Box || properties.getType() == EntityTypes::Sphere) { APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); } // Materials @@ -2501,6 +2536,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit()); APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic()); APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity()); + + _staticGrab.setProperties(properties); + _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, + propertiesDidntFit, propertyCount, appendState); } if (propertyCount > 0) { @@ -2562,7 +2601,7 @@ QByteArray EntityItemProperties::getPackedNormals() const { return packNormals(getNormals()); } -QByteArray EntityItemProperties::packNormals(const QVector& normals) const { +QByteArray EntityItemProperties::packNormals(const QVector& normals) const { int normalsSize = normals.size(); QByteArray packedNormals = QByteArray(normalsSize * 6 + 1, '0'); // add size of the array @@ -2579,7 +2618,7 @@ QByteArray EntityItemProperties::packNormals(const QVector& normals) QByteArray EntityItemProperties::getPackedStrokeColors() const { return packStrokeColors(getStrokeColors()); } -QByteArray EntityItemProperties::packStrokeColors(const QVector& strokeColors) const { +QByteArray EntityItemProperties::packStrokeColors(const QVector& strokeColors) const { int strokeColorsSize = strokeColors.size(); QByteArray packedStrokeColors = QByteArray(strokeColorsSize * 3 + 1, '0'); @@ -2589,9 +2628,9 @@ QByteArray EntityItemProperties::packStrokeColors(const QVector& stro for (int i = 0; i < strokeColorsSize; i++) { // add the color to the QByteArray - packedStrokeColors[i * 3 + 1] = strokeColors[i].r * 255; - packedStrokeColors[i * 3 + 2] = strokeColors[i].g * 255; - packedStrokeColors[i * 3 + 3] = strokeColors[i].b * 255; + packedStrokeColors[i * 3 + 1] = strokeColors[i].x * 255; + packedStrokeColors[i * 3 + 2] = strokeColors[i].y * 255; + packedStrokeColors[i * 3 + 3] = strokeColors[i].z * 255; } return packedStrokeColors; } @@ -2680,13 +2719,13 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int processedBytes += propertyFlags.getEncodedLength(); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, vec3, setPosition); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, vec3, setDimensions); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, quat, setRotation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION, glm::vec3, setAcceleration); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, vec3, setVelocity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, vec3, setGravity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION, vec3, setAcceleration); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RESTITUTION, float, setRestitution); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FRICTION, float, setFriction); @@ -2694,9 +2733,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SERVER_SCRIPTS, QString, setServerScripts); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, vec3, setRegistrationPoint); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, vec3, setAngularVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); @@ -2719,8 +2757,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Text) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, xColor, setTextColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, xColor, setBackgroundColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FACE_CAMERA, bool, setFaceCamera); } @@ -2729,19 +2767,20 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); } if (properties.getType() == EntityTypes::Light) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FALLOFF_RADIUS, float, setFalloffRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent); @@ -2756,22 +2795,23 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_SPEED, float, setEmitSpeed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPEED_SPREAD, float, setSpeedSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, glm::quat, setEmitOrientation); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, vec3, setEmitDimensions); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_START, float, setPolarStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, vec3, setAccelerationSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_SPREAD, float, setRadiusSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_START, float, setRadiusStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_FINISH, float, setRadiusFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, xColor, setColorSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, vec3, setColorStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, vec3, setColorFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, vec3Color, setColorStart); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, vec3Color, setColorFinish); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); @@ -2794,7 +2834,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL); - + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); @@ -2807,7 +2847,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::PolyVox) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, vec3, setVoxelVolumeSize); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_DATA, QByteArray, setVoxelData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); @@ -2822,14 +2862,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::Line) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_WIDTH, float, setLineWidth); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); } if (properties.getType() == EntityTypes::PolyLine) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_WIDTH, float, setLineWidth); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NORMALS, QByteArray, setPackedNormals); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_COLORS, QByteArray, setPackedStrokeColors); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); @@ -2843,6 +2885,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getType() == EntityTypes::Box || properties.getType() == EntityTypes::Sphere) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); } // Materials @@ -2881,6 +2924,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); + properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + return valid; } @@ -2888,14 +2933,14 @@ void EntityItemProperties::setPackedNormals(const QByteArray& value) { setNormals(unpackNormals(value)); } -QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { +QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { // the size of the vector is packed first - QVector unpackedNormals = QVector((int)normals[0]); + QVector unpackedNormals = QVector((int)normals[0]); if ((int)normals[0] == normals.size() / 6) { int j = 0; for (int i = 1; i < normals.size();) { - glm::vec3 aux = glm::vec3(); + vec3 aux = vec3(); i += unpackFloatVec3FromSignedTwoByteFixed((unsigned char*)normals.data() + i, aux, 15); unpackedNormals[j] = aux; j++; @@ -2911,9 +2956,9 @@ void EntityItemProperties::setPackedStrokeColors(const QByteArray& value) { setStrokeColors(unpackStrokeColors(value)); } -QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { +QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { // the size of the vector is packed first - QVector unpackedStrokeColors = QVector((int)strokeColors[0]); + QVector unpackedStrokeColors = QVector((int)strokeColors[0]); if ((int)strokeColors[0] == strokeColors.size() / 3) { int j = 0; @@ -3120,6 +3165,7 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _haze.markAllChanged(); _bloom.markAllChanged(); + _grab.markAllChanged(); _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; @@ -3161,6 +3207,8 @@ void EntityItemProperties::markAllChanged() { _queryAACubeChanged = true; + _shapeChanged = true; + _flyingAllowedChanged = true; _ghostingAllowedChanged = true; _filterURLChanged = true; @@ -3185,10 +3233,10 @@ void EntityItemProperties::markAllChanged() { AABox EntityItemProperties::getAABox() const { // _position represents the position of the registration point. - glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint; + vec3 registrationRemainder = vec3(1.0f) - _registrationPoint; - glm::vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint); - glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; + vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint); + vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(_rotation); @@ -3198,9 +3246,12 @@ AABox EntityItemProperties::getAABox() const { return AABox(rotatedExtentsRelativeToRegistrationPoint); } -bool EntityItemProperties::hasTerseUpdateChanges() const { - // a TerseUpdate includes the transform and its derivatives - return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged; +bool EntityItemProperties::hasTransformOrVelocityChanges() const { + return _positionChanged ||_localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged + || _accelerationChanged; } bool EntityItemProperties::hasMiscPhysicsChanges() const { @@ -3209,6 +3260,56 @@ bool EntityItemProperties::hasMiscPhysicsChanges() const { _compoundShapeURLChanged || _dynamicChanged || _collisionlessChanged || _collisionMaskChanged; } +bool EntityItemProperties::hasSimulationRestrictedChanges() const { + return _positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged + || _accelerationChanged + || _parentIDChanged || _parentJointIndexChanged; +} + +void EntityItemProperties::copySimulationRestrictedProperties(const EntityItemPointer& entity) { + if (!_parentIDChanged) { + setParentID(entity->getParentID()); + } + if (!_parentJointIndexChanged) { + setParentJointIndex(entity->getParentJointIndex()); + } + if (!_localPositionChanged && !_positionChanged) { + setPosition(entity->getWorldPosition()); + } + if (!_localRotationChanged && !_rotationChanged) { + setRotation(entity->getWorldOrientation()); + } + if (!_localVelocityChanged && !_velocityChanged) { + setVelocity(entity->getWorldVelocity()); + } + if (!_localAngularVelocityChanged && !_angularVelocityChanged) { + setAngularVelocity(entity->getWorldAngularVelocity()); + } + if (!_accelerationChanged) { + setAcceleration(entity->getAcceleration()); + } + if (!_localDimensionsChanged && !_dimensionsChanged) { + setDimensions(entity->getScaledDimensions()); + } +} + +void EntityItemProperties::clearSimulationRestrictedProperties() { + _positionChanged = false; + _localPositionChanged = false; + _rotationChanged = false; + _localRotationChanged = false; + _velocityChanged = false; + _localVelocityChanged = false; + _angularVelocityChanged = false; + _localAngularVelocityChanged = false; + _accelerationChanged = false; + _parentIDChanged = false; + _parentJointIndexChanged = false; +} + void EntityItemProperties::clearSimulationOwner() { _simulationOwner.clear(); _simulationOwnerChanged = true; @@ -3227,6 +3328,20 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) { } } +uint8_t EntityItemProperties::computeSimulationBidPriority() const { + uint8_t priority = 0; + if (_parentIDChanged || _parentJointIndexChanged) { + // we need higher simulation ownership priority to chang parenting info + priority = SCRIPT_GRAB_SIMULATION_PRIORITY; + } else if ( _positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged) { + priority = SCRIPT_POKE_SIMULATION_PRIORITY; + } + return priority; +} + QList EntityItemProperties::listChangedProperties() { QList out; if (containsPositionChange()) { @@ -3654,6 +3769,7 @@ QList EntityItemProperties::listChangedProperties() { getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); getBloom().listChangedProperties(out); + getGrab().listChangedProperties(out); return out; } @@ -3689,6 +3805,16 @@ bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const { return parentRelatedPropertyChanged() || dimensionsChanged(); } +bool EntityItemProperties::grabbingRelatedPropertyChanged() const { + const GrabPropertyGroup& grabProperties = getGrab(); + return grabProperties.triggerableChanged() || grabProperties.grabbableChanged() || + grabProperties.grabFollowsControllerChanged() || grabProperties.grabKinematicChanged() || + grabProperties.equippableChanged() || grabProperties.equippableLeftPositionChanged() || + grabProperties.equippableRightPositionChanged() || grabProperties.equippableLeftRotationChanged() || + grabProperties.equippableRightRotationChanged() || grabProperties.equippableIndicatorURLChanged() || + grabProperties.equippableIndicatorScaleChanged() || grabProperties.equippableIndicatorOffsetChanged(); +} + // Checking Certifiable Properties #define ADD_STRING_PROPERTY(n, N) if (!get##N().isEmpty()) json[#n] = get##N() #define ADD_ENUM_PROPERTY(n, N) json[#n] = get##N##AsString() diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 338ec5e071..c91ccda5aa 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -58,6 +58,9 @@ const std::array COMPONENT_MODES = { { ComponentPair { COMPONENT_MODE_ENABLED, { "enabled" } } } }; +using vec3Color = glm::vec3; +using u8vec3Color = glm::u8vec3; + /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete /// set of entity item properties via JavaScript hashes/QScriptValues @@ -105,6 +108,7 @@ public: bool getScalesWithParent() const; bool parentRelatedPropertyChanged() const; bool queryAACubeRelatedPropertyChanged() const; + bool grabbingRelatedPropertyChanged() const; AABox getAABox() const; @@ -137,10 +141,10 @@ public: DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT); DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP); DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL); - DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor, ParticleEffectEntityItem::DEFAULT_XCOLOR); - DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor, ParticleEffectEntityItem::DEFAULT_XCOLOR_SPREAD); - DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, vec3, particle::DEFAULT_COLOR_UNINITIALIZED); - DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3, particle::DEFAULT_COLOR_UNINITIALIZED); + DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR); + DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD); + DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); + DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA); DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD); DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START); @@ -164,8 +168,8 @@ public: DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner, SimulationOwner()); DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT); DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT); - DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor, TextEntityItem::DEFAULT_TEXT_COLOR); - DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR); + DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); + DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR); DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType, SHAPE_TYPE_NONE); DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES); DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float, particle::DEFAULT_LIFESPAN); @@ -206,7 +210,7 @@ public: DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); - DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, QVector()); + DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool, TextEntityItem::DEFAULT_FACE_CAMERA); @@ -233,8 +237,8 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, materialMappingMode, MaterialMappingMode, UV); DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, quint16, 0); DEFINE_PROPERTY_REF(PROP_PARENT_MATERIAL_NAME, ParentMaterialName, parentMaterialName, QString, "0"); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_POS, MaterialMappingPos, materialMappingPos, vec2, glm::vec2(0, 0)); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_SCALE, MaterialMappingScale, materialMappingScale, vec2, glm::vec2(1, 1)); + DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_POS, MaterialMappingPos, materialMappingPos, glm::vec2, glm::vec2(0.0f)); + DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_SCALE, MaterialMappingScale, materialMappingScale, glm::vec2, glm::vec2(1.0f)); DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_ROT, MaterialMappingRot, materialMappingRot, float, 0); DEFINE_PROPERTY_REF(PROP_MATERIAL_DATA, MaterialData, materialData, QString, ""); @@ -260,16 +264,16 @@ public: DEFINE_PROPERTY_REF(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32, ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION); // these are used when bouncing location data into and out of scripts - DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat, ENTITY_ITEM_DEFAULT_ROTATION); - DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, QVector()); + DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); @@ -292,6 +296,8 @@ public: DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); + DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); + static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); @@ -344,13 +350,18 @@ public: void setCreated(QDateTime& v); - bool hasTerseUpdateChanges() const; + bool hasTransformOrVelocityChanges() const; bool hasMiscPhysicsChanges() const; + bool hasSimulationRestrictedChanges() const; + void copySimulationRestrictedProperties(const EntityItemPointer& entity); + void clearSimulationRestrictedProperties(); + void clearSimulationOwner(); void setSimulationOwner(const QUuid& id, uint8_t priority); void setSimulationOwner(const QByteArray& data); void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); } + uint8_t computeSimulationBidPriority() const; void setActionDataDirty() { _actionDataChanged = true; } diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index ff4fb39354..d3a2dc6cec 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -101,8 +101,11 @@ changedProperties += P; \ } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec2& v) { return vec2toScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3toScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec2& v) { return vec2ToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3ToScriptValue(e, v); } +inline QScriptValue vec3Color_convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3ColorToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::u8vec3& v) { return u8vec3ToScriptValue(e, v); } +inline QScriptValue u8vec3Color_convertScriptValue(QScriptEngine* e, const glm::u8vec3& v) { return u8vec3ColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, bool v) { return QScriptValue(v); } @@ -111,10 +114,10 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QSc inline QScriptValue convertScriptValue(QScriptEngine* e, quint64 v) { return QScriptValue((qsreal)v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QScriptValue& v) { return v; } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ToScriptValue(e, v); } +inline QScriptValue qVectorVec3Color_convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ColorToScriptValue(e, v); } 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); } @@ -128,8 +131,6 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); } - - #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ @@ -142,6 +143,18 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu properties.setProperty(#g, groupProperties); \ } +#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(X,G,g,P,p,T) \ + if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ + (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ + QScriptValue groupProperties = properties.property(#g); \ + if (!groupProperties.isValid()) { \ + groupProperties = engine->newObject(); \ + } \ + QScriptValue V = T##_convertScriptValue(engine, get##P()); \ + groupProperties.setProperty(#p, V); \ + properties.setProperty(#g, groupProperties); \ + } + #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(X,G,g,P,p,M) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ @@ -161,6 +174,13 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu properties.setProperty(#P, V); \ } +#define COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(p,P,T) \ + if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ + QScriptValue V = T##_convertScriptValue(engine, _##P); \ + properties.setProperty(#P, V); \ + } + #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(P, G) \ properties.setProperty(#P, G); @@ -171,6 +191,13 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu properties.setProperty(#P, V); \ } +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(p, P, G, T) \ + if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ + QScriptValue V = T##_convertScriptValue(engine, G); \ + properties.setProperty(#P, V); \ + } + // same as COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER but uses #X instead of #P in the setProperty() step #define COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, X, G) \ if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ @@ -205,7 +232,6 @@ inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) inline EntityItemID EntityItemID_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } - inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; auto result = QDateTime::fromString(v.toVariant().toString().trimmed(), Qt::ISODate); @@ -213,8 +239,6 @@ inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& i return result; } - - inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; QString b64 = v.toVariant().toString().trimmed(); @@ -222,49 +246,31 @@ inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& } inline glm::vec2 vec2_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - isValid = false; /// assume it can't be converted - QScriptValue x = v.property("x"); - QScriptValue y = v.property("y"); - if (x.isValid() && y.isValid()) { - glm::vec4 newValue(0); - newValue.x = x.toVariant().toFloat(); - newValue.y = y.toVariant().toFloat(); - isValid = !glm::isnan(newValue.x) && - !glm::isnan(newValue.y); - if (isValid) { - return newValue; - } - } - return glm::vec2(0); + isValid = true; + glm::vec2 vec2; + vec2FromScriptValue(v, vec2); + return vec2; } inline glm::vec3 vec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - isValid = false; /// assume it can't be converted - QScriptValue x = v.property("x"); - QScriptValue y = v.property("y"); - QScriptValue z = v.property("z"); - if (!x.isValid()) { - x = v.property("red"); - } - if (!y.isValid()) { - y = v.property("green"); - } - if (!z.isValid()) { - z = v.property("blue"); - } - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newValue(0); - newValue.x = x.toVariant().toFloat(); - newValue.y = y.toVariant().toFloat(); - newValue.z = z.toVariant().toFloat(); - isValid = !glm::isnan(newValue.x) && - !glm::isnan(newValue.y) && - !glm::isnan(newValue.z); - if (isValid) { - return newValue; - } - } - return glm::vec3(0); + isValid = true; + glm::vec3 vec3; + vec3FromScriptValue(v, vec3); + return vec3; +} + +inline glm::vec3 vec3Color_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + glm::vec3 vec3; + vec3FromScriptValue(v, vec3); + return vec3; +} + +inline glm::u8vec3 u8vec3Color_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + glm::u8vec3 vec3; + u8vec3FromScriptValue(v, vec3); + return vec3; } inline AACube AACube_convertFromScriptValue(const QScriptValue& v, bool& isValid) { @@ -317,31 +323,6 @@ inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isVali return glm::quat(); } -inline xColor xColor_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - xColor newValue { 255, 255, 255 }; - isValid = false; /// assume it can't be converted - QScriptValue r = v.property("red"); - QScriptValue g = v.property("green"); - QScriptValue b = v.property("blue"); - if (!r.isValid()) { - r = v.property("x"); - } - if (!g.isValid()) { - g = v.property("y"); - } - if (!b.isValid()) { - b = v.property("z"); - } - if (r.isValid() && g.isValid() && b.isValid()) { - newValue.red = r.toVariant().toInt(); - newValue.green = g.toVariant().toInt(); - newValue.blue = b.toVariant().toInt(); - isValid = true; - } - return newValue; -} - - #define COPY_PROPERTY_IF_CHANGED(P) \ { \ if (other._##P##Changed) { \ diff --git a/libraries/entities/src/EntityPropertyFlags.cpp b/libraries/entities/src/EntityPropertyFlags.cpp new file mode 100644 index 0000000000..c077b153b8 --- /dev/null +++ b/libraries/entities/src/EntityPropertyFlags.cpp @@ -0,0 +1,207 @@ + +#include "EntityPropertyFlags.h" + + +QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { + QString result = "[ "; + + result = f.getHasProperty(PROP_PAGED_PROPERTY) ? result + "pagedProperty " : result; + result = f.getHasProperty(PROP_CUSTOM_PROPERTIES_INCLUDED) ? result + "customPropertiesIncluded " : result; + result = f.getHasProperty(PROP_VISIBLE) ? result + "visible " : result; + result = f.getHasProperty(PROP_CAN_CAST_SHADOW) ? result + "canCastShadow " : result; + result = f.getHasProperty(PROP_POSITION) ? result + "position " : result; + result = f.getHasProperty(PROP_DIMENSIONS) ? result + "dimensions " : result; + result = f.getHasProperty(PROP_ROTATION) ? result + "rotation " : result; + result = f.getHasProperty(PROP_DENSITY) ? result + "density " : result; + result = f.getHasProperty(PROP_VELOCITY) ? result + "velocity " : result; + result = f.getHasProperty(PROP_GRAVITY) ? result + "gravity " : result; + result = f.getHasProperty(PROP_DAMPING) ? result + "damping " : result; + result = f.getHasProperty(PROP_LIFETIME) ? result + "lifetime " : result; + result = f.getHasProperty(PROP_SCRIPT) ? result + "script " : result; + result = f.getHasProperty(PROP_COLOR) ? result + "color " : result; + result = f.getHasProperty(PROP_MODEL_URL) ? result + "modelUrl " : result; + result = f.getHasProperty(PROP_ANIMATION_URL) ? result + "animationUrl " : result; + result = f.getHasProperty(PROP_ANIMATION_FPS) ? result + "animationFps " : result; + result = f.getHasProperty(PROP_ANIMATION_FRAME_INDEX) ? result + "animationFrameIndex " : result; + result = f.getHasProperty(PROP_ANIMATION_PLAYING) ? result + "animationPlaying " : result; + result = f.getHasProperty(PROP_ANIMATION_ALLOW_TRANSLATION) ? result + "animationAllowTranslation " : result; + result = f.getHasProperty(PROP_RELAY_PARENT_JOINTS) ? result + "relayParentJoints " : result; + result = f.getHasProperty(PROP_REGISTRATION_POINT) ? result + "registrationPoint " : result; + result = f.getHasProperty(PROP_ANGULAR_VELOCITY) ? result + "angularVelocity " : result; + result = f.getHasProperty(PROP_ANGULAR_DAMPING) ? result + "angularDamping " : result; + result = f.getHasProperty(PROP_COLLISIONLESS) ? result + "collisionless " : result; + result = f.getHasProperty(PROP_DYNAMIC) ? result + "dynamic " : result; + result = f.getHasProperty(PROP_IS_SPOTLIGHT) ? result + "isSpotlight " : result; + result = f.getHasProperty(PROP_DIFFUSE_COLOR) ? result + "diffuseColor " : result; + result = f.getHasProperty(PROP_AMBIENT_COLOR_UNUSED) ? result + "ambientColorUnused " : result; + result = f.getHasProperty(PROP_SPECULAR_COLOR_UNUSED) ? result + "specularColorUnused " : result; + result = f.getHasProperty(PROP_INTENSITY) ? result + "intensity " : result; + result = f.getHasProperty(PROP_LINEAR_ATTENUATION_UNUSED) ? result + "linearAttenuationUnused " : result; + result = f.getHasProperty(PROP_QUADRATIC_ATTENUATION_UNUSED) ? result + "quadraticAttenuationUnused " : result; + result = f.getHasProperty(PROP_EXPONENT) ? result + "exponent " : result; + result = f.getHasProperty(PROP_CUTOFF) ? result + "cutoff " : result; + result = f.getHasProperty(PROP_LOCKED) ? result + "locked " : result; + result = f.getHasProperty(PROP_TEXTURES) ? result + "textures " : result; + result = f.getHasProperty(PROP_ANIMATION_SETTINGS_UNUSED) ? result + "animationSettingsUnused " : result; + result = f.getHasProperty(PROP_USER_DATA) ? result + "userData " : result; + result = f.getHasProperty(PROP_SHAPE_TYPE) ? result + "shapeType " : result; + result = f.getHasProperty(PROP_MAX_PARTICLES) ? result + "maxParticles " : result; + result = f.getHasProperty(PROP_LIFESPAN) ? result + "lifespan " : result; + result = f.getHasProperty(PROP_EMIT_RATE) ? result + "emitRate " : result; + result = f.getHasProperty(PROP_EMIT_SPEED) ? result + "emitSpeed " : result; + result = f.getHasProperty(PROP_EMIT_STRENGTH) ? result + "emitStrength " : result; + result = f.getHasProperty(PROP_EMIT_ACCELERATION) ? result + "emitAcceleration " : result; + result = f.getHasProperty(PROP_PARTICLE_RADIUS) ? result + "particleRadius " : result; + result = f.getHasProperty(PROP_COMPOUND_SHAPE_URL) ? result + "compoundShapeUrl " : result; + result = f.getHasProperty(PROP_MARKETPLACE_ID) ? result + "marketplaceID " : result; + result = f.getHasProperty(PROP_ACCELERATION) ? result + "acceleration " : result; + result = f.getHasProperty(PROP_SIMULATION_OWNER) ? result + "simulationOwner " : result; + result = f.getHasProperty(PROP_NAME) ? result + "name " : result; + result = f.getHasProperty(PROP_COLLISION_SOUND_URL) ? result + "collisionSoundUrl " : result; + result = f.getHasProperty(PROP_RESTITUTION) ? result + "restitution " : result; + result = f.getHasProperty(PROP_FRICTION) ? result + "friction " : result; + result = f.getHasProperty(PROP_VOXEL_VOLUME_SIZE) ? result + "voxelVolumeSize " : result; + result = f.getHasProperty(PROP_VOXEL_DATA) ? result + "voxelData " : result; + result = f.getHasProperty(PROP_VOXEL_SURFACE_STYLE) ? result + "voxelSurfaceStyle " : result; + result = f.getHasProperty(PROP_LINE_WIDTH) ? result + "lineWidth " : result; + result = f.getHasProperty(PROP_LINE_POINTS) ? result + "linePoints " : result; + result = f.getHasProperty(PROP_HREF) ? result + "href " : result; + result = f.getHasProperty(PROP_DESCRIPTION) ? result + "description " : result; + result = f.getHasProperty(PROP_FACE_CAMERA) ? result + "faceCamera " : result; + result = f.getHasProperty(PROP_SCRIPT_TIMESTAMP) ? result + "scriptTimestamp " : result; + result = f.getHasProperty(PROP_ACTION_DATA) ? result + "actionData " : result; + result = f.getHasProperty(PROP_X_TEXTURE_URL) ? result + "xTextureUrl " : result; + result = f.getHasProperty(PROP_Y_TEXTURE_URL) ? result + "yTextureUrl " : result; + result = f.getHasProperty(PROP_Z_TEXTURE_URL) ? result + "zTextureUrl " : result; + result = f.getHasProperty(PROP_NORMALS) ? result + "normals " : result; + result = f.getHasProperty(PROP_STROKE_COLORS) ? result + "strokeColors " : result; + result = f.getHasProperty(PROP_STROKE_WIDTHS) ? result + "strokeWidths " : result; + result = f.getHasProperty(PROP_IS_UV_MODE_STRETCH) ? result + "isUvModeStretch " : result; + result = f.getHasProperty(PROP_SPEED_SPREAD) ? result + "speedSpread " : result; + result = f.getHasProperty(PROP_ACCELERATION_SPREAD) ? result + "accelerationSpread " : result; + result = f.getHasProperty(PROP_X_N_NEIGHBOR_ID) ? result + "xNNeighborID " : result; + result = f.getHasProperty(PROP_Y_N_NEIGHBOR_ID) ? result + "yNNeighborID " : result; + result = f.getHasProperty(PROP_Z_N_NEIGHBOR_ID) ? result + "zNNeighborID " : result; + result = f.getHasProperty(PROP_X_P_NEIGHBOR_ID) ? result + "xPNeighborID " : result; + result = f.getHasProperty(PROP_Y_P_NEIGHBOR_ID) ? result + "yPNeighborID " : result; + result = f.getHasProperty(PROP_Z_P_NEIGHBOR_ID) ? result + "zPNeighborID " : result; + result = f.getHasProperty(PROP_RADIUS_SPREAD) ? result + "radiusSpread " : result; + result = f.getHasProperty(PROP_RADIUS_START) ? result + "radiusStart " : result; + result = f.getHasProperty(PROP_RADIUS_FINISH) ? result + "radiusFinish " : result; + result = f.getHasProperty(PROP_ALPHA) ? result + "alpha " : result; + result = f.getHasProperty(PROP_COLOR_SPREAD) ? result + "colorSpread " : result; + result = f.getHasProperty(PROP_COLOR_START) ? result + "colorStart " : result; + result = f.getHasProperty(PROP_COLOR_FINISH) ? result + "colorFinish " : result; + result = f.getHasProperty(PROP_ALPHA_SPREAD) ? result + "alphaSpread " : result; + result = f.getHasProperty(PROP_ALPHA_START) ? result + "alphaStart " : result; + result = f.getHasProperty(PROP_ALPHA_FINISH) ? result + "alphaFinish " : result; + result = f.getHasProperty(PROP_EMIT_ORIENTATION) ? result + "emitOrientation " : result; + result = f.getHasProperty(PROP_EMIT_DIMENSIONS) ? result + "emitDimensions " : result; + result = f.getHasProperty(PROP_EMIT_RADIUS_START) ? result + "emitRadiusStart " : result; + result = f.getHasProperty(PROP_POLAR_START) ? result + "polarStart " : result; + result = f.getHasProperty(PROP_POLAR_FINISH) ? result + "polarFinish " : result; + result = f.getHasProperty(PROP_AZIMUTH_START) ? result + "azimuthStart " : result; + result = f.getHasProperty(PROP_AZIMUTH_FINISH) ? result + "azimuthFinish " : result; + result = f.getHasProperty(PROP_ANIMATION_LOOP) ? result + "animationLoop " : result; + result = f.getHasProperty(PROP_ANIMATION_FIRST_FRAME) ? result + "animationFirstFrame " : result; + result = f.getHasProperty(PROP_ANIMATION_LAST_FRAME) ? result + "animationLastFrame " : result; + result = f.getHasProperty(PROP_ANIMATION_HOLD) ? result + "animationHold " : result; + result = f.getHasProperty(PROP_ANIMATION_START_AUTOMATICALLY) ? result + "animationStartAutomatically " : result; + result = f.getHasProperty(PROP_EMITTER_SHOULD_TRAIL) ? result + "emitterShouldTrail " : result; + result = f.getHasProperty(PROP_PARENT_ID) ? result + "parentID " : result; + result = f.getHasProperty(PROP_PARENT_JOINT_INDEX) ? result + "parentJointIndex " : result; + result = f.getHasProperty(PROP_LOCAL_POSITION) ? result + "localPosition " : result; + result = f.getHasProperty(PROP_LOCAL_ROTATION) ? result + "localRotation " : result; + result = f.getHasProperty(PROP_QUERY_AA_CUBE) ? result + "queryAaCube " : result; + result = f.getHasProperty(PROP_JOINT_ROTATIONS_SET) ? result + "jointRotationsSet " : result; + result = f.getHasProperty(PROP_JOINT_ROTATIONS) ? result + "jointRotations " : result; + result = f.getHasProperty(PROP_JOINT_TRANSLATIONS_SET) ? result + "jointTranslationsSet " : result; + result = f.getHasProperty(PROP_JOINT_TRANSLATIONS) ? result + "jointTranslations " : result; + result = f.getHasProperty(PROP_COLLISION_MASK) ? result + "collisionMask " : result; + result = f.getHasProperty(PROP_FALLOFF_RADIUS) ? result + "falloffRadius " : result; + result = f.getHasProperty(PROP_FLYING_ALLOWED) ? result + "flyingAllowed " : result; + result = f.getHasProperty(PROP_GHOSTING_ALLOWED) ? result + "ghostingAllowed " : result; + result = f.getHasProperty(PROP_CLIENT_ONLY) ? result + "clientOnly " : result; + result = f.getHasProperty(PROP_OWNING_AVATAR_ID) ? result + "owningAvatarID " : result; + result = f.getHasProperty(PROP_SHAPE) ? result + "shape " : result; + result = f.getHasProperty(PROP_DPI) ? result + "dpi " : result; + result = f.getHasProperty(PROP_LOCAL_VELOCITY) ? result + "localVelocity " : result; + result = f.getHasProperty(PROP_LOCAL_ANGULAR_VELOCITY) ? result + "localAngularVelocity " : result; + result = f.getHasProperty(PROP_LAST_EDITED_BY) ? result + "lastEditedBy " : result; + result = f.getHasProperty(PROP_SERVER_SCRIPTS) ? result + "serverScripts " : result; + result = f.getHasProperty(PROP_FILTER_URL) ? result + "filterUrl " : result; + result = f.getHasProperty(PROP_ITEM_NAME) ? result + "itemName " : result; + result = f.getHasProperty(PROP_ITEM_DESCRIPTION) ? result + "itemDescription " : result; + result = f.getHasProperty(PROP_ITEM_CATEGORIES) ? result + "itemCategories " : result; + result = f.getHasProperty(PROP_ITEM_ARTIST) ? result + "itemArtist " : result; + result = f.getHasProperty(PROP_ITEM_LICENSE) ? result + "itemLicense " : result; + result = f.getHasProperty(PROP_LIMITED_RUN) ? result + "limitedRun " : result; + result = f.getHasProperty(PROP_EDITION_NUMBER) ? result + "editionNumber " : result; + result = f.getHasProperty(PROP_ENTITY_INSTANCE_NUMBER) ? result + "entityInstanceNumber " : result; + result = f.getHasProperty(PROP_CERTIFICATE_ID) ? result + "certificateID " : result; + result = f.getHasProperty(PROP_STATIC_CERTIFICATE_VERSION) ? result + "staticCertificateVersion " : result; + result = f.getHasProperty(PROP_CLONEABLE) ? result + "cloneable " : result; + result = f.getHasProperty(PROP_CLONE_LIFETIME) ? result + "cloneLifetime " : result; + result = f.getHasProperty(PROP_CLONE_LIMIT) ? result + "cloneLimit " : result; + result = f.getHasProperty(PROP_CLONE_DYNAMIC) ? result + "cloneDynamic " : result; + result = f.getHasProperty(PROP_CLONE_AVATAR_ENTITY) ? result + "cloneAvatarEntity " : result; + result = f.getHasProperty(PROP_CLONE_ORIGIN_ID) ? result + "cloneOriginID " : result; + result = f.getHasProperty(PROP_HAZE_MODE) ? result + "hazeMode " : result; + result = f.getHasProperty(PROP_KEYLIGHT_COLOR) ? result + "keylightColor " : result; + result = f.getHasProperty(PROP_KEYLIGHT_INTENSITY) ? result + "keylightIntensity " : result; + result = f.getHasProperty(PROP_KEYLIGHT_DIRECTION) ? result + "keylightDirection " : result; + result = f.getHasProperty(PROP_KEYLIGHT_CAST_SHADOW) ? result + "keylightCastShadow " : result; + result = f.getHasProperty(PROP_HAZE_RANGE) ? result + "hazeRange " : result; + result = f.getHasProperty(PROP_HAZE_COLOR) ? result + "hazeColor " : result; + result = f.getHasProperty(PROP_HAZE_GLARE_COLOR) ? result + "hazeGlareColor " : result; + result = f.getHasProperty(PROP_HAZE_ENABLE_GLARE) ? result + "hazeEnableGlare " : result; + result = f.getHasProperty(PROP_HAZE_GLARE_ANGLE) ? result + "hazeGlareAngle " : result; + result = f.getHasProperty(PROP_HAZE_ALTITUDE_EFFECT) ? result + "hazeAltitudeEffect " : result; + result = f.getHasProperty(PROP_HAZE_CEILING) ? result + "hazeCeiling " : result; + result = f.getHasProperty(PROP_HAZE_BASE_REF) ? result + "hazeBaseRef " : result; + result = f.getHasProperty(PROP_HAZE_BACKGROUND_BLEND) ? result + "hazeBackgroundBlend " : result; + result = f.getHasProperty(PROP_HAZE_ATTENUATE_KEYLIGHT) ? result + "hazeAttenuateKeylight " : result; + result = f.getHasProperty(PROP_HAZE_KEYLIGHT_RANGE) ? result + "hazeKeylightRange " : result; + result = f.getHasProperty(PROP_HAZE_KEYLIGHT_ALTITUDE) ? result + "hazeKeylightAltitude " : result; + result = f.getHasProperty(PROP_KEY_LIGHT_MODE) ? result + "keyLightMode " : result; + result = f.getHasProperty(PROP_AMBIENT_LIGHT_MODE) ? result + "ambientLightMode " : result; + result = f.getHasProperty(PROP_SKYBOX_MODE) ? result + "skyboxMode " : result; + result = f.getHasProperty(PROP_LOCAL_DIMENSIONS) ? result + "localDimensions " : result; + result = f.getHasProperty(PROP_MATERIAL_URL) ? result + "materialUrl " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_MODE) ? result + "materialMappingMode " : result; + result = f.getHasProperty(PROP_MATERIAL_PRIORITY) ? result + "materialPriority " : result; + result = f.getHasProperty(PROP_PARENT_MATERIAL_NAME) ? result + "parentMaterialName " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_POS) ? result + "materialMappingPos " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_SCALE) ? result + "materialMappingScale " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_ROT) ? result + "materialMappingRot " : result; + result = f.getHasProperty(PROP_MATERIAL_DATA) ? result + "materialData " : result; + result = f.getHasProperty(PROP_VISIBLE_IN_SECONDARY_CAMERA) ? result + "visibleInSecondaryCamera " : result; + result = f.getHasProperty(PROP_PARTICLE_SPIN) ? result + "particleSpin " : result; + result = f.getHasProperty(PROP_SPIN_START) ? result + "spinStart " : result; + result = f.getHasProperty(PROP_SPIN_FINISH) ? result + "spinFinish " : result; + result = f.getHasProperty(PROP_SPIN_SPREAD) ? result + "spinSpread " : result; + result = f.getHasProperty(PROP_PARTICLE_ROTATE_WITH_ENTITY) ? result + "particleRotateWithEntity " : result; + result = f.getHasProperty(PROP_BLOOM_INTENSITY) ? result + "bloomIntensity " : result; + result = f.getHasProperty(PROP_BLOOM_THRESHOLD) ? result + "bloomThreshold " : result; + result = f.getHasProperty(PROP_BLOOM_SIZE) ? result + "bloomSize " : result; + result = f.getHasProperty(PROP_GRAB_GRABBABLE) ? result + "grab.Grabbable " : result; + result = f.getHasProperty(PROP_GRAB_KINEMATIC) ? result + "grab.Kinematic " : result; + result = f.getHasProperty(PROP_GRAB_FOLLOWS_CONTROLLER) ? result + "grab.FollowsController " : result; + result = f.getHasProperty(PROP_GRAB_TRIGGERABLE) ? result + "grab.Triggerable " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE) ? result + "grab.Equippable " : result; + result = + f.getHasProperty(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET) ? result + "grab.LeftEquippablePositionOffset " : result; + result = + f.getHasProperty(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET) ? result + "grab.LeftEquippableRotationOffset " : result; + result = + f.getHasProperty(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET) ? result + "grab.RightEquippablePositionOffset " : result; + result = + f.getHasProperty(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET) ? result + "grab.RightEquippableRotationOffset " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE_INDICATOR_URL) ? result + "grab.EquippableIndicatorURL " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE) ? result + "grab.EquippableIndicatorScale " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET) ? result + "grab.EquippableIndicatorOffset " : result; + + result += "]"; + dbg.nospace() << result; + return dbg; +} diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 3932730661..d2f687fbd3 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -262,6 +262,19 @@ enum EntityPropertyList { PROP_BLOOM_THRESHOLD, PROP_BLOOM_SIZE, + PROP_GRAB_GRABBABLE, + PROP_GRAB_KINEMATIC, + PROP_GRAB_FOLLOWS_CONTROLLER, + PROP_GRAB_TRIGGERABLE, + PROP_GRAB_EQUIPPABLE, + PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, + PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, + PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, + PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, + PROP_GRAB_EQUIPPABLE_INDICATOR_URL, + PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, + PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, @@ -304,5 +317,10 @@ typedef PropertyFlags EntityPropertyFlags; // one greater than the last item property due to the enum's auto-incrementing. extern EntityPropertyList PROP_LAST_ITEM; +QString EntityPropertyFlagsToString(EntityPropertyFlags propertiesFlags); + + +QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f); + #endif // hifi_EntityPropertyFlags_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3ecec89573..3491688588 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -16,6 +16,9 @@ #include #include +#include +#include +#include #include #include @@ -37,6 +40,7 @@ #include "WebEntityItem.h" #include #include +#include "GrabPropertyGroup.h" const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}"; @@ -174,6 +178,7 @@ EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperti } +// TODO: this method looks expensive and should take properties by reference, update it, and return void EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties, bool scalesWithParent) { // convert position and rotation properties from world-space to local, unless localPosition and localRotation @@ -236,19 +241,247 @@ EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProper } +void synchronizeSpatialKey(const GrabPropertyGroup& grabProperties, QJsonObject& grabbableKey, bool& userDataChanged) { + if (grabProperties.equippableLeftPositionChanged() || + grabProperties.equippableRightPositionChanged() || + grabProperties.equippableRightRotationChanged() || + grabProperties.equippableIndicatorURLChanged() || + grabProperties.equippableIndicatorScaleChanged() || + grabProperties.equippableIndicatorOffsetChanged()) { + + QJsonObject spatialKey = grabbableKey["spatialKey"].toObject(); + + if (grabProperties.equippableLeftPositionChanged()) { + if (grabProperties.getEquippableLeftPosition() == INITIAL_LEFT_EQUIPPABLE_POSITION) { + spatialKey.remove("leftRelativePosition"); + } else { + spatialKey["leftRelativePosition"] = + QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableLeftPosition())); + } + } + if (grabProperties.equippableRightPositionChanged()) { + if (grabProperties.getEquippableRightPosition() == INITIAL_RIGHT_EQUIPPABLE_POSITION) { + spatialKey.remove("rightRelativePosition"); + } else { + spatialKey["rightRelativePosition"] = + QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableRightPosition())); + } + } + if (grabProperties.equippableLeftRotationChanged()) { + spatialKey["relativeRotation"] = + QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableLeftRotation())); + } else if (grabProperties.equippableRightRotationChanged()) { + spatialKey["relativeRotation"] = + QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableRightRotation())); + } + + grabbableKey["spatialKey"] = spatialKey; + userDataChanged = true; + } +} + + +void synchronizeGrabbableKey(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { + if (grabProperties.triggerableChanged() || + grabProperties.grabbableChanged() || + grabProperties.grabFollowsControllerChanged() || + grabProperties.grabKinematicChanged() || + grabProperties.equippableChanged() || + grabProperties.equippableLeftPositionChanged() || + grabProperties.equippableRightPositionChanged() || + grabProperties.equippableRightRotationChanged()) { + + QJsonObject grabbableKey = userData["grabbableKey"].toObject(); + + if (grabProperties.triggerableChanged()) { + if (grabProperties.getTriggerable()) { + grabbableKey["triggerable"] = true; + } else { + grabbableKey.remove("triggerable"); + } + } + if (grabProperties.grabbableChanged()) { + if (grabProperties.getGrabbable()) { + grabbableKey.remove("grabbable"); + } else { + grabbableKey["grabbable"] = false; + } + } + if (grabProperties.grabFollowsControllerChanged()) { + if (grabProperties.getGrabFollowsController()) { + grabbableKey.remove("ignoreIK"); + } else { + grabbableKey["ignoreIK"] = false; + } + } + if (grabProperties.grabKinematicChanged()) { + if (grabProperties.getGrabKinematic()) { + grabbableKey.remove("kinematic"); + } else { + grabbableKey["kinematic"] = false; + } + } + if (grabProperties.equippableChanged()) { + if (grabProperties.getEquippable()) { + grabbableKey["equippable"] = true; + } else { + grabbableKey.remove("equippable"); + } + } + + if (grabbableKey.contains("spatialKey")) { + synchronizeSpatialKey(grabProperties, grabbableKey, userDataChanged); + } + + userData["grabbableKey"] = grabbableKey; + userDataChanged = true; + } +} + +void synchronizeGrabJoints(const GrabPropertyGroup& grabProperties, QJsonObject& joints) { + QJsonArray rightHand = joints["RightHand"].toArray(); + QJsonObject rightHandPosition = rightHand.size() > 0 ? rightHand[0].toObject() : QJsonObject(); + QJsonObject rightHandRotation = rightHand.size() > 1 ? rightHand[1].toObject() : QJsonObject(); + QJsonArray leftHand = joints["LeftHand"].toArray(); + QJsonObject leftHandPosition = leftHand.size() > 0 ? leftHand[0].toObject() : QJsonObject(); + QJsonObject leftHandRotation = leftHand.size() > 1 ? leftHand[1].toObject() : QJsonObject(); + + if (grabProperties.equippableLeftPositionChanged()) { + leftHandPosition = + QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableLeftPosition())).toObject(); + } + if (grabProperties.equippableRightPositionChanged()) { + rightHandPosition = + QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableRightPosition())).toObject(); + } + if (grabProperties.equippableLeftRotationChanged()) { + leftHandRotation = + QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableLeftRotation())).toObject(); + } + if (grabProperties.equippableRightRotationChanged()) { + rightHandRotation = + QJsonValue::fromVariant(quatToQMap(grabProperties.getEquippableRightRotation())).toObject(); + } + + rightHand = QJsonArray(); + rightHand.append(rightHandPosition); + rightHand.append(rightHandRotation); + joints["RightHand"] = rightHand; + leftHand = QJsonArray(); + leftHand.append(leftHandPosition); + leftHand.append(leftHandRotation); + joints["LeftHand"] = leftHand; +} + +void synchronizeEquipHotspot(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { + if (grabProperties.equippableLeftPositionChanged() || + grabProperties.equippableRightPositionChanged() || + grabProperties.equippableRightRotationChanged() || + grabProperties.equippableIndicatorURLChanged() || + grabProperties.equippableIndicatorScaleChanged() || + grabProperties.equippableIndicatorOffsetChanged()) { + + QJsonArray equipHotspots = userData["equipHotspots"].toArray(); + QJsonObject equipHotspot = equipHotspots[0].toObject(); + QJsonObject joints = equipHotspot["joints"].toObject(); + + synchronizeGrabJoints(grabProperties, joints); + + if (grabProperties.equippableIndicatorURLChanged()) { + equipHotspot["modelURL"] = grabProperties.getEquippableIndicatorURL(); + } + if (grabProperties.equippableIndicatorScaleChanged()) { + QJsonObject scale = + QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableIndicatorScale())).toObject(); + equipHotspot["radius"] = scale; + equipHotspot["modelScale"] = scale; + + } + if (grabProperties.equippableIndicatorOffsetChanged()) { + equipHotspot["position"] = + QJsonValue::fromVariant(vec3ToQMap(grabProperties.getEquippableIndicatorOffset())).toObject(); + } + + equipHotspot["joints"] = joints; + equipHotspots = QJsonArray(); + equipHotspots.append(equipHotspot); + userData["equipHotspots"] = equipHotspots; + userDataChanged = true; + } +} + +void synchronizeWearable(const GrabPropertyGroup& grabProperties, QJsonObject& userData, bool& userDataChanged) { + if (grabProperties.equippableLeftPositionChanged() || + grabProperties.equippableRightPositionChanged() || + grabProperties.equippableRightRotationChanged() || + grabProperties.equippableIndicatorURLChanged() || + grabProperties.equippableIndicatorScaleChanged() || + grabProperties.equippableIndicatorOffsetChanged()) { + + QJsonObject wearable = userData["wearable"].toObject(); + QJsonObject joints = wearable["joints"].toObject(); + + synchronizeGrabJoints(grabProperties, joints); + + wearable["joints"] = joints; + userData["wearable"] = wearable; + userDataChanged = true; + } +} + +void synchronizeEditedGrabProperties(EntityItemProperties& properties, const QString& previousUserdata) { + // After sufficient warning to content creators, we should be able to remove this. + + if (properties.grabbingRelatedPropertyChanged()) { + // This edit touches a new-style grab property, so make userData agree... + GrabPropertyGroup& grabProperties = properties.getGrab(); + + bool userDataChanged { false }; + + // if the edit changed userData, use the updated version coming along with the edit. If not, use + // what was already in the entity. + QByteArray userDataString; + if (properties.userDataChanged()) { + userDataString = properties.getUserData().toUtf8(); + } else { + userDataString = previousUserdata.toUtf8();; + } + QJsonObject userData = QJsonDocument::fromJson(userDataString).object(); + + if (userData.contains("grabbableKey")) { + synchronizeGrabbableKey(grabProperties, userData, userDataChanged); + } + if (userData.contains("equipHotspots")) { + synchronizeEquipHotspot(grabProperties, userData, userDataChanged); + } + if (userData.contains("wearable")) { + synchronizeWearable(grabProperties, userData, userDataChanged); + } + + if (userDataChanged) { + properties.setUserData(QJsonDocument(userData).toJson()); + } + + } else if (properties.userDataChanged()) { + // This edit touches userData (and doesn't touch a new-style grab property). Check for grabbableKey in the + // userdata and make the new-style grab properties agree + convertGrabUserDataToProperties(properties); + } +} + + QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.addedEntityCount++; auto nodeList = DependencyManager::get(); - auto sessionID = nodeList->getSessionUUID(); + const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; if (clientOnly) { - const QUuid myNodeID = sessionID; propertiesWithSimID.setClientOnly(clientOnly); - propertiesWithSimID.setOwningAvatarID(myNodeID); + propertiesWithSimID.setOwningAvatarID(sessionID); } propertiesWithSimID.setLastEditedBy(sessionID); @@ -257,6 +490,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); + synchronizeEditedGrabProperties(propertiesWithSimID, QString()); EntityItemID id; // If we have a local entity tree set, then also update it. @@ -290,7 +524,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti entity->setLastBroadcast(usecTimestampNow()); // since we're creating this object we will immediately volunteer to own its simulation - entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); properties.setLastEdited(entity->getLastEdited()); } else { qCDebug(entities) << "script failed to add new Entity to local Octree"; @@ -530,54 +764,90 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& _activityTracking.editedEntityCount++; - auto nodeList = DependencyManager::get(); - auto sessionID = nodeList->getSessionUUID(); + const auto sessionID = DependencyManager::get()->getSessionUUID(); EntityItemProperties properties = scriptSideProperties; - properties.setLastEditedBy(sessionID); EntityItemID entityID(id); if (!_entityTree) { + properties.setLastEditedBy(sessionID); queueEntityMessage(PacketType::EntityEdit, entityID, properties); return id; } - // If we have a local entity tree set, then also update it. - bool updatedEntity = false; - _entityTree->withWriteLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + EntityItemPointer entity(nullptr); + SimulationOwner simulationOwner; + _entityTree->withReadLock([&] { + // make a copy of entity for local logic outside of tree lock + entity = _entityTree->findEntityByEntityItemID(entityID); if (!entity) { return; } - if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) { + if (entity->getClientOnly() && entity->getOwningAvatarID() != sessionID) { // don't edit other avatar's avatarEntities + properties = EntityItemProperties(); return; } + // make a copy of simulationOwner for local logic outside of tree lock + simulationOwner = entity->getSimulationOwner(); + }); - if (scriptSideProperties.parentRelatedPropertyChanged()) { - // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them. - // If any of these changed, pull any missing properties from the entity. + QString previousUserdata; + if (entity) { + if (properties.hasSimulationRestrictedChanges()) { + if (_bidOnSimulationOwnership) { + // flag for simulation ownership, or upgrade existing ownership priority + // (actual bids for simulation ownership are sent by the PhysicalEntitySimulation) + entity->upgradeScriptSimulationPriority(properties.computeSimulationBidPriority()); + if (simulationOwner.getID() == sessionID) { + // we own the simulation --> copy ALL restricted properties + properties.copySimulationRestrictedProperties(entity); + } else { + // we don't own the simulation but think we would like to - if (!scriptSideProperties.parentIDChanged()) { - properties.setParentID(entity->getParentID()); - } - if (!scriptSideProperties.parentJointIndexChanged()) { - properties.setParentJointIndex(entity->getParentJointIndex()); - } - if (!scriptSideProperties.localPositionChanged() && !scriptSideProperties.positionChanged()) { - properties.setPosition(entity->getWorldPosition()); - } - if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) { - properties.setRotation(entity->getWorldOrientation()); - } - if (!scriptSideProperties.localDimensionsChanged() && !scriptSideProperties.dimensionsChanged()) { - properties.setDimensions(entity->getScaledDimensions()); + uint8_t desiredPriority = entity->getScriptSimulationPriority(); + if (desiredPriority < simulationOwner.getPriority()) { + // the priority at which we'd like to own it is not high enough + // --> assume failure and clear all restricted property changes + properties.clearSimulationRestrictedProperties(); + } else { + // the priority at which we'd like to own it is high enough to win. + // --> assume success and copy ALL restricted properties + properties.copySimulationRestrictedProperties(entity); + } + } + } else if (!simulationOwner.getID().isNull()) { + // someone owns this but not us + // clear restricted properties + properties.clearSimulationRestrictedProperties(); } + // clear the cached simulationPriority level + entity->upgradeScriptSimulationPriority(0); } + + // set these to make EntityItemProperties::getScalesWithParent() work correctly properties.setClientOnly(entity->getClientOnly()); properties.setOwningAvatarID(entity->getOwningAvatarID()); - properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent()); + + // make sure the properties has a type, so that the encode can know which properties to include + properties.setType(entity->getType()); + + previousUserdata = entity->getUserData(); + } else if (_bidOnSimulationOwnership) { + // bail when simulation participants don't know about entity + return QUuid(); + } + // TODO: it is possible there is no remaining useful changes in properties and we should bail early. + // How to check for this cheaply? + + properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent()); + synchronizeEditedGrabProperties(properties, previousUserdata); + properties.setLastEditedBy(sessionID); + + // done reading and modifying properties --> start write + bool updatedEntity = false; + _entityTree->withWriteLock([&] { updatedEntity = _entityTree->updateEntity(entityID, properties); }); @@ -590,63 +860,37 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // return QUuid(); // } - bool entityFound { false }; + bool hasQueryAACubeRelatedChanges = properties.queryAACubeRelatedPropertyChanged(); + // done writing, send update _entityTree->withReadLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + // find the entity again: maybe it was removed since we last found it + entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { - entityFound = true; - // make sure the properties has a type, so that the encode can know which properties to include - properties.setType(entity->getType()); - bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges(); - bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges; - if (_bidOnSimulationOwnership && hasPhysicsChanges) { - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); + uint64_t now = usecTimestampNow(); + entity->setLastBroadcast(now); - if (entity->getSimulatorID() == myNodeID) { - // we think we already own the simulation, so make sure to send ALL TerseUpdate properties - if (hasTerseUpdateChanges) { - entity->getAllTerseUpdateProperties(properties); - } - // TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object - // is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update - // and instead let the physics simulation decide when to send a terse update. This would remove - // the "slide-no-rotate" glitch (and typical double-update) that we see during the "poke rolling - // balls" test. However, even if we solve this problem we still need to provide a "slerp the visible - // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's - // simulation. - - if (entity->getSimulationPriority() < SCRIPT_POKE_SIMULATION_PRIORITY) { - // we re-assert our simulation ownership at a higher priority - properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); - } - } else { - // we make a bid for simulation ownership - properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); - entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY); - } - } - if (properties.queryAACubeRelatedPropertyChanged()) { + if (hasQueryAACubeRelatedChanges) { properties.setQueryAACube(entity->getQueryAACube()); - } - entity->setLastBroadcast(usecTimestampNow()); - properties.setLastEdited(entity->getLastEdited()); - // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server - // if they've changed. - entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { - if (descendant->getNestableType() == NestableType::Entity) { - if (descendant->updateQueryAACube()) { - EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); - EntityItemProperties newQueryCubeProperties; - newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); - newQueryCubeProperties.setLastEdited(properties.getLastEdited()); - queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); - entityDescendant->setLastBroadcast(usecTimestampNow()); + // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server + // if they've changed. + entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { + if (descendant->getNestableType() == NestableType::Entity) { + if (descendant->updateQueryAACube()) { + EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); + EntityItemProperties newQueryCubeProperties; + newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); + newQueryCubeProperties.setLastEdited(properties.getLastEdited()); + queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); + entityDescendant->setLastBroadcast(now); + } } - } - }); - } else { + }); + } + } + }); + if (!entity) { + if (hasQueryAACubeRelatedChanges) { // Sometimes ESS don't have the entity they are trying to edit in their local tree. In this case, // convertPropertiesFromScriptSemantics doesn't get called and local* edits will get dropped. // This is because, on the script side, "position" is in world frame, but in the network @@ -668,8 +912,6 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& properties.setDimensions(properties.getLocalDimensions()); } } - }); - if (!entityFound) { // we've made an edit to an entity we don't know about, or to a non-entity. If it's a known non-entity, // print a warning and don't send an edit packet to the entity-server. QSharedPointer parentFinder = DependencyManager::get(); @@ -1192,9 +1434,9 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c obj.setProperty("distance", value.distance); obj.setProperty("face", boxFaceToString(value.face)); - QScriptValue intersection = vec3toScriptValue(engine, value.intersection); + QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal); + QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; @@ -1449,7 +1691,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, } action->setIsMine(true); success = entity->addAction(simulation, action); - entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); return false; // Physics will cause a packet to be sent, so don't send from here. }); if (success) { @@ -1465,7 +1707,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) { bool success = entity->updateAction(simulation, actionID, arguments); if (success) { - entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); } return success; }); @@ -1479,7 +1721,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& success = entity->removeAction(simulation, actionID); if (success) { // reduce from grab to poke - entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY); } return false; // Physics will cause a packet to be sent, so don't send from here. }); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 4cac2205a4..fee8d72fe7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -669,7 +669,7 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) { void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { EntityItemPointer entity = findEntityByEntityItemID(entityID); if (entity) { - // remove clone ID from it's clone origin's clone ID list if clone origin exists + // remove clone ID from its clone origin's clone ID list if clone origin exists const QUuid& cloneOriginID = entity->getCloneOriginID(); if (!cloneOriginID.isNull()) { EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID); @@ -1934,6 +1934,14 @@ void EntityTree::fixupNeedsParentFixups() { } }); entity->locationChanged(true); + + // Update our parent's bounding box + bool success = false; + auto parent = entity->getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + entity->postParentFixup(); } else if (getIsServer() || _avatarIDs.contains(entity->getParentID())) { // this is a child of an avatar, which the entity server will never have @@ -2486,6 +2494,118 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer return true; } +void convertGrabUserDataToProperties(EntityItemProperties& properties) { + GrabPropertyGroup& grabProperties = properties.getGrab(); + QJsonObject userData = QJsonDocument::fromJson(properties.getUserData().toUtf8()).object(); + + QJsonValue grabbableKeyValue = userData["grabbableKey"]; + if (grabbableKeyValue.isObject()) { + QJsonObject grabbableKey = grabbableKeyValue.toObject(); + + QJsonValue wantsTrigger = grabbableKey["wantsTrigger"]; + if (wantsTrigger.isBool()) { + grabProperties.setTriggerable(wantsTrigger.toBool()); + } + QJsonValue triggerable = grabbableKey["triggerable"]; + if (triggerable.isBool()) { + grabProperties.setTriggerable(triggerable.toBool()); + } + QJsonValue grabbable = grabbableKey["grabbable"]; + if (grabbable.isBool()) { + grabProperties.setGrabbable(grabbable.toBool()); + } + QJsonValue ignoreIK = grabbableKey["ignoreIK"]; + if (ignoreIK.isBool()) { + grabProperties.setGrabFollowsController(ignoreIK.toBool()); + } + QJsonValue kinematic = grabbableKey["kinematic"]; + if (kinematic.isBool()) { + grabProperties.setGrabKinematic(kinematic.toBool()); + } + QJsonValue equippable = grabbableKey["equippable"]; + if (equippable.isBool()) { + grabProperties.setEquippable(equippable.toBool()); + } + + if (grabbableKey["spatialKey"].isObject()) { + QJsonObject spatialKey = grabbableKey["spatialKey"].toObject(); + grabProperties.setEquippable(true); + if (spatialKey["leftRelativePosition"].isObject()) { + grabProperties.setEquippableLeftPosition(qMapToVec3(spatialKey["leftRelativePosition"].toVariant())); + } + if (spatialKey["rightRelativePosition"].isObject()) { + grabProperties.setEquippableRightPosition(qMapToVec3(spatialKey["rightRelativePosition"].toVariant())); + } + if (spatialKey["relativeRotation"].isObject()) { + grabProperties.setEquippableLeftRotation(qMapToQuat(spatialKey["relativeRotation"].toVariant())); + grabProperties.setEquippableRightRotation(qMapToQuat(spatialKey["relativeRotation"].toVariant())); + } + } + } + + QJsonValue wearableValue = userData["wearable"]; + if (wearableValue.isObject()) { + QJsonObject wearable = wearableValue.toObject(); + QJsonObject joints = wearable["joints"].toObject(); + if (joints["LeftHand"].isArray()) { + QJsonArray leftHand = joints["LeftHand"].toArray(); + if (leftHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableLeftPosition(qMapToVec3(leftHand[0].toVariant())); + grabProperties.setEquippableLeftRotation(qMapToQuat(leftHand[1].toVariant())); + } + } + if (joints["RightHand"].isArray()) { + QJsonArray rightHand = joints["RightHand"].toArray(); + if (rightHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableRightPosition(qMapToVec3(rightHand[0].toVariant())); + grabProperties.setEquippableRightRotation(qMapToQuat(rightHand[1].toVariant())); + } + } + } + + QJsonValue equipHotspotsValue = userData["equipHotspots"]; + if (equipHotspotsValue.isArray()) { + QJsonArray equipHotspots = equipHotspotsValue.toArray(); + if (equipHotspots.size() > 0) { + // just take the first one + QJsonObject firstHotSpot = equipHotspots[0].toObject(); + QJsonObject joints = firstHotSpot["joints"].toObject(); + if (joints["LeftHand"].isArray()) { + QJsonArray leftHand = joints["LeftHand"].toArray(); + if (leftHand.size() == 2) { + grabProperties.setEquippableLeftPosition(qMapToVec3(leftHand[0].toVariant())); + grabProperties.setEquippableLeftRotation(qMapToQuat(leftHand[1].toVariant())); + } + } + if (joints["RightHand"].isArray()) { + QJsonArray rightHand = joints["RightHand"].toArray(); + if (rightHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableRightPosition(qMapToVec3(rightHand[0].toVariant())); + grabProperties.setEquippableRightRotation(qMapToQuat(rightHand[1].toVariant())); + } + } + QJsonValue indicatorURL = firstHotSpot["modelURL"]; + if (indicatorURL.isString()) { + grabProperties.setEquippableIndicatorURL(indicatorURL.toString()); + } + QJsonValue indicatorScale = firstHotSpot["modelScale"]; + if (indicatorScale.isDouble()) { + grabProperties.setEquippableIndicatorScale(glm::vec3((float)indicatorScale.toDouble())); + } else if (indicatorScale.isObject()) { + grabProperties.setEquippableIndicatorScale(qMapToVec3(indicatorScale.toVariant())); + } + QJsonValue indicatorOffset = firstHotSpot["position"]; + if (indicatorOffset.isObject()) { + grabProperties.setEquippableIndicatorOffset(qMapToVec3(indicatorOffset.toVariant())); + } + } + } +} + + bool EntityTree::readFromMap(QVariantMap& map) { // These are needed to deal with older content (before adding inheritance modes) int contentVersion = map["Version"].toInt(); @@ -2559,7 +2679,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { // The legacy version had no keylight mode - this is set to on properties.setKeyLightMode(COMPONENT_MODE_ENABLED); - + // The ambient URL has been moved from "keyLight" to "ambientLight" if (entityMap.contains("keyLight")) { QVariantMap keyLightObject = entityMap["keyLight"].toMap(); @@ -2630,6 +2750,11 @@ bool EntityTree::readFromMap(QVariantMap& map) { } } + // convert old grab-related userData to new grab properties + if (contentVersion < (int)EntityVersion::GrabProperties) { + convertGrabUserDataToProperties(properties); + } + // Zero out the spread values that were fixed in version ParticleEntityFix so they behave the same as before if (contentVersion < (int)EntityVersion::ParticleEntityFix) { properties.setRadiusSpread(0.0f); @@ -2643,9 +2768,11 @@ bool EntityTree::readFromMap(QVariantMap& map) { success = false; } - const QUuid& cloneOriginID = entity->getCloneOriginID(); - if (!cloneOriginID.isNull()) { - cloneIDs[cloneOriginID].push_back(entity->getEntityItemID()); + if (entity) { + const QUuid& cloneOriginID = entity->getCloneOriginID(); + if (!cloneOriginID.isNull()) { + cloneIDs[cloneOriginID].push_back(entity->getEntityItemID()); + } } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index b852a695d8..c6a590ec71 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -426,4 +426,6 @@ private: std::map _namedPaths; }; +void convertGrabUserDataToProperties(EntityItemProperties& properties); + #endif // hifi_EntityTree_h diff --git a/libraries/entities/src/GrabPropertyGroup.cpp b/libraries/entities/src/GrabPropertyGroup.cpp new file mode 100644 index 0000000000..996eed4720 --- /dev/null +++ b/libraries/entities/src/GrabPropertyGroup.cpp @@ -0,0 +1,333 @@ +// +// GrabPropertyGroup.h +// libraries/entities/src +// +// Created by Seth Alves on 2018-8-8. +// 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 +// + +#include "GrabPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableLeftPosition, equippableLeftPosition); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableLeftRotation, equippableLeftRotation); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableRightPosition, equippableRightPosition); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableRightRotation, equippableRightRotation); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, + EquippableIndicatorURL, equippableIndicatorURL); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, + EquippableIndicatorScale, equippableIndicatorScale); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, + EquippableIndicatorOffset, equippableIndicatorOffset); + +} + +void GrabPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabbable, bool, setGrabbable); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabKinematic, bool, setGrabKinematic); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabFollowsController, bool, setGrabFollowsController); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, triggerable, bool, setTriggerable); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippable, bool, setEquippable); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableLeftPosition, vec3, setEquippableLeftPosition); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableLeftRotation, quat, setEquippableLeftRotation); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableRightPosition, vec3, setEquippableRightPosition); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableRightRotation, quat, setEquippableRightRotation); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableIndicatorURL, QString, setEquippableIndicatorURL); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableIndicatorScale, vec3, setEquippableIndicatorScale); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableIndicatorOffset, vec3, setEquippableIndicatorOffset); +} + +void GrabPropertyGroup::merge(const GrabPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(grabbable); + COPY_PROPERTY_IF_CHANGED(grabKinematic); + COPY_PROPERTY_IF_CHANGED(grabFollowsController); + COPY_PROPERTY_IF_CHANGED(triggerable); + COPY_PROPERTY_IF_CHANGED(equippable); + COPY_PROPERTY_IF_CHANGED(equippableLeftPosition); + COPY_PROPERTY_IF_CHANGED(equippableLeftRotation); + COPY_PROPERTY_IF_CHANGED(equippableRightPosition); + COPY_PROPERTY_IF_CHANGED(equippableRightRotation); + COPY_PROPERTY_IF_CHANGED(equippableIndicatorURL); + COPY_PROPERTY_IF_CHANGED(equippableIndicatorScale); + COPY_PROPERTY_IF_CHANGED(equippableIndicatorOffset); +} + +void GrabPropertyGroup::debugDump() const { + qCDebug(entities) << " GrabPropertyGroup: ---------------------------------------------"; + + qCDebug(entities) << " _grabbable:" << _grabbable; + qCDebug(entities) << " _grabKinematic:" << _grabKinematic; + qCDebug(entities) << " _grabFollowsController:" << _grabFollowsController; + qCDebug(entities) << " _triggerable:" << _triggerable; + qCDebug(entities) << " _equippable:" << _equippable; + qCDebug(entities) << " _equippableLeftPosition:" << _equippableLeftPosition; + qCDebug(entities) << " _equippableLeftRotation:" << _equippableLeftRotation; + qCDebug(entities) << " _equippableRightPosition:" << _equippableRightPosition; + qCDebug(entities) << " _equippableRightRotation:" << _equippableRightRotation; + qCDebug(entities) << " _equippableIndicatorURL:" << _equippableIndicatorURL; + qCDebug(entities) << " _equippableIndicatorScale:" << _equippableIndicatorScale; + qCDebug(entities) << " _equippableIndicatorOffset:" << _equippableIndicatorOffset; +} + +void GrabPropertyGroup::listChangedProperties(QList& out) { + if (grabbableChanged()) { + out << "grab-grabbable"; + } + if (grabKinematicChanged()) { + out << "grab-grabKinematic"; + } + if (grabFollowsControllerChanged()) { + out << "grab-followsController"; + } + if (triggerableChanged()) { + out << "grab-triggerable"; + } + if (equippableChanged()) { + out << "grab-equippable"; + } + if (equippableLeftPositionChanged()) { + out << "grab-equippableLeftPosition"; + } + if (equippableLeftRotationChanged()) { + out << "grab-equippableLeftRotation"; + } + if (equippableRightPositionChanged()) { + out << "grab-equippableRightPosition"; + } + if (equippableRightRotationChanged()) { + out << "grab-equippableRightRotation"; + } + if (equippableIndicatorURLChanged()) { + out << "grab-equippableIndicatorURL"; + } + if (equippableIndicatorScaleChanged()) { + out << "grab-equippableIndicatorScale"; + } + if (equippableIndicatorOffsetChanged()) { + out << "grab-equippableIndicatorOffset"; + } +} + +bool GrabPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, getGrabbable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, getGrabKinematic()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, getEquippableRightRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, getEquippableIndicatorURL()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, getEquippableIndicatorScale()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, getEquippableIndicatorOffset()); + + return true; +} + +bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, bool, setGrabbable); + READ_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, bool, setGrabKinematic); + READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); + READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableRightRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, QString, setEquippableIndicatorURL); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, glm::vec3, setEquippableIndicatorScale); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, glm::vec3, setEquippableIndicatorOffset); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_GRABBABLE, Grabbable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_KINEMATIC, GrabKinematic); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_TRIGGERABLE, Triggerable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE, Equippable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, EquippableRightRotation); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, EquippableIndicatorURL); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, EquippableIndicatorScale); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, EquippableIndicatorOffset); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void GrabPropertyGroup::markAllChanged() { + _grabbableChanged = true; + _grabKinematicChanged = true; + _grabFollowsControllerChanged = true; + _triggerableChanged = true; + _equippableChanged = true; + _equippableLeftPositionChanged = true; + _equippableLeftRotationChanged = true; + _equippableRightPositionChanged = true; + _equippableRightRotationChanged = true; + _equippableIndicatorURLChanged = true; + _equippableIndicatorScaleChanged = true; + _equippableIndicatorOffsetChanged = true; +} + +EntityPropertyFlags GrabPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_GRAB_GRABBABLE, grabbable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_KINEMATIC, grabKinematic); + CHECK_PROPERTY_CHANGE(PROP_GRAB_FOLLOWS_CONTROLLER, grabFollowsController); + CHECK_PROPERTY_CHANGE(PROP_GRAB_TRIGGERABLE, triggerable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE, equippable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, equippableLeftPosition); + CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, equippableLeftRotation); + CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, equippableRightPosition); + CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, equippableRightRotation); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, equippableIndicatorURL); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, equippableIndicatorScale); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, equippableIndicatorOffset); + + return changedProperties; +} + +void GrabPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Grabbable, getGrabbable); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabKinematic, getGrabKinematic); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabFollowsController, getGrabFollowsController); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Triggerable, getTriggerable); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Equippable, getEquippable); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableLeftPosition, getEquippableLeftPosition); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableLeftRotation, getEquippableLeftRotation); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableRightPosition, getEquippableRightPosition); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableRightRotation, getEquippableRightRotation); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableIndicatorURL, getEquippableIndicatorURL); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableIndicatorScale, getEquippableIndicatorScale); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableIndicatorOffset, getEquippableIndicatorOffset); +} + +bool GrabPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Grabbable, grabbable, setGrabbable); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabKinematic, grabKinematic, setGrabKinematic); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabFollowsController, grabFollowsController, setGrabFollowsController); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Triggerable, triggerable, setTriggerable); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Equippable, equippable, setEquippable); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableLeftPosition, equippableLeftPosition, setEquippableLeftPosition); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableLeftRotation, equippableLeftRotation, setEquippableLeftRotation); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableRightPosition, equippableRightPosition, + setEquippableRightPosition); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableRightRotation, equippableRightRotation, + setEquippableRightRotation); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableIndicatorURL, equippableIndicatorURL, + setEquippableIndicatorURL); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableIndicatorScale, equippableIndicatorScale, + setEquippableIndicatorScale); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableIndicatorOffset, equippableIndicatorOffset, + setEquippableIndicatorOffset); + + return somethingChanged; +} + +EntityPropertyFlags GrabPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_GRAB_GRABBABLE; + requestedProperties += PROP_GRAB_KINEMATIC; + requestedProperties += PROP_GRAB_FOLLOWS_CONTROLLER; + requestedProperties += PROP_GRAB_TRIGGERABLE; + requestedProperties += PROP_GRAB_EQUIPPABLE; + requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET; + requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET; + requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET; + requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET; + requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_URL; + requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE; + requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET; + + return requestedProperties; +} + +void GrabPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, getGrabbable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, getGrabKinematic()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, getEquippableRightRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, getEquippableIndicatorURL()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, getEquippableIndicatorScale()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, getEquippableIndicatorOffset()); +} + +int GrabPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, bool, setGrabbable); + READ_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, bool, setGrabKinematic); + READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); + READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableRightRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, QString, setEquippableIndicatorURL); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, glm::vec3, setEquippableIndicatorScale); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, glm::vec3, setEquippableIndicatorOffset); + + return bytesRead; +} diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h new file mode 100644 index 0000000000..37b157de06 --- /dev/null +++ b/libraries/entities/src/GrabPropertyGroup.h @@ -0,0 +1,141 @@ +// +// GrabPropertyGroup.h +// libraries/entities/src +// +// Created by Seth Alves on 2018-8-8. +// 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 +// + +#ifndef hifi_GrabPropertyGroup_h +#define hifi_GrabPropertyGroup_h + +#include + +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class ReadBitstreamToTreeParams; + +static const bool INITIAL_GRABBABLE { true }; +static const bool INITIAL_KINEMATIC { true }; +static const bool INITIAL_FOLLOWS_CONTROLLER { true }; +static const bool INITIAL_TRIGGERABLE { false }; +static const bool INITIAL_EQUIPPABLE { false }; +static const glm::vec3 INITIAL_LEFT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; +static const glm::quat INITIAL_LEFT_EQUIPPABLE_ROTATION { glm::quat() }; +static const glm::vec3 INITIAL_RIGHT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; +static const glm::quat INITIAL_RIGHT_EQUIPPABLE_ROTATION { glm::quat() }; +static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_SCALE { glm::vec3(1.0f) }; +static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) }; + + +/**jsdoc + * Grab is defined by the following properties. + * @typedef {object} Entities.Grab + * + * @property {boolean} grabbable=true - If true the entity can be grabbed. + * @property {boolean} grabKinematic=true - If true the entity is updated in a kinematic manner. + * If false it will be grabbed using a tractor action. A kinematic grab will make the item appear more + * tightly held, but will cause it to behave poorly when interacting with dynamic entities. + * @property {boolean} grabFollowsController=true - If true the entity will follow the motions of the + * hand-controller even if the avatar's hand can't get to the implied position. This should be true + * for tools, pens, etc and false for things meant to decorate the hand. + * + * @property {boolean} triggerable=false - If true the entity will receive calls to trigger + * {@link Controller|Controller entity methods}. + * + * @property {boolean} equippable=true - If true the entity can be equipped. + * @property {Vec3} equippableLeftPosition=0,0,0 - Positional offset from the left hand, when equipped. + * @property {Quat} equippableLeftRotation=0,0,0,1 - Rotational offset from the left hand, when equipped. + * @property {Vec3} equippableRightPosition=0,0,0 - Positional offset from the right hand, when equipped. + * @property {Quat} equippableRightRotation=0,0,0,1 - Rotational offset from the right hand, when equipped. + * + * @property {string} equippableIndicatorURL="" - If non-empty, this model will be used to indicate that an + * entity is equippable, rather than the default. + * @property {Vec3} equippableIndicatorScale=1,1,1 - If equippableIndicatorURL is non-empty, this controls the + scale of the displayed overlay. + * @property {Vec3} equippableIndicatorOffset=0,0,0 - If equippableIndicatorURL is non-empty, this controls the + relative offset of the displayed overlay from the equippable entity. + */ + + +class GrabPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + + void merge(const GrabPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + // returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + // grab properties + DEFINE_PROPERTY(PROP_GRAB_GRABBABLE, Grabbable, grabbable, bool, INITIAL_GRABBABLE); + DEFINE_PROPERTY(PROP_GRAB_KINEMATIC, GrabKinematic, grabKinematic, bool, INITIAL_KINEMATIC); + DEFINE_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController, grabFollowsController, bool, + INITIAL_FOLLOWS_CONTROLLER); + DEFINE_PROPERTY(PROP_GRAB_TRIGGERABLE, Triggerable, triggerable, bool, INITIAL_TRIGGERABLE); + DEFINE_PROPERTY(PROP_GRAB_EQUIPPABLE, Equippable, equippable, bool, INITIAL_EQUIPPABLE); + DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition, equippableLeftPosition, + glm::vec3, INITIAL_LEFT_EQUIPPABLE_POSITION); + DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation, equippableLeftRotation, + glm::quat, INITIAL_LEFT_EQUIPPABLE_ROTATION); + DEFINE_PROPERTY_REF(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition, equippableRightPosition, + glm::vec3, INITIAL_RIGHT_EQUIPPABLE_POSITION); + DEFINE_PROPERTY_REF(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, EquippableRightRotation, equippableRightRotation, + glm::quat, INITIAL_RIGHT_EQUIPPABLE_ROTATION); + DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, EquippableIndicatorURL, equippableIndicatorURL, QString, ""); + DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, EquippableIndicatorScale, equippableIndicatorScale, + glm::vec3, INITIAL_EQUIPPABLE_INDICATOR_SCALE); + DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, EquippableIndicatorOffset, equippableIndicatorOffset, + glm::vec3, INITIAL_EQUIPPABLE_INDICATOR_OFFSET); +}; + +#endif // hifi_GrabPropertyGroup_h diff --git a/libraries/entities/src/HazePropertyGroup.cpp b/libraries/entities/src/HazePropertyGroup.cpp index c15b28707c..632f73ced6 100644 --- a/libraries/entities/src/HazePropertyGroup.cpp +++ b/libraries/entities/src/HazePropertyGroup.cpp @@ -18,8 +18,8 @@ void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor, u8vec3Color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_ENABLE_GLARE, Haze, haze, HazeEnableGlare, hazeEnableGlare); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_GLARE_ANGLE, Haze, haze, HazeGlareAngle, hazeGlareAngle); @@ -36,8 +36,8 @@ void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp void HazePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeRange, float, setHazeRange); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeColor, xColor, setHazeColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareColor, xColor, setHazeGlareColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeColor, u8vec3Color, setHazeColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareColor, u8vec3Color, setHazeGlareColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeEnableGlare, bool, setHazeEnableGlare); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareAngle, float, setHazeGlareAngle); @@ -167,8 +167,8 @@ bool HazePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, bool somethingChanged = false; READ_ENTITY_PROPERTY(PROP_HAZE_RANGE, float, setHazeRange); - READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, xColor, setHazeColor); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, xColor, setHazeGlareColor); + READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, u8vec3Color, setHazeColor); + READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, u8vec3Color, setHazeGlareColor); READ_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, bool, setHazeEnableGlare); READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, float, setHazeGlareAngle); @@ -343,8 +343,8 @@ int HazePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* dat const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_HAZE_RANGE, float, setHazeRange); - READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, xColor, setHazeColor); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, xColor, setHazeGlareColor); + READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, u8vec3Color, setHazeColor); + READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, u8vec3Color, setHazeGlareColor); READ_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, bool, setHazeEnableGlare); READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, float, setHazeGlareAngle); diff --git a/libraries/entities/src/HazePropertyGroup.h b/libraries/entities/src/HazePropertyGroup.h index e992aefbf3..595dbeaf51 100644 --- a/libraries/entities/src/HazePropertyGroup.h +++ b/libraries/entities/src/HazePropertyGroup.h @@ -28,8 +28,8 @@ class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; static const float INITIAL_HAZE_RANGE{ 1000.0f }; -static const xColor initialHazeGlareColorXcolor{ 255, 229, 179 }; -static const xColor initialHazeColorXcolor{ 128, 154, 179 }; +static const glm::u8vec3 initialHazeGlareColor { 255, 229, 179 }; +static const glm::u8vec3 initialHazeColor { 128, 154, 179 }; static const float INITIAL_HAZE_GLARE_ANGLE{ 20.0f }; static const float INITIAL_HAZE_BASE_REFERENCE{ 0.0f }; @@ -118,8 +118,8 @@ public: // Range only parameters DEFINE_PROPERTY(PROP_HAZE_RANGE, HazeRange, hazeRange, float, INITIAL_HAZE_RANGE); - DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, xColor, initialHazeColorXcolor); - DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, xColor, initialHazeGlareColorXcolor); + DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, glm::u8vec3, initialHazeColor); + DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, glm::u8vec3, initialHazeGlareColor); DEFINE_PROPERTY(PROP_HAZE_ENABLE_GLARE, HazeEnableGlare, hazeEnableGlare, bool, false); DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_ANGLE, HazeGlareAngle, hazeGlareAngle, float, INITIAL_HAZE_GLARE_ANGLE); diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index 67e6f05921..f0ad2965ce 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -17,7 +17,7 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; +const glm::u8vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; @@ -26,20 +26,20 @@ const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false }; void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows); } void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, u8vec3Color, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, vec3, setDirection); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, castShadows, bool, setCastShadows); // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, u8vec3Color, setColor, getColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, vec3, setDirection, getDirection); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightCastShadows, bool, setCastShadows, getCastShadows); @@ -99,7 +99,7 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl bool overwriteLocalData = true; bool somethingChanged = false; - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); @@ -187,7 +187,7 @@ int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index b966b78fc7..d7fa75a32e 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -84,13 +84,13 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - static const xColor DEFAULT_KEYLIGHT_COLOR; + static const glm::u8vec3 DEFAULT_KEYLIGHT_COLOR; static const float DEFAULT_KEYLIGHT_INTENSITY; static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; static const bool DEFAULT_KEYLIGHT_CAST_SHADOWS; - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, glm::u8vec3, DEFAULT_KEYLIGHT_COLOR); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); DEFINE_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, CastShadows, castShadows, bool, DEFAULT_KEYLIGHT_CAST_SHADOWS); diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index 57a83a4173..e3de5f66f8 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -38,7 +38,6 @@ EntityItemPointer LightEntityItem::factory(const EntityItemID& entityID, const E // our non-pure virtual subclass for now... LightEntityItem::LightEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Light; - _color[RED_INDEX] = _color[GREEN_INDEX] = _color[BLUE_INDEX] = 0; } void LightEntityItem::setUnscaledDimensions(const glm::vec3& value) { @@ -73,7 +72,7 @@ EntityItemProperties LightEntityItem::getProperties(const EntityPropertyFlags& d EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(intensity, getIntensity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(exponent, getExponent); COPY_ENTITY_PROPERTY_TO_PROPERTIES(cutoff, getCutoff); @@ -176,7 +175,7 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_EXPONENT, float, setExponent); READ_ENTITY_PROPERTY(PROP_CUTOFF, float, setCutoff); @@ -214,26 +213,15 @@ void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, getFalloffRadius()); } -const rgbColor& LightEntityItem::getColor() const { - return _color; -} - -xColor LightEntityItem::getXColor() const { - xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; -} - -void LightEntityItem::setColor(const rgbColor& value) { - withWriteLock([&] { - memcpy(_color, value, sizeof(_color)); - _lightPropertiesChanged = true; +glm::u8vec3 LightEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; }); } -void LightEntityItem::setColor(const xColor& value) { +void LightEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; + _color = value; _lightPropertiesChanged = true; }); } diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index f56b5ce624..813333d534 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -52,18 +52,12 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - const rgbColor& getColor() const; - xColor getXColor() const; - - void setColor(const rgbColor& value); - void setColor(const xColor& value); + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); bool getIsSpotlight() const; void setIsSpotlight(bool value); - void setIgnoredColor(const rgbColor& value) { } - void setIgnoredAttenuation(float value) { } - float getIntensity() const; void setIntensity(float value); float getFalloffRadius() const; @@ -96,7 +90,7 @@ public: private: // properties of a light - rgbColor _color; + glm::u8vec3 _color; bool _isSpotlight { DEFAULT_IS_SPOTLIGHT }; float _intensity { DEFAULT_INTENSITY }; float _falloffRadius { DEFAULT_FALLOFF_RADIUS }; diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 1b20922b7d..4957c30112 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -41,13 +41,8 @@ EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& de EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - properties._color = getXColor(); - properties._colorChanged = false; - - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineWidth, getLineWidth); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); return properties; @@ -56,12 +51,11 @@ EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& de bool LineEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineWidth, setLineWidth); SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); - if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -120,7 +114,7 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_LINE_WIDTH, float, setLineWidth); READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); @@ -154,36 +148,21 @@ void LineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits void LineEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " LINE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; + qCDebug(entities) << " color:" << _color; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } - -const rgbColor& LineEntityItem::getColor() const { - return _color; -} - -xColor LineEntityItem::getXColor() const { - xColor result; - withReadLock([&] { - result = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; - }); - return result; -} - -void LineEntityItem::setColor(const rgbColor& value) { - withWriteLock([&] { - memcpy(_color, value, sizeof(_color)); +glm::u8vec3 LineEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; }); } -void LineEntityItem::setColor(const xColor& value) { +void LineEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; + _color = value; }); } diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 06f7830e06..1ebb248d23 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -41,11 +41,8 @@ class LineEntityItem : public EntityItem { EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - const rgbColor& getColor() const; - xColor getXColor() const; - - void setColor(const rgbColor& value); - void setColor(const xColor& value); + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); void setLineWidth(float lineWidth); float getLineWidth() const; @@ -76,7 +73,7 @@ class LineEntityItem : public EntityItem { static const int MAX_POINTS_PER_LINE; private: - rgbColor _color; + glm::u8vec3 _color; float _lineWidth { DEFAULT_LINE_WIDTH }; QVector _points; bool _pointsChanged { true }; diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp index 06afda4283..825dd83348 100644 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -290,9 +290,9 @@ void MaterialEntityItem::applyMaterial() { return; } Transform textureTransform; - textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0)); - textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialMappingRot))); - textureTransform.setScale(glm::vec3(_materialMappingScale, 1)); + textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f)); + textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot))); + textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f)); material->setTextureTransforms(textureTransform); graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, getPriority()); @@ -314,8 +314,25 @@ void MaterialEntityItem::applyMaterial() { _retryApply = true; } +AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) { + AACube aaCube = EntityItem::calculateInitialQueryAACube(success); + // A Material entity's queryAACube contains its parent's queryAACube + auto parent = getParentPointer(success); + if (success && parent) { + success = false; + AACube parentQueryAACube = parent->calculateInitialQueryAACube(success); + if (success) { + aaCube += parentQueryAACube.getMinimumPoint(); + aaCube += parentQueryAACube.getMaximumPoint(); + } + } + return aaCube; +} + void MaterialEntityItem::postParentFixup() { removeMaterial(); + _queryAACubeSet = false; // force an update so we contain our parent + updateQueryAACube(); applyMaterial(); } diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h index 7177aaf718..8aaa833db9 100644 --- a/libraries/entities/src/MaterialEntityItem.h +++ b/libraries/entities/src/MaterialEntityItem.h @@ -85,6 +85,8 @@ public: void postParentFixup() override; + AACube calculateInitialQueryAACube(bool& success) override; + private: // URL for this material. Currently, only JSON format is supported. Set to "materialData" to use the material data to live edit a material. // The following fields are supported in the JSON: diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index e3fca680f3..b4b00e57a7 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -39,7 +39,6 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem( // set the last animated when interface (re)starts _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; - _color[0] = _color[1] = _color[2] = 0; _visuallyReady = false; } @@ -56,7 +55,7 @@ void ModelEntityItem::setTextures(const QString& textures) { EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); @@ -117,7 +116,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; bool animationPropertiesChanged = false; - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); @@ -154,6 +153,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_COLOR; requestedProperties += PROP_MODEL_URL; requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_TEXTURES; @@ -520,9 +520,6 @@ QVector ModelEntityItem::getJointTranslationsSet() const { } -xColor ModelEntityItem::getXColor() const { - xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; -} bool ModelEntityItem::hasModel() const { return resultWithReadLock([&] { return !_modelURL.isEmpty(); @@ -554,17 +551,19 @@ QString ModelEntityItem::getCompoundShapeURL() const { return _compoundShapeURL.get(); } -void ModelEntityItem::setColor(const rgbColor& value) { +QString ModelEntityItem::getCollisionShapeURL() const { + return getShapeType() == SHAPE_TYPE_COMPOUND ? getCompoundShapeURL() : getModelURL(); +} + +void ModelEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { - memcpy(_color, value, sizeof(_color)); + _color = value; }); } -void ModelEntityItem::setColor(const xColor& value) { - withWriteLock([&] { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; +glm::u8vec3 ModelEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; }); } @@ -630,30 +629,6 @@ bool ModelEntityItem::getAnimationHold() const { }); } -void ModelEntityItem::setAnimationFirstFrame(float firstFrame) { - withWriteLock([&] { - _animationProperties.setFirstFrame(firstFrame); - }); -} - -float ModelEntityItem::getAnimationFirstFrame() const { - return resultWithReadLock([&] { - return _animationProperties.getFirstFrame(); - }); -} - -void ModelEntityItem::setAnimationLastFrame(float lastFrame) { - withWriteLock([&] { - _animationProperties.setLastFrame(lastFrame); - }); -} - -float ModelEntityItem::getAnimationLastFrame() const { - return resultWithReadLock([&] { - return _animationProperties.getLastFrame(); - }); -} - bool ModelEntityItem::getAnimationIsPlaying() const { return resultWithReadLock([&] { return _animationProperties.getRunning(); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 8d34664a09..5ca3e2caa1 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -55,12 +55,11 @@ public: void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; - // TODO: Move these to subclasses, or other appropriate abstraction // getters/setters applicable to models and particles + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); - const rgbColor& getColor() const { return _color; } - xColor getXColor() const; bool hasModel() const; virtual bool hasCompoundShapeURL() const; @@ -70,8 +69,8 @@ public: static const QString DEFAULT_COMPOUND_SHAPE_URL; QString getCompoundShapeURL() const; - void setColor(const rgbColor& value); - void setColor(const xColor& value); + // Returns the URL used for the collision shape + QString getCollisionShapeURL() const; // model related properties virtual void setModelURL(const QString& url); @@ -80,9 +79,10 @@ public: // Animation related items... AnimationPropertyGroup getAnimationProperties() const; + // TODO: audit and remove unused Animation accessors bool hasAnimation() const; QString getAnimationURL() const; - void setAnimationURL(const QString& url); + virtual void setAnimationURL(const QString& url); void setAnimationCurrentFrame(float value); void setAnimationIsPlaying(bool value); @@ -100,12 +100,6 @@ public: void setRelayParentJoints(bool relayJoints); bool getRelayParentJoints() const; - void setAnimationFirstFrame(float firstFrame); - float getAnimationFirstFrame() const; - - void setAnimationLastFrame(float lastFrame); - float getAnimationLastFrame() const; - bool getAnimationIsPlaying() const; float getAnimationCurrentFrame() const; float getAnimationFPS() const; @@ -157,7 +151,7 @@ protected: QVector _localJointData; int _lastKnownCurrentFrame{-1}; - rgbColor _color; + glm::u8vec3 _color; QString _modelURL; bool _relayParentJoints; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index cea3e94ef0..acdeac0e93 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -90,10 +90,10 @@ bool operator==(const Properties& a, const Properties& b) { return (a.color == b.color) && (a.alpha == b.alpha) && + (a.radiusStart == b.radiusStart) && (a.radius == b.radius) && (a.spin == b.spin) && (a.rotateWithEntity == b.rotateWithEntity) && - (a.radiusStart == b.radiusStart) && (a.lifespan == b.lifespan) && (a.maxParticles == b.maxParticles) && (a.emission == b.emission) && @@ -117,18 +117,7 @@ bool Properties::valid() const { (alpha.range.start == glm::clamp(alpha.range.start, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.range.finish == glm::clamp(alpha.range.finish, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.gradient.spread == glm::clamp(alpha.gradient.spread, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && - (lifespan == glm::clamp(lifespan, MINIMUM_LIFESPAN, MAXIMUM_LIFESPAN)) && - (emission.rate == glm::clamp(emission.rate, MINIMUM_EMIT_RATE, MAXIMUM_EMIT_RATE)) && - (emission.speed.target == glm::clamp(emission.speed.target, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && - (emission.speed.spread == glm::clamp(emission.speed.spread, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && - (emission.dimensions == glm::clamp(emission.dimensions, vec3(MINIMUM_EMIT_DIMENSION), vec3(MAXIMUM_EMIT_DIMENSION))) && (radiusStart == glm::clamp(radiusStart, MINIMUM_EMIT_RADIUS_START, MAXIMUM_EMIT_RADIUS_START)) && - (polar.start == glm::clamp(polar.start, MINIMUM_POLAR, MAXIMUM_POLAR)) && - (polar.finish == glm::clamp(polar.finish, MINIMUM_POLAR, MAXIMUM_POLAR)) && - (azimuth.start == glm::clamp(azimuth.start, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH)) && - (azimuth.finish == glm::clamp(azimuth.finish, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH)) && - (emission.acceleration.target == glm::clamp(emission.acceleration.target, vec3(MINIMUM_EMIT_ACCELERATION), vec3(MAXIMUM_EMIT_ACCELERATION))) && - (emission.acceleration.spread == glm::clamp(emission.acceleration.spread, vec3(MINIMUM_ACCELERATION_SPREAD), vec3(MAXIMUM_ACCELERATION_SPREAD))) && (radius.gradient.target == glm::clamp(radius.gradient.target, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.range.start == glm::clamp(radius.range.start, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.range.finish == glm::clamp(radius.range.finish, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && @@ -136,7 +125,19 @@ bool Properties::valid() const { (spin.gradient.target == glm::clamp(spin.gradient.target, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.range.start == glm::clamp(spin.range.start, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.range.finish == glm::clamp(spin.range.finish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && - (spin.gradient.spread == glm::clamp(spin.gradient.spread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)); + (spin.gradient.spread == glm::clamp(spin.gradient.spread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && + (lifespan == glm::clamp(lifespan, MINIMUM_LIFESPAN, MAXIMUM_LIFESPAN)) && + (maxParticles == glm::clamp(maxParticles, MINIMUM_MAX_PARTICLES, MAXIMUM_MAX_PARTICLES)) && + (emission.rate == glm::clamp(emission.rate, MINIMUM_EMIT_RATE, MAXIMUM_EMIT_RATE)) && + (emission.speed.target == glm::clamp(emission.speed.target, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && + (emission.speed.spread == glm::clamp(emission.speed.spread, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && + (emission.acceleration.target == glm::clamp(emission.acceleration.target, vec3(MINIMUM_EMIT_ACCELERATION), vec3(MAXIMUM_EMIT_ACCELERATION))) && + (emission.acceleration.spread == glm::clamp(emission.acceleration.spread, vec3(MINIMUM_ACCELERATION_SPREAD), vec3(MAXIMUM_ACCELERATION_SPREAD)) && + (emission.dimensions == glm::clamp(emission.dimensions, vec3(MINIMUM_EMIT_DIMENSION), vec3(MAXIMUM_EMIT_DIMENSION))) && + (polar.start == glm::clamp(polar.start, MINIMUM_POLAR, MAXIMUM_POLAR)) && + (polar.finish == glm::clamp(polar.finish, MINIMUM_POLAR, MAXIMUM_POLAR)) && + (azimuth.start == glm::clamp(azimuth.start, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH)) && + (azimuth.finish == glm::clamp(azimuth.finish, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH))); } bool Properties::emitting() const { @@ -151,9 +152,6 @@ uint64_t Properties::emitIntervalUsecs() const { return 0; } -const xColor ParticleEffectEntityItem::DEFAULT_XCOLOR = xColor(static_cast(DEFAULT_COLOR.r), static_cast(DEFAULT_COLOR.g), static_cast(DEFAULT_COLOR.b)); -const xColor ParticleEffectEntityItem::DEFAULT_XCOLOR_SPREAD = xColor(static_cast(DEFAULT_COLOR_SPREAD.r), static_cast(DEFAULT_COLOR_SPREAD.g), static_cast(DEFAULT_COLOR_SPREAD.b)); - EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new ParticleEffectEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); @@ -165,7 +163,6 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte EntityItem(entityItemID) { _type = EntityTypes::ParticleEffect; - setColor(DEFAULT_COLOR); _visuallyReady = false; } @@ -412,7 +409,7 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); // FIXME - this doesn't appear to get used COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles); @@ -503,28 +500,12 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert return somethingChanged; } -void ParticleEffectEntityItem::setColor(const vec3& value) { +void ParticleEffectEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { _particleProperties.color.gradient.target = value; }); } -void ParticleEffectEntityItem::setColor(const xColor& value) { - withWriteLock([&] { - _particleProperties.color.gradient.target.r = value.red; - _particleProperties.color.gradient.target.g = value.green; - _particleProperties.color.gradient.target.b = value.blue; - }); -} - -xColor ParticleEffectEntityItem::getXColor() const { - xColor color; - color.red = _particleProperties.color.gradient.target.r; - color.green = _particleProperties.color.gradient.target.g; - color.blue = _particleProperties.color.gradient.target.b; - return color; -} - int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, @@ -533,15 +514,15 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, bool, setIsEmitting); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); - READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, vec3, setAccelerationSpread); + READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); @@ -549,9 +530,9 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_RADIUS_START, float, setRadiusStart); READ_ENTITY_PROPERTY(PROP_RADIUS_FINISH, float, setRadiusFinish); - READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, xColor, setColorSpread); - READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3, setColorStart); - READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3, setColorFinish); + READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); + READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3Color, setColorStart); + READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3Color, setColorFinish); READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); READ_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY(PROP_ALPHA_START, float, setAlphaStart); @@ -560,7 +541,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_EMIT_SPEED, float, setEmitSpeed); READ_ENTITY_PROPERTY(PROP_SPEED_SPREAD, float, setSpeedSpread); READ_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); - READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, vec3, setEmitDimensions); + READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions); READ_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); READ_ENTITY_PROPERTY(PROP_POLAR_START, float, setPolarStart); READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); @@ -629,7 +610,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_COLOR, getXColor()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, getIsEmitting()); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles()); @@ -713,22 +694,12 @@ void ParticleEffectEntityItem::setColorFinish(const vec3& colorFinish) { }); } -void ParticleEffectEntityItem::setColorSpread(const xColor& value) { +void ParticleEffectEntityItem::setColorSpread(const glm::u8vec3& value) { withWriteLock([&] { - _particleProperties.color.gradient.spread.r = value.red; - _particleProperties.color.gradient.spread.g = value.green; - _particleProperties.color.gradient.spread.b = value.blue; + _particleProperties.color.gradient.spread = value; }); } -xColor ParticleEffectEntityItem::getColorSpread() const { - xColor color; - color.red = _particleProperties.color.gradient.spread.r; - color.green = _particleProperties.color.gradient.spread.g; - color.blue = _particleProperties.color.gradient.spread.b; - return color; -} - void ParticleEffectEntityItem::setEmitterShouldTrail(bool emitterShouldTrail) { withWriteLock([&] { _particleProperties.emission.shouldTrail = emitterShouldTrail; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index a8e133942e..89f1e834ea 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -20,9 +20,9 @@ namespace particle { static const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work static const float UNINITIALIZED = NAN; - static const vec3 DEFAULT_COLOR = { 255, 255, 255 }; + static const u8vec3 DEFAULT_COLOR = { 255, 255, 255 }; static const vec3 DEFAULT_COLOR_UNINITIALIZED = { UNINITIALIZED, UNINITIALIZED, UNINITIALIZED }; - static const vec3 DEFAULT_COLOR_SPREAD = { 0, 0, 0 }; + static const u8vec3 DEFAULT_COLOR_SPREAD = { 0, 0, 0 }; static const float DEFAULT_ALPHA = 1.0f; static const float DEFAULT_ALPHA_SPREAD = 0.0f; static const float DEFAULT_ALPHA_START = UNINITIALIZED; @@ -42,8 +42,8 @@ namespace particle { static const float MINIMUM_EMIT_SPEED = -1000.0f; static const float MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3 static const float DEFAULT_SPEED_SPREAD = 1.0f; - static const glm::quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical - static const glm::vec3 DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point + static const quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical + static const vec3 DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point static const float MINIMUM_EMIT_DIMENSION = 0.0f; static const float MAXIMUM_EMIT_DIMENSION = (float)TREE_SCALE; static const float DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0) @@ -57,10 +57,10 @@ namespace particle { static const float MAXIMUM_AZIMUTH = SCRIPT_MAXIMUM_PI; static const float DEFAULT_AZIMUTH_START = -PI; // Emit full circumference (when polarFinish > 0) static const float DEFAULT_AZIMUTH_FINISH = PI; // "" - static const glm::vec3 DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f); + static const vec3 DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f); static const float MINIMUM_EMIT_ACCELERATION = -100.0f; // ~ 10g static const float MAXIMUM_EMIT_ACCELERATION = 100.0f; - static const glm::vec3 DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f); + static const vec3 DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f); static const float MINIMUM_ACCELERATION_SPREAD = 0.0f; static const float MAXIMUM_ACCELERATION_SPREAD = 100.0f; static const float DEFAULT_PARTICLE_RADIUS = 0.025f; @@ -177,9 +177,10 @@ namespace particle { Properties& operator =(const Properties& other) { color = other.color; alpha = other.alpha; + radiusStart = other.radiusStart; + radius = other.radius; spin = other.spin; rotateWithEntity = other.rotateWithEntity; - radius = other.radius; lifespan = other.lifespan; maxParticles = other.maxParticles; emission = other.emission; @@ -189,10 +190,10 @@ namespace particle { return *this; } - vec4 getColorStart() const { return vec4(ColorUtils::sRGBToLinearVec3(color.range.start / 255.0f), alpha.range.start); } - vec4 getColorMiddle() const { return vec4(ColorUtils::sRGBToLinearVec3(color.gradient.target / 255.0f), alpha.gradient.target); } - vec4 getColorFinish() const { return vec4(ColorUtils::sRGBToLinearVec3(color.range.finish / 255.0f), alpha.range.finish); } - vec4 getColorSpread() const { return vec4(ColorUtils::sRGBToLinearVec3(color.gradient.spread / 255.0f), alpha.gradient.spread); } + vec4 getColorStart() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.range.start)), alpha.range.start); } + vec4 getColorMiddle() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.gradient.target)), alpha.gradient.target); } + vec4 getColorFinish() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.range.finish)), alpha.range.finish); } + vec4 getColorSpread() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.gradient.spread)), alpha.gradient.spread); } }; } // namespace particles @@ -228,11 +229,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - xColor getXColor() const; - vec3 getColor() const { return _particleProperties.color.gradient.target; } - - void setColor(const vec3& value); - void setColor(const xColor& value); + void setColor(const glm::u8vec3& value); + glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; } void setColorStart(const vec3& colorStart); vec3 getColorStart() const { return _particleProperties.color.range.start; } @@ -240,8 +238,8 @@ public: void setColorFinish(const vec3& colorFinish); vec3 getColorFinish() const { return _particleProperties.color.range.finish; } - void setColorSpread(const xColor& colorSpread); - xColor getColorSpread() const; + void setColorSpread(const glm::u8vec3& colorSpread); + glm::u8vec3 getColorSpread() const { return _particleProperties.color.gradient.spread; } void setAlpha(float alpha); float getAlpha() const { return _particleProperties.alpha.gradient.target; } @@ -344,9 +342,6 @@ public: particle::Properties getParticleProperties() const; - static const xColor DEFAULT_XCOLOR; - static const xColor DEFAULT_XCOLOR_SPREAD; - protected: particle::Properties _particleProperties; bool _isEmitting { true }; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 2d0689b56a..c72256822d 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -39,12 +39,8 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID) : Entit EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { QWriteLocker lock(&_quadReadWriteLock); EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - - properties._color = getXColor(); - properties._colorChanged = false; - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineWidth, getLineWidth); COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); @@ -204,7 +200,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_LINE_WIDTH, float, setLineWidth); READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); READ_ENTITY_PROPERTY(PROP_NORMALS, QVector, setNormals); @@ -253,7 +249,7 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode void PolyLineEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " QUAD EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; + qCDebug(entities) << " color:" << _color; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); @@ -261,7 +257,7 @@ void PolyLineEntityItem::debugDump() const { -QVector PolyLineEntityItem::getLinePoints() const { +QVector PolyLineEntityItem::getLinePoints() const { QVector result; withReadLock([&] { result = _points; @@ -269,7 +265,7 @@ QVector PolyLineEntityItem::getLinePoints() const { return result; } -QVector PolyLineEntityItem::getNormals() const { +QVector PolyLineEntityItem::getNormals() const { QVector result; withReadLock([&] { result = _normals; @@ -309,3 +305,16 @@ void PolyLineEntityItem::setTextures(const QString& textures) { } }); } + +void PolyLineEntityItem::setColor(const glm::u8vec3& value) { + withWriteLock([&] { + _strokeColorsChanged = true; + _color = value; + }); +} + +glm::u8vec3 PolyLineEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; + }); +} diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 94b46ac5e2..f640bd7a9e 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -41,19 +41,8 @@ class PolyLineEntityItem : public EntityItem { EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - const rgbColor& getColor() const { return _color; } - xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } - - void setColor(const rgbColor& value) { - _strokeColorsChanged = true; - memcpy(_color, value, sizeof(_color)); - } - void setColor(const xColor& value) { - _strokeColorsChanged = true; - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; - } + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } @@ -109,7 +98,7 @@ private: void calculateScaleAndRegistrationPoint(); protected: - rgbColor _color; + glm::u8vec3 _color; float _lineWidth { DEFAULT_LINE_WIDTH }; bool _pointsChanged { true }; bool _normalsChanged { true }; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 484a6110c3..e65742b36c 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -70,7 +70,7 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID) : EntityI _type = EntityTypes::PolyVox; } -void PolyVoxEntityItem::setVoxelVolumeSize(const vec3& voxelVolumeSize) { +void PolyVoxEntityItem::setVoxelVolumeSize(const glm::vec3& voxelVolumeSize) { withWriteLock([&] { assert(!glm::any(glm::isnan(voxelVolumeSize))); diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index ba238ab24f..dd05ce67b1 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -54,7 +54,7 @@ class PolyVoxEntityItem : public EntityItem { virtual void debugDump() const override; - virtual void setVoxelVolumeSize(const vec3& voxelVolumeSize); + virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize); virtual glm::vec3 getVoxelVolumeSize() const; virtual void setVoxelData(const QByteArray& voxelData); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index a5e4e25e67..f67134da0a 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -73,7 +73,7 @@ namespace entity { return Shape::Sphere; } - ::QString stringFromShape(Shape shape) { + QString stringFromShape(Shape shape) { return shapeStrings[shape]; } } @@ -117,8 +117,10 @@ ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem( EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + properties.setShape(entity::stringFromShape(getShape())); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + properties._shapeChanged = false; + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); return properties; @@ -182,7 +184,7 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape); - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); return bytesRead; @@ -210,32 +212,24 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -void ShapeEntityItem::setColor(const rgbColor& value) { - memcpy(_color, value, sizeof(rgbColor)); - _material->setAlbedo(glm::vec3(_color[0], _color[1], _color[2]) / 255.0f); +void ShapeEntityItem::setColor(const glm::u8vec3& value) { + withWriteLock([&] { + _color = value; + _material->setAlbedo(toGlm(_color)); + }); } -xColor ShapeEntityItem::getXColor() const { - return xColor { _color[0], _color[1], _color[2] }; -} - -void ShapeEntityItem::setColor(const xColor& value) { - setColor(rgbColor { value.red, value.green, value.blue }); -} - -QColor ShapeEntityItem::getQColor() const { - auto& color = getColor(); - return QColor(color[0], color[1], color[2], (int)(getAlpha() * 255)); -} - -void ShapeEntityItem::setColor(const QColor& value) { - setColor(rgbColor { (uint8_t)value.red(), (uint8_t)value.green(), (uint8_t)value.blue() }); - setAlpha(value.alpha()); +glm::u8vec3 ShapeEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; + }); } void ShapeEntityItem::setAlpha(float alpha) { - _alpha = alpha; - _material->setOpacity(alpha); + withWriteLock([&] { + _alpha = alpha; + _material->setOpacity(alpha); + }); } void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { @@ -313,7 +307,7 @@ void ShapeEntityItem::debugDump() const { qCDebug(entities) << " name:" << _name; qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " collisionShapeType:" << ShapeInfo::getNameForShapeType(getShapeType()); - qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; + qCDebug(entities) << " color:" << _color; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); @@ -419,5 +413,4 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { // This value specifies how the shape should be treated by physics calculations. ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; -} - +} \ No newline at end of file diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index d0326a3794..c89a8934f8 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -31,7 +31,7 @@ namespace entity { }; Shape shapeFromString(const ::QString& shapeString); - ::QString stringFromShape(Shape shape); + QString stringFromShape(Shape shape); } class ShapeEntityItem : public EntityItem { @@ -77,17 +77,11 @@ public: float getAlpha() const { return _alpha; }; void setAlpha(float alpha); - const rgbColor& getColor() const { return _color; } - void setColor(const rgbColor& value); + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); void setUnscaledDimensions(const glm::vec3& value) override; - xColor getXColor() const; - void setColor(const xColor& value); - - QColor getQColor() const; - void setColor(const QColor& value); - bool shouldBePhysical() const override { return !isDead(); } bool supportsDetailedIntersection() const override; @@ -110,7 +104,7 @@ public: protected: float _alpha { 1.0f }; - rgbColor _color; + glm::u8vec3 _color; entity::Shape _shape { entity::Shape::Sphere }; //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index ba40c3fa6f..89ffa95dbe 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -16,15 +16,15 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -const xColor SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; +const glm::u8vec3 SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_URL, Skybox, skybox, URL, url); } void SkyboxPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, color, xColor, setColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, color, u8vec3Color, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, url, QString, setURL); } @@ -71,7 +71,7 @@ bool SkyboxPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlag bool overwriteLocalData = true; bool somethingChanged = false; - READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_SKYBOX_URL, QString, setURL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_SKYBOX_COLOR, Color); @@ -143,7 +143,7 @@ int SkyboxPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* d int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_SKYBOX_URL, QString, setURL); return bytesRead; diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index a94365d24d..c3f9b421f4 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -32,7 +32,7 @@ class ReadBitstreamToTreeParams; /**jsdoc * A skybox is defined by the following properties. * @typedef {object} Entities.Skybox - * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the + * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the * color of the cube map image. * @property {string} url="" - A cube map image that is used to render the sky. */ @@ -83,16 +83,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - glm::vec3 getColorVec3() const { - const quint8 MAX_COLOR = 255; - glm::vec3 color = { (float)_color.red / (float)MAX_COLOR, - (float)_color.green / (float)MAX_COLOR, - (float)_color.blue / (float)MAX_COLOR }; - return color; - } - - static const xColor DEFAULT_COLOR; - DEFINE_PROPERTY_REF(PROP_SKYBOX_COLOR, Color, color, xColor, DEFAULT_COLOR); + static const glm::u8vec3 DEFAULT_COLOR; + DEFINE_PROPERTY_REF(PROP_SKYBOX_COLOR, Color, color, glm::u8vec3, DEFAULT_COLOR); DEFINE_PROPERTY_REF(PROP_SKYBOX_URL, URL, url, QString, ""); }; diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index da843256a0..8dd4877ce2 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -25,8 +25,8 @@ const QString TextEntityItem::DEFAULT_TEXT(""); const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; -const xColor TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; -const xColor TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; +const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; +const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; const bool TextEntityItem::DEFAULT_FACE_CAMERA = false; EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -51,8 +51,8 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColorX); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColorX); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); return properties; } @@ -91,8 +91,8 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); - READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, rgbColor, setTextColor); - READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, rgbColor, setBackgroundColor); + READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor); + READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor); READ_ENTITY_PROPERTY(PROP_FACE_CAMERA, bool, setFaceCamera); return bytesRead; @@ -206,55 +206,27 @@ float TextEntityItem::getLineHeight() const { return result; } -const rgbColor& TextEntityItem::getTextColor() const { - return _textColor; -} - -const rgbColor& TextEntityItem::getBackgroundColor() const { - return _backgroundColor; -} - -xColor TextEntityItem::getTextColorX() const { - xColor result; - withReadLock([&] { - result = { _textColor[RED_INDEX], _textColor[GREEN_INDEX], _textColor[BLUE_INDEX] }; - }); - return result; -} - -void TextEntityItem::setTextColor(const rgbColor& value) { +void TextEntityItem::setTextColor(const glm::u8vec3& value) { withWriteLock([&] { - memcpy(_textColor, value, sizeof(_textColor)); + _textColor = value; }); } -void TextEntityItem::setTextColor(const xColor& value) { +glm::u8vec3 TextEntityItem::getTextColor() const { + return resultWithReadLock([&] { + return _textColor; + }); +} + +void TextEntityItem::setBackgroundColor(const glm::u8vec3& value) { withWriteLock([&] { - _textColor[RED_INDEX] = value.red; - _textColor[GREEN_INDEX] = value.green; - _textColor[BLUE_INDEX] = value.blue; + _backgroundColor = value; }); } -xColor TextEntityItem::getBackgroundColorX() const { - xColor result; - withReadLock([&] { - result = { _backgroundColor[RED_INDEX], _backgroundColor[GREEN_INDEX], _backgroundColor[BLUE_INDEX] }; - }); - return result; -} - -void TextEntityItem::setBackgroundColor(const rgbColor& value) { - withWriteLock([&] { - memcpy(_backgroundColor, value, sizeof(_backgroundColor)); - }); -} - -void TextEntityItem::setBackgroundColor(const xColor& value) { - withWriteLock([&] { - _backgroundColor[RED_INDEX] = value.red; - _backgroundColor[GREEN_INDEX] = value.green; - _backgroundColor[BLUE_INDEX] = value.blue; +glm::u8vec3 TextEntityItem::getBackgroundColor() const { + return resultWithReadLock([&] { + return _backgroundColor; }); } diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index cbddf87fda..357697fdec 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -63,21 +63,13 @@ public: void setLineHeight(float value); float getLineHeight() const; - static const xColor DEFAULT_TEXT_COLOR; - // FIXME should not return a reference because of thread safety, but can't return an array - const rgbColor& getTextColor() const; - xColor getTextColorX() const; + static const glm::u8vec3 DEFAULT_TEXT_COLOR; + glm::u8vec3 getTextColor() const; + void setTextColor(const glm::u8vec3& value); - void setTextColor(const rgbColor& value); - void setTextColor(const xColor& value); - - static const xColor DEFAULT_BACKGROUND_COLOR; - // FIXME should not return a reference because of thread safety, but can't return an array - const rgbColor& getBackgroundColor() const; - xColor getBackgroundColorX() const; - - void setBackgroundColor(const rgbColor& value); - void setBackgroundColor(const xColor& value); + static const glm::u8vec3 DEFAULT_BACKGROUND_COLOR; + glm::u8vec3 getBackgroundColor() const; + void setBackgroundColor(const glm::u8vec3& value); static const bool DEFAULT_FACE_CAMERA; bool getFaceCamera() const; @@ -86,8 +78,8 @@ public: private: QString _text; float _lineHeight; - rgbColor _textColor; - rgbColor _backgroundColor; + glm::u8vec3 _textColor; + glm::u8vec3 _backgroundColor; bool _faceCamera; }; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fdebb16bc8..84caa98ace 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -70,8 +70,8 @@ public: }; -/// A single blendshape extracted from an FBX document. -class FBXBlendshape { +/// A single blendshape. +class HFMBlendshape { public: QVector indices; QVector vertices; @@ -79,19 +79,19 @@ public: QVector tangents; }; -struct FBXJointShapeInfo { - // same units and frame as FBXJoint.translation +struct HFMJointShapeInfo { + // same units and frame as HFMJoint.translation glm::vec3 avgPoint; std::vector dots; std::vector points; std::vector debugLines; }; -/// A single joint (transformation node) extracted from an FBX document. -class FBXJoint { +/// A single joint (transformation node). +class HFMJoint { public: - FBXJointShapeInfo shapeInfo; + HFMJointShapeInfo shapeInfo; QVector freeLineage; bool isFree; int parentIndex; @@ -126,8 +126,8 @@ public: }; -/// A single binding to a joint in an FBX document. -class FBXCluster { +/// A single binding to a joint. +class HFMCluster { public: int jointIndex; @@ -137,8 +137,8 @@ public: const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; -/// A texture map in an FBX document. -class FBXTexture { +/// A texture map. +class HFMTexture { public: QString id; QString name; @@ -156,7 +156,7 @@ public: }; /// A single part of a mesh (with the same material). -class FBXMeshPart { +class HFMMeshPart { public: QVector quadIndices; // original indices from the FBX mesh @@ -166,10 +166,10 @@ public: QString materialID; }; -class FBXMaterial { +class HFMMaterial { public: - FBXMaterial() {}; - FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, + HFMMaterial() {}; + HFMMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, float shininess, float opacity) : diffuseColor(diffuseColor), specularColor(specularColor), @@ -203,17 +203,17 @@ public: QString shadingModel; graphics::MaterialPointer _material; - FBXTexture normalTexture; - FBXTexture albedoTexture; - FBXTexture opacityTexture; - FBXTexture glossTexture; - FBXTexture roughnessTexture; - FBXTexture specularTexture; - FBXTexture metallicTexture; - FBXTexture emissiveTexture; - FBXTexture occlusionTexture; - FBXTexture scatteringTexture; - FBXTexture lightmapTexture; + HFMTexture normalTexture; + HFMTexture albedoTexture; + HFMTexture opacityTexture; + HFMTexture glossTexture; + HFMTexture roughnessTexture; + HFMTexture specularTexture; + HFMTexture metallicTexture; + HFMTexture emissiveTexture; + HFMTexture occlusionTexture; + HFMTexture scatteringTexture; + HFMTexture lightmapTexture; glm::vec2 lightmapParams{ 0.0f, 1.0f }; @@ -231,11 +231,11 @@ public: bool needTangentSpace() const; }; -/// A single mesh (with optional blendshapes) extracted from an FBX document. -class FBXMesh { +/// A single mesh (with optional blendshapes). +class HFMMesh { public: - QVector parts; + QVector parts; QVector vertices; QVector normals; @@ -247,12 +247,12 @@ public: QVector clusterWeights; QVector originalIndices; - QVector clusters; + QVector clusters; Extents meshExtents; glm::mat4 modelTransform; - QVector blendshapes; + QVector blendshapes; unsigned int meshIndex; // the order the meshes appeared in the object file @@ -265,7 +265,7 @@ public: class ExtractedMesh { public: - FBXMesh mesh; + HFMMesh mesh; QMultiHash newIndices; QVector > blendshapeIndexMaps; QVector > partMaterialTextures; @@ -277,15 +277,15 @@ public: * @property {Quat[]} rotations * @property {Vec3[]} translations */ -/// A single animation frame extracted from an FBX document. -class FBXAnimationFrame { +/// A single animation frame. +class HFMAnimationFrame { public: QVector rotations; QVector translations; }; -/// A light in an FBX document. -class FBXLight { +/// A light. +class HFMLight { public: QString name; Transform transform; @@ -293,7 +293,7 @@ public: float fogValue; glm::vec3 color; - FBXLight() : + HFMLight() : name(), transform(), intensity(1.0f), @@ -302,26 +302,26 @@ public: {} }; -Q_DECLARE_METATYPE(FBXAnimationFrame) -Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(HFMAnimationFrame) +Q_DECLARE_METATYPE(QVector) -/// A set of meshes extracted from an FBX document. -class FBXGeometry { +/// The runtime model format. +class HFMModel { public: - using Pointer = std::shared_ptr; + using Pointer = std::shared_ptr; QString originalURL; QString author; QString applicationName; ///< the name of the application that generated the model - QVector joints; + QVector joints; QHash jointIndices; ///< 1-based, so as to more easily detect missing indices bool hasSkeletonJoints; - QVector meshes; + QVector meshes; QVector scripts; - QHash materials; + QHash materials; glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file @@ -348,7 +348,7 @@ public: Extents bindExtents; Extents meshExtents; - QVector animationFrames; + QVector animationFrames; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; @@ -368,7 +368,7 @@ public: QList blendshapeChannelNames; }; -Q_DECLARE_METATYPE(FBXGeometry) -Q_DECLARE_METATYPE(FBXGeometry::Pointer) +Q_DECLARE_METATYPE(HFMModel) +Q_DECLARE_METATYPE(HFMModel::Pointer) #endif // hifi_FBX_h_ diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index dd766f002c..2cf57e54b4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -40,19 +40,19 @@ using namespace std; -int FBXGeometryPointerMetaTypeId = qRegisterMetaType(); +int HFMModelPointerMetaTypeId = qRegisterMetaType(); -QStringList FBXGeometry::getJointNames() const { +QStringList HFMModel::getJointNames() const { QStringList names; - foreach (const FBXJoint& joint, joints) { + foreach (const HFMJoint& joint, joints) { names.append(joint.name); } return names; } -bool FBXGeometry::hasBlendedMeshes() const { +bool HFMModel::hasBlendedMeshes() const { if (!meshes.isEmpty()) { - foreach (const FBXMesh& mesh, meshes) { + foreach (const HFMMesh& mesh, meshes) { if (!mesh.blendshapes.isEmpty()) { return true; } @@ -61,7 +61,7 @@ bool FBXGeometry::hasBlendedMeshes() const { return false; } -Extents FBXGeometry::getUnscaledMeshExtents() const { +Extents HFMModel::getUnscaledMeshExtents() const { const Extents& extents = meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which @@ -74,12 +74,12 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { } // TODO: Move to graphics::Mesh when Sam's ready -bool FBXGeometry::convexHullContains(const glm::vec3& point) const { +bool HFMModel::convexHullContains(const glm::vec3& point) const { if (!getUnscaledMeshExtents().containsPoint(point)) { return false; } - auto checkEachPrimitive = [=](FBXMesh& mesh, QVector indices, int primitiveSize) -> bool { + auto checkEachPrimitive = [=](HFMMesh& mesh, QVector indices, int primitiveSize) -> bool { // Check whether the point is "behind" all the primitives. int verticesSize = mesh.vertices.size(); for (int j = 0; @@ -124,16 +124,16 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const { return false; } -QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { +QString HFMModel::getModelNameOfMesh(int meshIndex) const { if (meshIndicesToModelNames.contains(meshIndex)) { return meshIndicesToModelNames.value(meshIndex); } return QString(); } -int fbxGeometryMetaTypeId = qRegisterMetaType(); -int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); -int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); +int hfmModelMetaTypeId = qRegisterMetaType(); +int hfmAnimationFrameMetaTypeId = qRegisterMetaType(); +int hfmAnimationFrameVectorMetaTypeId = qRegisterMetaType>(); glm::vec3 parseVec3(const QString& string) { @@ -264,17 +264,17 @@ public: }; glm::mat4 getGlobalTransform(const QMultiMap& _connectionParentMap, - const QHash& models, 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()) { visitedNodes.append(nodeID); // Append each node we visit - const FBXModel& model = models.value(nodeID); - globalTransform = glm::translate(model.translation) * model.preTransform * glm::mat4_cast(model.preRotation * - model.rotation * model.postRotation) * model.postTransform * globalTransform; - if (model.hasGeometricOffset) { - glm::mat4 geometricOffset = createMatFromScaleQuatAndPos(model.geometricScaling, model.geometricRotation, model.geometricTranslation); + const FBXModel& fbxModel = fbxModels.value(nodeID); + globalTransform = glm::translate(fbxModel.translation) * fbxModel.preTransform * glm::mat4_cast(fbxModel.preRotation * + fbxModel.rotation * fbxModel.postRotation) * fbxModel.postTransform * globalTransform; + if (fbxModel.hasGeometricOffset) { + glm::mat4 geometricOffset = createMatFromScaleQuatAndPos(fbxModel.geometricScaling, fbxModel.geometricRotation, fbxModel.geometricTranslation); globalTransform = globalTransform * geometricOffset; } @@ -290,7 +290,7 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen continue; } - if (models.contains(parentID)) { + if (fbxModels.contains(parentID)) { nodeID = parentID; break; } @@ -303,7 +303,7 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen class ExtractedBlendshape { public: QString id; - FBXBlendshape blendshape; + HFMBlendshape blendshape; }; void printNode(const FBXNode& node, int indentLevel) { @@ -329,7 +329,7 @@ public: }; void appendModelIDs(const QString& parentID, const QMultiMap& connectionChildMap, - QHash& models, QSet& remainingModels, QVector& modelIDs, bool isRootNode = false) { + QHash& fbxModels, QSet& remainingModels, QVector& modelIDs, bool isRootNode = false) { if (remainingModels.contains(parentID)) { modelIDs.append(parentID); remainingModels.remove(parentID); @@ -337,17 +337,17 @@ void appendModelIDs(const QString& parentID, const QMultiMap& int parentIndex = isRootNode ? -1 : modelIDs.size() - 1; foreach (const QString& childID, connectionChildMap.values(parentID)) { if (remainingModels.contains(childID)) { - FBXModel& model = models[childID]; - if (model.parentIndex == -1) { - model.parentIndex = parentIndex; - appendModelIDs(childID, connectionChildMap, models, remainingModels, modelIDs); + FBXModel& fbxModel = fbxModels[childID]; + if (fbxModel.parentIndex == -1) { + fbxModel.parentIndex = parentIndex; + appendModelIDs(childID, connectionChildMap, fbxModels, remainingModels, modelIDs); } } } } -FBXBlendshape extractBlendshape(const FBXNode& object) { - FBXBlendshape blendshape; +HFMBlendshape extractBlendshape(const FBXNode& object) { + HFMBlendshape blendshape; foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { blendshape.indices = FBXReader::getIntVector(data); @@ -362,9 +362,9 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { return blendshape; } -using IndexAccessor = std::function; +using IndexAccessor = std::function; -static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, +static void setTangents(const HFMMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, const QVector& vertices, const QVector& normals, QVector& tangents) { glm::vec3 vertex[2]; glm::vec3 normal; @@ -381,14 +381,14 @@ static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor } } -static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, +static void createTangents(const HFMMesh& mesh, bool generateFromTexCoords, const QVector& vertices, const QVector& normals, QVector& tangents, IndexAccessor accessor) { // if we have a normal map (and texture coordinates), we must compute tangents if (generateFromTexCoords && !mesh.texCoords.isEmpty()) { tangents.resize(vertices.size()); - foreach(const FBXMeshPart& part, mesh.parts) { + foreach(const HFMMeshPart& part, mesh.parts) { for (int i = 0; i < part.quadIndices.size(); i += 4) { setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents); setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents); @@ -403,27 +403,27 @@ static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents); } if ((part.triangleIndices.size() % 3) != 0) { - qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; + qCDebug(modelformat) << "Error in extractHFMModel part.triangleIndices.size() is not divisible by three "; } } } } -static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape); +static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape); -void FBXMesh::createBlendShapeTangents(bool generateTangents) { +void HFMMesh::createBlendShapeTangents(bool generateTangents) { for (auto& blendShape : blendshapes) { _createBlendShapeTangents(*this, generateTangents, blendShape); } } -void FBXMesh::createMeshTangents(bool generateFromTexCoords) { - FBXMesh& mesh = *this; +void HFMMesh::createMeshTangents(bool generateFromTexCoords) { + HFMMesh& mesh = *this; // This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't // const in the lambda function. auto& tangents = mesh.tangents; createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents, - [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + [&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { outVertices[0] = mesh.vertices[firstIndex]; outVertices[1] = mesh.vertices[secondIndex]; outNormal = mesh.normals[firstIndex]; @@ -431,7 +431,7 @@ void FBXMesh::createMeshTangents(bool generateFromTexCoords) { }); } -static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) { +static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape) { // Create lookup to get index in blend shape from vertex index in mesh std::vector reverseIndices; reverseIndices.resize(mesh.vertices.size()); @@ -443,7 +443,7 @@ static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, } createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents, - [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + [&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { const auto index1 = reverseIndices[firstIndex]; const auto index2 = reverseIndices[secondIndex]; @@ -481,7 +481,7 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first]; for (int i = 0; i < extracted.blendshape.indices.size(); i++) { int oldIndex = extracted.blendshape.indices.at(i); @@ -503,7 +503,7 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& connectionParentMap, - const QHash& models, const QString& modelID, const QString& url) { + const QHash& fbxModels, const QString& modelID, const QString& url) { QString topID = modelID; QVector visitedNodes; // Used to prevent following a cycle forever { @@ -515,7 +515,7 @@ QString getTopModelID(const QMultiMap& connectionParentMap, continue; } - if (models.contains(parentID)) { + if (fbxModels.contains(parentID)) { topID = parentID; goto outerContinue; } @@ -539,7 +539,7 @@ public: QVector values; }; -bool checkMaterialsHaveTextures(const QHash& materials, +bool checkMaterialsHaveTextures(const QHash& materials, const QHash& textureFilenames, const QMultiMap& _connectionChildMap) { foreach (const QString& materialID, materials.keys()) { foreach (const QString& childID, _connectionChildMap.values(materialID)) { @@ -569,8 +569,8 @@ int matchTextureUVSetToAttributeChannel(const QString& texUVSetName, const QHash } -FBXLight extractLight(const FBXNode& object) { - FBXLight light; +HFMLight extractLight(const FBXNode& object) { + HFMLight light; foreach (const FBXNode& subobject, object.children) { QString childname = QString(subobject.name); if (subobject.name == "Properties70") { @@ -615,7 +615,7 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } -FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { +HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; QHash modelIDsToNames; @@ -624,7 +624,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QVector blendshapes; - QHash models; + QHash fbxModels; QHash clusters; QHash animationCurves; @@ -636,7 +636,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QHash yComponents; QHash zComponents; - std::map lights; + std::map lights; QVariantHash joints = mapping.value("joint").toHash(); QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); @@ -689,10 +689,10 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - FBXGeometry* geometryPtr = new FBXGeometry; - FBXGeometry& geometry = *geometryPtr; + HFMModel* hfmModelPtr = new HFMModel; + HFMModel& hfmModel = *hfmModelPtr; - geometry.originalURL = url; + hfmModel.originalURL = url; float unitScaleFactor = 1.0f; glm::vec3 ambientColor; @@ -708,7 +708,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (subobject.name == "MetaData") { foreach (const FBXNode& subsubobject, subobject.children) { if (subsubobject.name == "Author") { - geometry.author = subsubobject.properties.at(0).toString(); + hfmModel.author = subsubobject.properties.at(0).toString(); } } } else if (subobject.name == "Properties70") { @@ -716,7 +716,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS static const QVariant APPLICATION_NAME = QVariant(QByteArray("Original|ApplicationName")); if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && subsubobject.properties.at(0) == APPLICATION_NAME) { - geometry.applicationName = subsubobject.properties.at(4).toString(); + hfmModel.applicationName = subsubobject.properties.at(4).toString(); } } } @@ -814,7 +814,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec3 geometricRotation; glm::vec3 rotationMin, rotationMax; - FBXModel model = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), + FBXModel fbxModel = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), glm::mat4(), glm::vec3(), glm::vec3(), false, glm::vec3(), glm::quat(), glm::vec3(1.0f) }; ExtractedMesh* mesh = NULL; @@ -944,7 +944,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS lightprop = vprop.toString(); } - FBXLight light = extractLight(object); + HFMLight light = extractLight(object); } } } else { @@ -963,27 +963,27 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html - model.translation = translation; + fbxModel.translation = translation; - model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); - model.preRotation = glm::quat(glm::radians(preRotation)); - model.rotation = glm::quat(glm::radians(rotation)); - model.postRotation = glm::inverse(glm::quat(glm::radians(postRotation))); - model.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * + fbxModel.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); + fbxModel.preRotation = glm::quat(glm::radians(preRotation)); + fbxModel.rotation = glm::quat(glm::radians(rotation)); + fbxModel.postRotation = glm::inverse(glm::quat(glm::radians(postRotation))); + fbxModel.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); // NOTE: angles from the FBX file are in degrees // so we convert them to radians for the FBXModel class - model.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f, + fbxModel.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f, rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f)); - model.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, + fbxModel.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f)); - model.hasGeometricOffset = hasGeometricOffset; - model.geometricTranslation = geometricTranslation; - model.geometricRotation = glm::quat(glm::radians(geometricRotation)); - model.geometricScaling = geometricScaling; + fbxModel.hasGeometricOffset = hasGeometricOffset; + fbxModel.geometricTranslation = geometricTranslation; + fbxModel.geometricRotation = glm::quat(glm::radians(geometricRotation)); + fbxModel.geometricScaling = geometricScaling; - models.insert(getID(object.properties), model); + fbxModels.insert(getID(object.properties), fbxModel); } else if (object.name == "Texture") { TextureParam tex; foreach (const FBXNode& subobject, object.children) { @@ -1102,7 +1102,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS _textureContent.insert(filepath, content); } } else if (object.name == "Material") { - FBXMaterial material; + HFMMaterial material; material.name = (object.properties.at(1).toString()); foreach (const FBXNode& subobject, object.children) { bool properties = false; @@ -1255,7 +1255,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #endif } material.materialID = getID(object.properties); - _fbxMaterials.insert(material.materialID, material); + _hfmMaterials.insert(material.materialID, material); } else if (object.name == "NodeAttribute") { @@ -1276,7 +1276,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!attributetype.isEmpty()) { if (attributetype == "Light") { - FBXLight light = extractLight(object); + HFMLight light = extractLight(object); lights[attribID] = light; } } @@ -1307,7 +1307,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS name = name.mid(name.lastIndexOf('.') + 1); } QString id = getID(object.properties); - geometry.blendshapeChannelNames << name; + hfmModel.blendshapeChannelNames << name; foreach (const WeightedIndex& index, blendshapeIndices.values(name)) { blendshapeChannelIndices.insert(id, index); } @@ -1345,7 +1345,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) { - std::map< QString, FBXLight >::iterator lightIt = lights.find(childID); + std::map< QString, HFMLight >::iterator lightIt = lights.find(childID); if (lightIt != lights.end()) { _lightmapLevel = (*lightIt).second.intensity; if (_lightmapLevel <= 0.0f) { @@ -1454,26 +1454,26 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS float offsetScale = mapping.value("scale", 1.0f).toFloat() * unitScaleFactor * METERS_PER_CENTIMETER; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); - geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), + hfmModel.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), mapping.value("tz").toFloat())) * glm::mat4_cast(offsetRotation) * glm::scale(glm::vec3(offsetScale, offsetScale, offsetScale)); // get the list of models in depth-first traversal order QVector modelIDs; - QSet remainingModels; - for (QHash::const_iterator model = models.constBegin(); model != models.constEnd(); model++) { + QSet remainingFBXModels; + for (QHash::const_iterator fbxModel = fbxModels.constBegin(); fbxModel != fbxModels.constEnd(); fbxModel++) { // models with clusters must be parented to the cluster top // Unless the model is a root node. - bool isARootNode = !modelIDs.contains(_connectionParentMap.value(model.key())); + bool isARootNode = !modelIDs.contains(_connectionParentMap.value(fbxModel.key())); if (!isARootNode) { - foreach(const QString& deformerID, _connectionChildMap.values(model.key())) { + foreach(const QString& deformerID, _connectionChildMap.values(fbxModel.key())) { foreach(const QString& clusterID, _connectionChildMap.values(deformerID)) { if (!clusters.contains(clusterID)) { continue; } - QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url); - _connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key()); - _connectionParentMap.insert(model.key(), topID); + QString topID = getTopModelID(_connectionParentMap, fbxModels, _connectionChildMap.value(clusterID), url); + _connectionChildMap.remove(_connectionParentMap.take(fbxModel.key()), fbxModel.key()); + _connectionParentMap.insert(fbxModel.key(), topID); goto outerBreak; } } @@ -1481,21 +1481,21 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // make sure the parent is in the child map - QString parent = _connectionParentMap.value(model.key()); - if (!_connectionChildMap.contains(parent, model.key())) { - _connectionChildMap.insert(parent, model.key()); + QString parent = _connectionParentMap.value(fbxModel.key()); + if (!_connectionChildMap.contains(parent, fbxModel.key())) { + _connectionChildMap.insert(parent, fbxModel.key()); } - remainingModels.insert(model.key()); + remainingFBXModels.insert(fbxModel.key()); } - while (!remainingModels.isEmpty()) { - QString first = *remainingModels.constBegin(); - foreach (const QString& id, remainingModels) { + while (!remainingFBXModels.isEmpty()) { + QString first = *remainingFBXModels.constBegin(); + foreach (const QString& id, remainingFBXModels) { if (id < first) { first = id; } } - QString topID = getTopModelID(_connectionParentMap, models, first, url); - appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, models, remainingModels, modelIDs, true); + QString topID = getTopModelID(_connectionParentMap, fbxModels, first, url); + appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, fbxModels, remainingFBXModels, modelIDs, true); } // figure the number of animation frames from the curves @@ -1504,56 +1504,56 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS frameCount = qMax(frameCount, curve.values.size()); } for (int i = 0; i < frameCount; i++) { - FBXAnimationFrame frame; + HFMAnimationFrame frame; frame.rotations.resize(modelIDs.size()); frame.translations.resize(modelIDs.size()); - geometry.animationFrames.append(frame); + hfmModel.animationFrames.append(frame); } // convert the models to joints QVariantList freeJoints = mapping.values("freeJoint"); - geometry.hasSkeletonJoints = false; + hfmModel.hasSkeletonJoints = false; foreach (const QString& modelID, modelIDs) { - const FBXModel& model = models[modelID]; - FBXJoint joint; - joint.isFree = freeJoints.contains(model.name); - joint.parentIndex = model.parentIndex; + const FBXModel& fbxModel = fbxModels[modelID]; + HFMJoint joint; + joint.isFree = freeJoints.contains(fbxModel.name); + joint.parentIndex = fbxModel.parentIndex; // get the indices of all ancestors starting with the first free one (if any) - int jointIndex = geometry.joints.size(); + int jointIndex = hfmModel.joints.size(); joint.freeLineage.append(jointIndex); int lastFreeIndex = joint.isFree ? 0 : -1; - for (int index = joint.parentIndex; index != -1; index = geometry.joints.at(index).parentIndex) { - if (geometry.joints.at(index).isFree) { + for (int index = joint.parentIndex; index != -1; index = hfmModel.joints.at(index).parentIndex) { + if (hfmModel.joints.at(index).isFree) { lastFreeIndex = joint.freeLineage.size(); } joint.freeLineage.append(index); } joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1); - joint.translation = model.translation; // these are usually in centimeters - joint.preTransform = model.preTransform; - joint.preRotation = model.preRotation; - joint.rotation = model.rotation; - joint.postRotation = model.postRotation; - joint.postTransform = model.postTransform; - joint.rotationMin = model.rotationMin; - joint.rotationMax = model.rotationMax; + joint.translation = fbxModel.translation; // these are usually in centimeters + joint.preTransform = fbxModel.preTransform; + joint.preRotation = fbxModel.preRotation; + joint.rotation = fbxModel.rotation; + joint.postRotation = fbxModel.postRotation; + joint.postTransform = fbxModel.postTransform; + joint.rotationMin = fbxModel.rotationMin; + joint.rotationMax = fbxModel.rotationMax; - joint.hasGeometricOffset = model.hasGeometricOffset; - joint.geometricTranslation = model.geometricTranslation; - joint.geometricRotation = model.geometricRotation; - joint.geometricScaling = model.geometricScaling; + joint.hasGeometricOffset = fbxModel.hasGeometricOffset; + joint.geometricTranslation = fbxModel.geometricTranslation; + joint.geometricRotation = fbxModel.geometricRotation; + joint.geometricScaling = fbxModel.geometricScaling; glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; if (joint.parentIndex == -1) { - joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform * + 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 FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); + const HFMJoint& parentJoint = hfmModel.joints.at(joint.parentIndex); joint.transform = parentJoint.transform * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; @@ -1561,20 +1561,20 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS extractTranslation(joint.transform)); } joint.inverseBindRotation = joint.inverseDefaultRotation; - joint.name = model.name; + joint.name = fbxModel.name; foreach (const QString& childID, _connectionChildMap.values(modelID)) { QString type = typeFlags.value(childID); if (!type.isEmpty()) { - geometry.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton")); + hfmModel.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton")); break; } } joint.bindTransformFoundInCluster = false; - geometry.joints.append(joint); - geometry.jointIndices.insert(model.name, geometry.joints.size()); + hfmModel.joints.append(joint); + hfmModel.jointIndices.insert(fbxModel.name, hfmModel.joints.size()); QString rotationID = localRotations.value(modelID); AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID)); @@ -1590,11 +1590,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec3 defaultPosValues = joint.translation; for (int i = 0; i < frameCount; i++) { - geometry.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( + hfmModel.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( xRotCurve.values.isEmpty() ? defaultRotValues.x : xRotCurve.values.at(i % xRotCurve.values.size()), yRotCurve.values.isEmpty() ? defaultRotValues.y : yRotCurve.values.at(i % yRotCurve.values.size()), zRotCurve.values.isEmpty() ? defaultRotValues.z : zRotCurve.values.at(i % zRotCurve.values.size())))); - geometry.animationFrames[i].translations[jointIndex] = glm::vec3( + hfmModel.animationFrames[i].translations[jointIndex] = glm::vec3( xPosCurve.values.isEmpty() ? defaultPosValues.x : xPosCurve.values.at(i % xPosCurve.values.size()), yPosCurve.values.isEmpty() ? defaultPosValues.y : yPosCurve.values.at(i % yPosCurve.values.size()), zPosCurve.values.isEmpty() ? defaultPosValues.z : zPosCurve.values.at(i % zPosCurve.values.size())); @@ -1603,35 +1603,35 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // NOTE: shapeVertices are in joint-frame std::vector shapeVertices; - shapeVertices.resize(std::max(1, geometry.joints.size()) ); + shapeVertices.resize(std::max(1, hfmModel.joints.size()) ); // find our special joints - geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); - geometry.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); - geometry.neckJointIndex = modelIDs.indexOf(jointNeckID); - geometry.rootJointIndex = modelIDs.indexOf(jointRootID); - geometry.leanJointIndex = modelIDs.indexOf(jointLeanID); - geometry.headJointIndex = modelIDs.indexOf(jointHeadID); - geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); - geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); - geometry.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); - geometry.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); + hfmModel.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); + hfmModel.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); + hfmModel.neckJointIndex = modelIDs.indexOf(jointNeckID); + hfmModel.rootJointIndex = modelIDs.indexOf(jointRootID); + hfmModel.leanJointIndex = modelIDs.indexOf(jointLeanID); + hfmModel.headJointIndex = modelIDs.indexOf(jointHeadID); + hfmModel.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); + hfmModel.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); + hfmModel.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); + hfmModel.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); foreach (const QString& id, humanIKJointIDs) { - geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); + hfmModel.humanIKJointIndices.append(modelIDs.indexOf(id)); } // extract the translation component of the neck transform - if (geometry.neckJointIndex != -1) { - const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform; - geometry.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); + if (hfmModel.neckJointIndex != -1) { + const glm::mat4& transform = hfmModel.joints.at(hfmModel.neckJointIndex).transform; + hfmModel.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); } - geometry.bindExtents.reset(); - geometry.meshExtents.reset(); + hfmModel.bindExtents.reset(); + hfmModel.meshExtents.reset(); // Create the Material Library - consolidateFBXMaterials(mapping); + consolidateHFMMaterials(mapping); // We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key @@ -1643,7 +1643,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // 33 - 128 textures --> 512 // etc... QSet uniqueTextures; - for (auto& material : _fbxMaterials) { + for (auto& material : _hfmMaterials) { material.getTextureNames(uniqueTextures); } int numTextures = uniqueTextures.size(); @@ -1659,15 +1659,15 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } while (numTextureThreshold < numTextures && maxWidth > MIN_MIP_TEXTURE_WIDTH); qCDebug(modelformat) << "Capped square texture width =" << maxWidth << "for model" << url << "with" << numTextures << "textures"; - for (auto& material : _fbxMaterials) { + for (auto& material : _hfmMaterials) { material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } } #endif - geometry.materials = _fbxMaterials; + hfmModel.materials = _hfmMaterials; // see if any materials have texture children - bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, _textureFilenames, _connectionChildMap); + bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap); for (QMap::iterator it = meshes.begin(); it != meshes.end(); it++) { ExtractedMesh& extracted = it.value(); @@ -1675,14 +1675,14 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS extracted.mesh.meshExtents.reset(); // accumulate local transforms - QString modelID = models.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); - glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, models, modelID, geometry.applicationName == "mixamo.com", url); + QString modelID = fbxModels.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); + glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, hfmModel.applicationName == "mixamo.com", url); // 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)); - geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); - geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + hfmModel.meshExtents.minimum = glm::min(hfmModel.meshExtents.minimum, transformedVertex); + hfmModel.meshExtents.maximum = glm::max(hfmModel.meshExtents.maximum, transformedVertex); extracted.mesh.meshExtents.minimum = glm::min(extracted.mesh.meshExtents.minimum, transformedVertex); extracted.mesh.meshExtents.maximum = glm::max(extracted.mesh.meshExtents.maximum, transformedVertex); @@ -1698,13 +1698,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = children.size() - 1; i >= 0; i--) { const QString& childID = children.at(i); - if (_fbxMaterials.contains(childID)) { + if (_hfmMaterials.contains(childID)) { // the pure material associated with this part - FBXMaterial material = _fbxMaterials.value(childID); + HFMMaterial material = _hfmMaterials.value(childID); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { if (extracted.partMaterialTextures.at(j).first == materialIndex) { - FBXMeshPart& part = extracted.mesh.parts[j]; + HFMMeshPart& part = extracted.mesh.parts[j]; part.materialID = material.materialID; generateTangents |= material.needTangentSpace(); } @@ -1713,7 +1713,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS materialIndex++; } else if (_textureFilenames.contains(childID)) { - FBXTexture texture = getTexture(childID); + 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)) { @@ -1736,47 +1736,47 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!clusters.contains(clusterID)) { continue; } - FBXCluster fbxCluster; + 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); - fbxCluster.jointIndex = modelIDs.indexOf(jointID); - if (fbxCluster.jointIndex == -1) { + hfmCluster.jointIndex = modelIDs.indexOf(jointID); + if (hfmCluster.jointIndex == -1) { qCDebug(modelformat) << "Joint not in model list: " << jointID; - fbxCluster.jointIndex = 0; + hfmCluster.jointIndex = 0; } - fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + 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. - fbxCluster.inverseBindMatrix[0][3] = 0.0f; - fbxCluster.inverseBindMatrix[1][3] = 0.0f; - fbxCluster.inverseBindMatrix[2][3] = 0.0f; - fbxCluster.inverseBindMatrix[3][3] = 1.0f; + hfmCluster.inverseBindMatrix[0][3] = 0.0f; + hfmCluster.inverseBindMatrix[1][3] = 0.0f; + hfmCluster.inverseBindMatrix[2][3] = 0.0f; + hfmCluster.inverseBindMatrix[3][3] = 1.0f; - fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); + hfmCluster.inverseBindTransform = Transform(hfmCluster.inverseBindMatrix); - extracted.mesh.clusters.append(fbxCluster); + extracted.mesh.clusters.append(hfmCluster); // override the bind rotation with the transform link - FBXJoint& joint = geometry.joints[fbxCluster.jointIndex]; + 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(geometry.offset * joint.bindTransform); - geometry.bindExtents.addPoint(bindTranslation); + glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * joint.bindTransform); + hfmModel.bindExtents.addPoint(bindTranslation); } } // if we don't have a skinned joint, parent to the model itself if (extracted.mesh.clusters.isEmpty()) { - FBXCluster cluster; + HFMCluster cluster; cluster.jointIndex = modelIDs.indexOf(modelID); if (cluster.jointIndex == -1) { qCDebug(modelformat) << "Model not in model list: " << modelID; @@ -1786,7 +1786,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // whether we're skinned depends on how many clusters are attached - const FBXCluster& firstFBXCluster = extracted.mesh.clusters.at(0); + const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0); glm::mat4 inverseModelTransform = glm::inverse(modelTransform); if (clusterIDs.size() > 1) { // this is a multi-mesh joint @@ -1799,16 +1799,16 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = 0; i < clusterIDs.size(); i++) { QString clusterID = clusterIDs.at(i); const Cluster& cluster = clusters[clusterID]; - const FBXCluster& fbxCluster = extracted.mesh.clusters.at(i); - int jointIndex = fbxCluster.jointIndex; - FBXJoint& joint = geometry.joints[jointIndex]; + const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); + int jointIndex = hfmCluster.jointIndex; + HFMJoint& joint = hfmModel.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; float boneLength = 0.0f; if (joint.parentIndex != -1) { - boneBegin = extractTranslation(inverseModelTransform * geometry.joints[joint.parentIndex].bindTransform); + boneBegin = extractTranslation(inverseModelTransform * hfmModel.joints[joint.parentIndex].bindTransform); boneDirection = boneEnd - boneBegin; boneLength = glm::length(boneDirection); if (boneLength > EPSILON) { @@ -1881,8 +1881,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } else { // this is a single-mesh joint - int jointIndex = firstFBXCluster.jointIndex; - FBXJoint& joint = geometry.joints[jointIndex]; + 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; @@ -1902,8 +1902,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } buildModelMesh(extracted.mesh, url); - geometry.meshes.append(extracted.mesh); - int meshIndex = geometry.meshes.size() - 1; + hfmModel.meshes.append(extracted.mesh); + int meshIndex = hfmModel.meshes.size() - 1; if (extracted.mesh._mesh) { extracted.mesh._mesh->displayName = QString("%1#/mesh/%2").arg(url).arg(meshIndex).toStdString(); extracted.mesh._mesh->modelName = modelIDsToNames.value(modelID).toStdString(); @@ -1923,8 +1923,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS }; // now that all joints have been scanned compute a k-Dop bounding volume of mesh - for (int i = 0; i < geometry.joints.size(); ++i) { - FBXJoint& joint = geometry.joints[i]; + for (int i = 0; i < hfmModel.joints.size(); ++i) { + HFMJoint& joint = hfmModel.joints[i]; // NOTE: points are in joint-frame ShapeVertices& points = shapeVertices.at(i); @@ -1958,7 +1958,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS generateBoundryLinesForDop14(joint.shapeInfo.dots, joint.shapeInfo.avgPoint, joint.shapeInfo.debugLines); } } - geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); + hfmModel.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); // attempt to map any meshes to a named model for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); @@ -1971,14 +1971,14 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS const QString& modelID = ooChildToParent.value(meshID); if (modelIDsToNames.contains(modelID)) { const QString& modelName = modelIDsToNames.value(modelID); - geometry.meshIndicesToModelNames.insert(meshIndex, modelName); + hfmModel.meshIndicesToModelNames.insert(meshIndex, modelName); } } } { int i = 0; - for (const auto& mesh : geometry.meshes) { - auto name = geometry.getModelNameOfMesh(i++); + for (const auto& mesh : hfmModel.meshes) { + auto name = hfmModel.getModelNameOfMesh(i++); if (!name.isEmpty()) { if (mesh._mesh) { mesh._mesh->modelName = name.toStdString(); @@ -1991,16 +1991,16 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } } - return geometryPtr; + return hfmModelPtr; } -FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { - QBuffer buffer(const_cast(&model)); +HFMModel* readFBX(const QByteArray& data, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { + QBuffer buffer(const_cast(&data)); buffer.open(QIODevice::ReadOnly); return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel); } -FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +HFMModel* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { FBXReader reader; reader._rootNode = FBXReader::parseFBX(device); reader._loadLightmaps = loadLightmaps; @@ -2008,5 +2008,5 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri qCDebug(modelformat) << "Reading FBX: " << url; - return reader.extractFBXGeometry(mapping, url); + return reader.extractHFMModel(mapping, url); } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index c391ea6647..d9a216eeb8 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -34,13 +34,13 @@ class QIODevice; class FBXNode; -/// Reads FBX geometry from the supplied model and mapping data. +/// Reads HFMModel from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMModel* readFBX(const QByteArray& data, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); -/// Reads FBX geometry from the supplied model and mapping data. +/// Reads HFMModel from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMModel* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); class TextureParam { public: @@ -103,20 +103,20 @@ class ExtractedMesh; class FBXReader { public: - FBXGeometry* _fbxGeometry; + HFMModel* _hfmModel; FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); - FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); + HFMModel* extractHFMModel(const QVariantHash& mapping, const QString& url); static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true); QHash meshes; - static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); + static void buildModelMesh(HFMMesh& extractedMesh, const QString& url); static glm::vec3 normalizeDirForPacking(const glm::vec3& dir); - FBXTexture getTexture(const QString& textureID); + HFMTexture getTexture(const QString& textureID); QHash _textureNames; // Hashes the original RelativeFilename of textures @@ -142,9 +142,9 @@ public: QHash ambientFactorTextures; QHash occlusionTextures; - QHash _fbxMaterials; + QHash _hfmMaterials; - void consolidateFBXMaterials(const QVariantHash& mapping); + void consolidateHFMMaterials(const QVariantHash& mapping); bool _loadLightmaps = true; float _lightmapOffset = 0.0f; diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index d5902962e5..ff1de30b97 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -27,7 +27,7 @@ #include "ModelFormatLogging.h" -void FBXMaterial::getTextureNames(QSet& textureList) const { +void HFMMaterial::getTextureNames(QSet& textureList) const { if (!normalTexture.isNull()) { textureList.insert(normalTexture.name); } @@ -63,7 +63,7 @@ void FBXMaterial::getTextureNames(QSet& textureList) const { } } -void FBXMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { +void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { normalTexture.maxNumPixels = maxNumPixels; albedoTexture.maxNumPixels = maxNumPixels; opacityTexture.maxNumPixels = maxNumPixels; @@ -77,12 +77,12 @@ void FBXMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { lightmapTexture.maxNumPixels = maxNumPixels; } -bool FBXMaterial::needTangentSpace() const { +bool HFMMaterial::needTangentSpace() const { return !normalTexture.isNull(); } -FBXTexture FBXReader::getTexture(const QString& textureID) { - FBXTexture texture; +HFMTexture FBXReader::getTexture(const QString& textureID) { + HFMTexture texture; const QByteArray& filepath = _textureFilepaths.value(textureID); texture.content = _textureContent.value(filepath); @@ -123,7 +123,7 @@ FBXTexture FBXReader::getTexture(const QString& textureID) { return texture; } -void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { +void FBXReader::consolidateHFMMaterials(const QVariantHash& mapping) { QString materialMapString = mapping.value("materialMap").toString(); QJsonDocument materialMapDocument = QJsonDocument::fromJson(materialMapString.toUtf8()); @@ -133,16 +133,16 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapString; } } - for (QHash::iterator it = _fbxMaterials.begin(); it != _fbxMaterials.end(); it++) { - FBXMaterial& material = (*it); + for (QHash::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) { + HFMMaterial& material = (*it); // Maya is the exporting the shading model and we are trying to use it bool isMaterialLambert = (material.shadingModel.toLower() == "lambert"); // the pure material associated with this part bool detectDifferentUVs = false; - FBXTexture diffuseTexture; - FBXTexture diffuseFactorTexture; + HFMTexture diffuseTexture; + HFMTexture diffuseFactorTexture; QString diffuseTextureID = diffuseTextures.value(material.materialID); QString diffuseFactorTextureID = diffuseFactorTextures.value(material.materialID); @@ -169,7 +169,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity()); } - FBXTexture transparentTexture; + HFMTexture transparentTexture; QString transparentTextureID = transparentTextures.value(material.materialID); // If PBS Material, systematically bind the albedo texture as transparency texture and check for the alpha channel if (material.isPBSMaterial) { @@ -181,7 +181,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity()); } - FBXTexture normalTexture; + HFMTexture normalTexture; QString bumpTextureID = bumpTextures.value(material.materialID); QString normalTextureID = normalTextures.value(material.materialID); if (!normalTextureID.isNull()) { @@ -198,7 +198,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity()); } - FBXTexture specularTexture; + HFMTexture specularTexture; QString specularTextureID = specularTextures.value(material.materialID); if (!specularTextureID.isNull()) { specularTexture = getTexture(specularTextureID); @@ -206,7 +206,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material.specularTexture = specularTexture; } - FBXTexture metallicTexture; + HFMTexture metallicTexture; QString metallicTextureID = metallicTextures.value(material.materialID); if (!metallicTextureID.isNull()) { metallicTexture = getTexture(metallicTextureID); @@ -214,7 +214,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material.metallicTexture = metallicTexture; } - FBXTexture roughnessTexture; + HFMTexture roughnessTexture; QString roughnessTextureID = roughnessTextures.value(material.materialID); if (!roughnessTextureID.isNull()) { roughnessTexture = getTexture(roughnessTextureID); @@ -222,7 +222,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (roughnessTexture.texcoordSet != 0) || (!roughnessTexture.transform.isIdentity()); } - FBXTexture shininessTexture; + HFMTexture shininessTexture; QString shininessTextureID = shininessTextures.value(material.materialID); if (!shininessTextureID.isNull()) { shininessTexture = getTexture(shininessTextureID); @@ -230,7 +230,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (shininessTexture.texcoordSet != 0) || (!shininessTexture.transform.isIdentity()); } - FBXTexture emissiveTexture; + HFMTexture emissiveTexture; QString emissiveTextureID = emissiveTextures.value(material.materialID); if (!emissiveTextureID.isNull()) { emissiveTexture = getTexture(emissiveTextureID); @@ -245,7 +245,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { } } - FBXTexture occlusionTexture; + HFMTexture occlusionTexture; QString occlusionTextureID = occlusionTextures.value(material.materialID); if (occlusionTextureID.isNull()) { // 2nd chance @@ -265,7 +265,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { lightmapParams.x = _lightmapOffset; lightmapParams.y = _lightmapLevel; - FBXTexture ambientTexture; + HFMTexture ambientTexture; QString ambientTextureID = ambientTextures.value(material.materialID); if (ambientTextureID.isNull()) { // 2nd chance @@ -326,7 +326,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { if (materialOptions.contains("scatteringMap")) { QByteArray scatteringMap = materialOptions.value("scatteringMap").toVariant().toByteArray(); - material.scatteringTexture = FBXTexture(); + material.scatteringTexture = HFMTexture(); material.scatteringTexture.name = material.name + ".scatteringMap"; material.scatteringTexture.filename = scatteringMap; } diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c9b004c3a8..5b1c708378 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -42,9 +42,9 @@ using vec2h = glm::tvec2; -#define FBX_PACK_COLORS 1 +#define HFM_PACK_COLORS 1 -#if FBX_PACK_COLORS +#if HFM_PACK_COLORS using ColorType = glm::uint32; #define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32 #else @@ -397,7 +397,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } // enumerate the vertices and construct the extracted mesh - for (int i = 0; i < numVertices; ++i) { + for (uint32_t i = 0; i < numVertices; ++i) { draco::PointIndex vertexIndex(i); if (positionAttribute) { @@ -453,7 +453,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } } - for (int i = 0; i < dracoMesh->num_faces(); ++i) { + for (uint32_t i = 0; i < dracoMesh->num_faces(); ++i) { // grab the material ID and texture ID for this face, if we have it auto& dracoFace = dracoMesh->face(draco::FaceIndex(i)); auto& firstCorner = dracoFace[0]; @@ -469,7 +469,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QPair materialTexture(materialID, 0); - // grab or setup the FBXMeshPart for the part this face belongs to + // grab or setup the HFMMeshPart for the part this face belongs to int& partIndexPlusOne = materialTextureParts[materialTexture]; if (partIndexPlusOne == 0) { data.extracted.partMaterialTextures.append(materialTexture); @@ -478,7 +478,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } // give the mesh part this index - FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; + HFMMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(firstCorner.value()); part.triangleIndices.append(dracoFace[1].value()); part.triangleIndices.append(dracoFace[2].value()); @@ -511,7 +511,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); partIndex = data.extracted.mesh.parts.size(); } - FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; + HFMMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; if (endIndex - beginIndex == 4) { appendIndex(data, part.quadIndices, beginIndex++, deduplicate); @@ -565,9 +565,9 @@ glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) { return dir; } -void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { +void FBXReader::buildModelMesh(HFMMesh& extractedMesh, const QString& url) { unsigned int totalSourceIndices = 0; - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } @@ -583,17 +583,17 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { return; } - FBXMesh& fbxMesh = extractedMesh; + HFMMesh& hfmMesh = extractedMesh; graphics::MeshPointer mesh(new graphics::Mesh()); int numVerts = extractedMesh.vertices.size(); - if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) { + if (!hfmMesh.normals.empty() && hfmMesh.tangents.empty()) { // Fill with a dummy value to force tangents to be present if there are normals - fbxMesh.tangents.reserve(fbxMesh.normals.size()); - std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X); + hfmMesh.tangents.reserve(hfmMesh.normals.size()); + std::fill_n(std::back_inserter(hfmMesh.tangents), hfmMesh.normals.size(), Vectors::UNIT_X); } // Same thing with blend shapes - for (auto& blendShape : fbxMesh.blendshapes) { + for (auto& blendShape : hfmMesh.blendshapes) { if (!blendShape.normals.empty() && blendShape.tangents.empty()) { // Fill with a dummy value to force tangents to be present if there are normals blendShape.tangents.reserve(blendShape.normals.size()); @@ -609,8 +609,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Normal and tangent are always there together packed in normalized xyz32bits word (times 2) const auto normalElement = FBX_NORMAL_ELEMENT; - const int normalsSize = fbxMesh.normals.size() * normalElement.getSize(); - const int tangentsSize = fbxMesh.tangents.size() * normalElement.getSize(); + const int normalsSize = hfmMesh.normals.size() * normalElement.getSize(); + const int tangentsSize = hfmMesh.tangents.size() * normalElement.getSize(); // If there are normals then there should be tangents assert(normalsSize <= tangentsSize); if (tangentsSize > normalsSize) { @@ -620,22 +620,22 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Color attrib const auto colorElement = FBX_COLOR_ELEMENT; - const int colorsSize = fbxMesh.colors.size() * colorElement.getSize(); + const int colorsSize = hfmMesh.colors.size() * colorElement.getSize(); // Texture coordinates are stored in 2 half floats const auto texCoordsElement = gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV); - const int texCoordsSize = fbxMesh.texCoords.size() * texCoordsElement.getSize(); - const int texCoords1Size = fbxMesh.texCoords1.size() * texCoordsElement.getSize(); + const int texCoordsSize = hfmMesh.texCoords.size() * texCoordsElement.getSize(); + const int texCoords1Size = hfmMesh.texCoords1.size() * texCoordsElement.getSize(); // Support for 4 skinning clusters: // 4 Indices are uint8 ideally, uint16 if more than 256. - const auto clusterIndiceElement = (fbxMesh.clusters.size() < 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); // Cluster indices and weights must be the same sizes const int NUM_CLUSTERS_PER_VERT = 4; - const int numVertClusters = (fbxMesh.clusterIndices.size() == fbxMesh.clusterWeights.size() ? fbxMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0); + 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(); @@ -660,9 +660,9 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (normalsSize > 0) { std::vector normalsAndTangents; - normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size()); - for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin(); - normalIt != fbxMesh.normals.constEnd(); + normalsAndTangents.reserve(hfmMesh.normals.size() + hfmMesh.tangents.size()); + for (auto normalIt = hfmMesh.normals.constBegin(), tangentIt = hfmMesh.tangents.constBegin(); + normalIt != hfmMesh.normals.constEnd(); ++normalIt, ++tangentIt) { #if FBX_PACK_NORMALS const auto normal = normalizeDirForPacking(*normalIt); @@ -681,24 +681,24 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Pack colors if (colorsSize > 0) { -#if FBX_PACK_COLORS +#if HFM_PACK_COLORS std::vector colors; - colors.reserve(fbxMesh.colors.size()); - for (const auto& color : fbxMesh.colors) { + colors.reserve(hfmMesh.colors.size()); + for (const auto& color : hfmMesh.colors) { colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f))); } vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data()); #else - vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData()); + vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) hfmMesh.colors.constData()); #endif } // Pack Texcoords 0 and 1 (if exists) if (texCoordsSize > 0) { QVector texCoordData; - texCoordData.reserve(fbxMesh.texCoords.size()); - for (auto& texCoordVec2f : fbxMesh.texCoords) { + texCoordData.reserve(hfmMesh.texCoords.size()); + for (auto& texCoordVec2f : hfmMesh.texCoords) { vec2h texCoordVec2h; texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); @@ -709,8 +709,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { } if (texCoords1Size > 0) { QVector texCoordData; - texCoordData.reserve(fbxMesh.texCoords1.size()); - for (auto& texCoordVec2f : fbxMesh.texCoords1) { + texCoordData.reserve(hfmMesh.texCoords1.size()); + for (auto& texCoordVec2f : hfmMesh.texCoords1) { vec2h texCoordVec2h; texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); @@ -722,22 +722,22 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Clusters data if (clusterIndicesSize > 0) { - if (fbxMesh.clusters.size() < UINT8_MAX) { + if (hfmMesh.clusters.size() < UINT8_MAX) { // yay! we can fit the clusterIndices within 8-bits - int32_t numIndices = fbxMesh.clusterIndices.size(); + int32_t numIndices = hfmMesh.clusterIndices.size(); QVector clusterIndices; clusterIndices.resize(numIndices); for (int32_t i = 0; i < numIndices; ++i) { - assert(fbxMesh.clusterIndices[i] <= UINT8_MAX); - clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]); + assert(hfmMesh.clusterIndices[i] <= UINT8_MAX); + clusterIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]); } vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData()); } else { - vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData()); + vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.constData()); } } if (clusterWeightsSize > 0) { - vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData()); + vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.constData()); } @@ -856,7 +856,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Index and Part Buffers unsigned int totalIndices = 0; - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { totalIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } @@ -875,7 +875,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (extractedMesh.parts.size() > 1) { indexNum = 0; } - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { graphics::Mesh::Part modelPart(indexNum, 0, 0, graphics::Mesh::TRIANGLES); if (part.quadTrianglesIndices.size()) { diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 317342b886..9cd43ddf08 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -533,10 +533,10 @@ bool GLTFReader::addTexture(const QJsonObject& object) { return true; } -bool GLTFReader::parseGLTF(const QByteArray& model) { +bool GLTFReader::parseGLTF(const QByteArray& data) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); - QJsonDocument d = QJsonDocument::fromJson(model); + QJsonDocument d = QJsonDocument::fromJson(data); QJsonObject jsFile = d.object(); bool isvalid = setAsset(jsFile); @@ -697,7 +697,7 @@ glm::mat4 GLTFReader::getModelTransform(const GLTFNode& node) { return tmat; } -bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { +bool GLTFReader::buildGeometry(HFMModel& hfmModel, const QUrl& url) { //Build dependencies QVector> nodeDependencies(_file.nodes.size()); @@ -727,17 +727,17 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { } //Build default joints - geometry.joints.resize(1); - geometry.joints[0].isFree = false; - geometry.joints[0].parentIndex = -1; - geometry.joints[0].distanceToParent = 0; - geometry.joints[0].translation = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); - geometry.joints[0].name = "OBJ"; - geometry.joints[0].isSkeletonJoint = true; + hfmModel.joints.resize(1); + hfmModel.joints[0].isFree = false; + hfmModel.joints[0].parentIndex = -1; + hfmModel.joints[0].distanceToParent = 0; + hfmModel.joints[0].translation = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMin = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMax = glm::vec3(0, 0, 0); + hfmModel.joints[0].name = "OBJ"; + hfmModel.joints[0].isSkeletonJoint = true; - geometry.jointIndices["x"] = 1; + hfmModel.jointIndices["x"] = 1; //Build materials QVector materialIDs; @@ -750,10 +750,10 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { for (int i = 0; i < materialIDs.size(); i++) { QString& matid = materialIDs[i]; - geometry.materials[matid] = FBXMaterial(); - FBXMaterial& fbxMaterial = geometry.materials[matid]; - fbxMaterial._material = std::make_shared(); - setFBXMaterial(fbxMaterial, _file.materials[i]); + hfmModel.materials[matid] = HFMMaterial(); + HFMMaterial& hfmMaterial = hfmModel.materials[matid]; + hfmMaterial._material = std::make_shared(); + setHFMMaterial(hfmMaterial, _file.materials[i]); } @@ -765,9 +765,9 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { if (node.defined["mesh"]) { qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - geometry.meshes.append(FBXMesh()); - FBXMesh& mesh = geometry.meshes[geometry.meshes.size() - 1]; - FBXCluster cluster; + hfmModel.meshes.append(HFMMesh()); + HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; + HFMCluster cluster; cluster.jointIndex = 0; cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, 0, 1, 0, 0, @@ -775,7 +775,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { 0, 0, 0, 1); mesh.clusters.append(cluster); - FBXMeshPart part = FBXMeshPart(); + HFMMeshPart part = HFMMeshPart(); int indicesAccessorIdx = primitive.indices; @@ -886,7 +886,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { mesh.meshExtents.reset(); foreach(const glm::vec3& vertex, mesh.vertices) { mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); + hfmModel.meshExtents.addPoint(vertex); } // since mesh.modelTransform seems to not have any effect I apply the transformation the model @@ -898,7 +898,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { } } - mesh.meshIndex = geometry.meshes.size(); + mesh.meshIndex = hfmModel.meshes.size(); FBXReader::buildModelMesh(mesh, url.toString()); } @@ -910,7 +910,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { return true; } -FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, +HFMModel* GLTFReader::readGLTF(QByteArray& data, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) { _url = url; @@ -922,15 +922,15 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping _url = QUrl(QFileInfo(localFileName).absoluteFilePath()); } - parseGLTF(model); + parseGLTF(data); //_file.dump(); - FBXGeometry* geometryPtr = new FBXGeometry(); - FBXGeometry& geometry = *geometryPtr; + HFMModel* hfmModelPtr = new HFMModel(); + HFMModel& hfmModel = *hfmModelPtr; - buildGeometry(geometry, url); + buildGeometry(hfmModel, url); - //fbxDebugDump(geometry); - return geometryPtr; + //hfmDebugDump(data); + return hfmModelPtr; } @@ -953,7 +953,8 @@ bool GLTFReader::doesResourceExist(const QString& url) { } std::tuple GLTFReader::requestData(QUrl& url) { - auto request = DependencyManager::get()->createResourceRequest(nullptr, url); + auto request = DependencyManager::get()->createResourceRequest( + nullptr, url, true, -1, "GLTFReader::requestData"); if (!request) { return std::make_tuple(false, QByteArray()); @@ -996,8 +997,8 @@ QNetworkReply* GLTFReader::request(QUrl& url, bool isTest) { return netReply; // trying to sync later on. } -FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { - FBXTexture fbxtex = FBXTexture(); +HFMTexture GLTFReader::getHFMTexture(const GLTFTexture& texture) { + HFMTexture fbxtex = HFMTexture(); fbxtex.texcoordSet = 0; if (texture.defined["source"]) { @@ -1013,7 +1014,7 @@ FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { return fbxtex; } -void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& material) { +void GLTFReader::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material) { if (material.defined["name"]) { @@ -1028,17 +1029,17 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia } if (material.defined["emissiveTexture"]) { - fbxmat.emissiveTexture = getFBXTexture(_file.textures[material.emissiveTexture]); + fbxmat.emissiveTexture = getHFMTexture(_file.textures[material.emissiveTexture]); fbxmat.useEmissiveMap = true; } if (material.defined["normalTexture"]) { - fbxmat.normalTexture = getFBXTexture(_file.textures[material.normalTexture]); + fbxmat.normalTexture = getHFMTexture(_file.textures[material.normalTexture]); fbxmat.useNormalMap = true; } if (material.defined["occlusionTexture"]) { - fbxmat.occlusionTexture = getFBXTexture(_file.textures[material.occlusionTexture]); + fbxmat.occlusionTexture = getHFMTexture(_file.textures[material.occlusionTexture]); fbxmat.useOcclusionMap = true; } @@ -1049,14 +1050,14 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia fbxmat.metallic = material.pbrMetallicRoughness.metallicFactor; } if (material.pbrMetallicRoughness.defined["baseColorTexture"]) { - fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + fbxmat.opacityTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + fbxmat.albedoTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.useAlbedoMap = true; } if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { - fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useRoughnessMap = true; - fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { @@ -1180,37 +1181,37 @@ void GLTFReader::retriangulate(const QVector& inIndices, const QVector() }; - FBXGeometry& geometry { *geometryPtr }; + auto hfmModelPtr { std::make_shared() }; + HFMModel& hfmModel { *hfmModelPtr }; OBJTokenizer tokenizer { &buffer }; float scaleGuess = 1.0f; bool needsMaterialLibrary = false; _url = url; - geometry.meshExtents.reset(); - geometry.meshes.append(FBXMesh()); + hfmModel.meshExtents.reset(); + hfmModel.meshes.append(HFMMesh()); try { // call parseOBJGroup as long as it's returning true. Each successful call will - // add a new meshPart to the geometry's single mesh. - while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess, combineParts)) {} + // add a new meshPart to the model's single mesh. + while (parseOBJGroup(tokenizer, mapping, hfmModel, scaleGuess, combineParts)) {} - FBXMesh& mesh = geometry.meshes[0]; + HFMMesh& mesh = hfmModel.meshes[0]; mesh.meshIndex = 0; - geometry.joints.resize(1); - geometry.joints[0].isFree = false; - geometry.joints[0].parentIndex = -1; - geometry.joints[0].distanceToParent = 0; - geometry.joints[0].translation = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); - geometry.joints[0].name = "OBJ"; - geometry.joints[0].isSkeletonJoint = true; + hfmModel.joints.resize(1); + hfmModel.joints[0].isFree = false; + hfmModel.joints[0].parentIndex = -1; + hfmModel.joints[0].distanceToParent = 0; + hfmModel.joints[0].translation = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMin = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMax = glm::vec3(0, 0, 0); + hfmModel.joints[0].name = "OBJ"; + hfmModel.joints[0].isSkeletonJoint = true; - geometry.jointIndices["x"] = 1; + hfmModel.jointIndices["x"] = 1; - FBXCluster cluster; + HFMCluster cluster; cluster.jointIndex = 0; cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, 0, 1, 0, 0, @@ -696,20 +697,20 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m mesh.clusters.append(cluster); QMap materialMeshIdMap; - QVector fbxMeshParts; + QVector hfmMeshParts; for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { - FBXMeshPart& meshPart = mesh.parts[i]; + 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). // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. if (!materialMeshIdMap.contains(face.materialName)) { - // Create a new FBXMesh for this material mapping. + // Create a new HFMMesh for this material mapping. materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); - fbxMeshParts.append(FBXMeshPart()); - FBXMeshPart& meshPartNew = fbxMeshParts.last(); + 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. @@ -744,14 +745,14 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m // clean up old mesh parts. int unmodifiedMeshPartCount = mesh.parts.count(); mesh.parts.clear(); - mesh.parts = QVector(fbxMeshParts); + mesh.parts = QVector(hfmMeshParts); 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) { - FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; + HFMMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); @@ -817,13 +818,13 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m mesh.meshExtents.reset(); foreach(const glm::vec3& vertex, mesh.vertices) { mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); + hfmModel.meshExtents.addPoint(vertex); } // Build the single mesh. FBXReader::buildModelMesh(mesh, url.toString()); - // fbxDebugDump(geometry); + // hfmDebugDump(hfmModel); } catch(const std::exception& e) { qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } @@ -884,38 +885,38 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m if (!objMaterial.used) { continue; } - geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, + hfmModel.materials[materialID] = HFMMaterial(objMaterial.diffuseColor, objMaterial.specularColor, objMaterial.emissiveColor, objMaterial.shininess, objMaterial.opacity); - FBXMaterial& fbxMaterial = geometry.materials[materialID]; - fbxMaterial.materialID = materialID; - fbxMaterial._material = std::make_shared(); - graphics::MaterialPointer modelMaterial = fbxMaterial._material; + HFMMaterial& hfmMaterial = hfmModel.materials[materialID]; + hfmMaterial.materialID = materialID; + hfmMaterial._material = std::make_shared(); + graphics::MaterialPointer modelMaterial = hfmMaterial._material; if (!objMaterial.diffuseTextureFilename.isEmpty()) { - fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; + hfmMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; + hfmMaterial.specularTexture.filename = objMaterial.specularTextureFilename; } if (!objMaterial.emissiveTextureFilename.isEmpty()) { - fbxMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; + hfmMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; } if (!objMaterial.bumpTextureFilename.isEmpty()) { - fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; - fbxMaterial.normalTexture.isBumpmap = true; - fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; + hfmMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; + hfmMaterial.normalTexture.isBumpmap = true; + hfmMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; } if (!objMaterial.opacityTextureFilename.isEmpty()) { - fbxMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; + hfmMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; } - modelMaterial->setEmissive(fbxMaterial.emissiveColor); - modelMaterial->setAlbedo(fbxMaterial.diffuseColor); - modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); - modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess)); + modelMaterial->setEmissive(hfmMaterial.emissiveColor); + modelMaterial->setAlbedo(hfmMaterial.diffuseColor); + modelMaterial->setMetallic(glm::length(hfmMaterial.specularColor)); + modelMaterial->setRoughness(graphics::Material::shininessToRoughness(hfmMaterial.shininess)); bool applyTransparency = false; bool applyShininess = false; @@ -970,7 +971,7 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m } if (applyTransparency) { - fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); + hfmMaterial.opacity = std::max(hfmMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); } if (applyShininess) { modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS); @@ -984,18 +985,18 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m modelMaterial->setFresnel(glm::vec3(1.0f)); } - modelMaterial->setOpacity(fbxMaterial.opacity); + modelMaterial->setOpacity(hfmMaterial.opacity); } - return geometryPtr; + return hfmModelPtr; } -void fbxDebugDump(const FBXGeometry& fbxgeo) { - qCDebug(modelformat) << "---------------- fbxGeometry ----------------"; - qCDebug(modelformat) << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints; - qCDebug(modelformat) << " offset =" << fbxgeo.offset; - qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count(); - foreach (FBXMesh mesh, fbxgeo.meshes) { +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(); @@ -1013,7 +1014,7 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " meshExtents =" << mesh.meshExtents; qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; qCDebug(modelformat) << " parts.count() =" << mesh.parts.count(); - foreach (FBXMeshPart meshPart, mesh.parts) { + foreach (HFMMeshPart meshPart, mesh.parts) { qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); /* @@ -1030,16 +1031,16 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { */ } qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count(); - foreach (FBXCluster cluster, mesh.clusters) { + foreach (HFMCluster cluster, mesh.clusters) { qCDebug(modelformat) << " jointIndex =" << cluster.jointIndex; qCDebug(modelformat) << " inverseBindMatrix =" << cluster.inverseBindMatrix; } } - qCDebug(modelformat) << " jointIndices =" << fbxgeo.jointIndices; - qCDebug(modelformat) << " joints.count() =" << fbxgeo.joints.count(); + qCDebug(modelformat) << " jointIndices =" << hfmModel.jointIndices; + qCDebug(modelformat) << " joints.count() =" << hfmModel.joints.count(); - foreach (FBXJoint joint, fbxgeo.joints) { + foreach (HFMJoint joint, hfmModel.joints) { qCDebug(modelformat) << " isFree =" << joint.isFree; qCDebug(modelformat) << " freeLineage" << joint.freeLineage; qCDebug(modelformat) << " parentIndex" << joint.parentIndex; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index e432a3ea51..0088e8e9d7 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -42,7 +42,7 @@ public: bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. - // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. + // Even though HFMMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); private: void addFrom(const OBJFace* face, int index); @@ -54,7 +54,7 @@ public: } ; // Materials and references to material names can come in any order, and different mesh parts can refer to the same material. -// Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files. +// Therefore it would get pretty hacky to try to use HFMMeshPart to store these as we traverse the files. class OBJMaterial { public: float shininess; @@ -87,13 +87,13 @@ public: QString currentMaterialName; QHash materials; - FBXGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); + HFMModel::Pointer readOBJ(QByteArray& data, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: QUrl _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMModel& hfmModel, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); @@ -103,5 +103,5 @@ 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(FBXMeshPart& meshPart, QString materialID); -void fbxDebugDump(const FBXGeometry& fbxgeo); +void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID); +void hfmDebugDump(const HFMModel& hfmModel); diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index ad7e51fbd3..d4ecbaa5ba 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -25,25 +25,24 @@ #include "GLLogging.h" #include "Config.h" #include "GLHelpers.h" +#include "QOpenGLContextWrapper.h" using namespace gl; bool Context::enableDebugLogger() { +#if defined(Q_OS_MAC) + // OSX does not support GL_KHR_debug or GL_ARB_debug_output + return false; +#else #if defined(DEBUG) || defined(USE_GLES) static bool enableDebugLogger = true; #else static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); #endif - static std::once_flag once; - std::call_once(once, [&] { - // If the previous run crashed, force GL debug logging on - if (qApp->property(hifi::properties::CRASHED).toBool()) { - enableDebugLogger = true; - } - }); return enableDebugLogger; +#endif } @@ -68,8 +67,6 @@ void Context::updateSwapchainMemoryUsage(size_t prevSize, size_t newSize) { } -Context* Context::PRIMARY = nullptr; - Context::Context() {} Context::Context(QWindow* window) { @@ -97,9 +94,6 @@ void Context::release() { _context = nullptr; #endif _window = nullptr; - if (PRIMARY == this) { - PRIMARY = nullptr; - } updateSwapchainMemoryCounter(); } @@ -235,16 +229,9 @@ typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShare GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); -void Context::create() { - if (!PRIMARY) { - PRIMARY = static_cast(qApp->property(hifi::properties::gl::PRIMARY_CONTEXT).value()); - } - - if (PRIMARY) { - _version = PRIMARY->_version; - } - +void Context::create(QOpenGLContext* shareContext) { assert(0 != _hwnd); assert(0 == _hdc); auto hwnd = _hwnd; @@ -338,7 +325,10 @@ void Context::create() { contextAttribs.push_back(0); } contextAttribs.push_back(0); - auto shareHglrc = PRIMARY ? PRIMARY->_hglrc : 0; + if (!shareContext) { + shareContext = qt_gl_global_share_context(); + } + HGLRC shareHglrc = (HGLRC)QOpenGLContextWrapper::nativeContext(shareContext); _hglrc = wglCreateContextAttribsARB(_hdc, shareHglrc, &contextAttribs[0]); } @@ -346,11 +336,6 @@ void Context::create() { throw std::runtime_error("Could not create GL context"); } - if (!PRIMARY) { - PRIMARY = this; - qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY)); - } - if (!makeCurrent()) { throw std::runtime_error("Could not make context current"); } @@ -363,12 +348,11 @@ void Context::create() { #endif - OffscreenContext::~OffscreenContext() { _window->deleteLater(); } -void OffscreenContext::create() { +void OffscreenContext::create(QOpenGLContext* shareContext) { if (!_window) { _window = new QWindow(); _window->setFlags(Qt::MSWindowsOwnDC); @@ -379,5 +363,5 @@ void OffscreenContext::create() { qCDebug(glLogging) << "New Offscreen GLContext, window size = " << windowSize.width() << " , " << windowSize.height(); QGuiApplication::processEvents(); } - Parent::create(); + Parent::create(shareContext); } diff --git a/libraries/gl/src/gl/Context.h b/libraries/gl/src/gl/Context.h index b6160cbd6c..1040868c78 100644 --- a/libraries/gl/src/gl/Context.h +++ b/libraries/gl/src/gl/Context.h @@ -21,6 +21,7 @@ class QSurface; class QWindow; class QOpenGLContext; class QThread; +class QOpenGLDebugMessage; #if defined(Q_OS_WIN) #define GL_CUSTOM_CONTEXT @@ -30,7 +31,6 @@ namespace gl { class Context { protected: QWindow* _window { nullptr }; - static Context* PRIMARY; static void destroyContext(QOpenGLContext* context); #if defined(GL_CUSTOM_CONTEXT) uint32_t _version { 0x0401 }; @@ -48,25 +48,29 @@ namespace gl { public: static bool enableDebugLogger(); + static void debugMessageHandler(const QOpenGLDebugMessage &debugMessage); + static void setupDebugLogging(QOpenGLContext* context); + Context(); Context(QWindow* window); void release(); virtual ~Context(); + QWindow* getWindow() const { return _window; } void clear(); void setWindow(QWindow* window); bool makeCurrent(); static void makeCurrent(QOpenGLContext* context, QSurface* surface); void swapBuffers(); void doneCurrent(); - virtual void create(); + virtual void create(QOpenGLContext* shareContext = nullptr); QOpenGLContext* qglContext(); void moveToThread(QThread* thread); static size_t evalSurfaceMemoryUsage(uint32_t width, uint32_t height, uint32_t pixelSize); static size_t getSwapchainMemoryUsage(); static void updateSwapchainMemoryUsage(size_t prevSize, size_t newSize); - + private: static std::atomic _totalSwapchainMemoryUsage; @@ -77,11 +81,9 @@ namespace gl { class OffscreenContext : public Context { using Parent = Context; - protected: - QWindow* _window { nullptr }; public: virtual ~OffscreenContext(); - void create() override; + void create(QOpenGLContext* shareContext = nullptr) override; }; } diff --git a/libraries/gl/src/gl/ContextQt.cpp b/libraries/gl/src/gl/ContextQt.cpp index dd65c3076c..f554877b2a 100644 --- a/libraries/gl/src/gl/ContextQt.cpp +++ b/libraries/gl/src/gl/ContextQt.cpp @@ -17,6 +17,8 @@ #include #endif +#include + #include "GLHelpers.h" using namespace gl; @@ -47,6 +49,32 @@ void Context::moveToThread(QThread* thread) { qglContext()->moveToThread(thread); } +void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) { + auto severity = debugMessage.severity(); + switch (severity) { + case QOpenGLDebugMessage::NotificationSeverity: + case QOpenGLDebugMessage::LowSeverity: + return; + default: + break; + } + qDebug(glLogging) << debugMessage; + return; +} + +void Context::setupDebugLogging(QOpenGLContext *context) { + QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(context); + QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, nullptr, [](const QOpenGLDebugMessage& message){ + Context::debugMessageHandler(message); + }); + if (logger->initialize()) { + logger->enableMessages(); + logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); + } else { + qCWarning(glLogging) << "OpenGL context does not support debugging"; + } +} + #ifndef GL_CUSTOM_CONTEXT bool Context::makeCurrent() { updateSwapchainMemoryCounter(); @@ -65,21 +93,29 @@ void Context::doneCurrent() { } } +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); const QSurfaceFormat& getDefaultOpenGLSurfaceFormat(); - -void Context::create() { +void Context::create(QOpenGLContext* shareContext) { _context = new QOpenGLContext(); - if (PRIMARY) { - _context->setShareContext(PRIMARY->qglContext()); - } else { - PRIMARY = this; + _context->setFormat(_window->format()); + if (!shareContext) { + shareContext = qt_gl_global_share_context(); } - _context->setFormat(getDefaultOpenGLSurfaceFormat()); - _context->create(); + _context->setShareContext(shareContext); + _context->create(); _swapchainPixelSize = evalGLFormatSwapchainPixelSize(_context->format()); updateSwapchainMemoryCounter(); + + if (!makeCurrent()) { + throw std::runtime_error("Could not make context current"); + } + if (enableDebugLogger()) { + setupDebugLogging(_context); + } + doneCurrent(); + } #endif diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 7ebba4f8d8..15a41c3dc1 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -13,6 +13,8 @@ #include #include +#include "Context.h" + size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format) { size_t pixelSize = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() + format.alphaBufferSize(); // We don't apply the length of the swap chain into this pixelSize since it is not vsible for the Process (on windows). @@ -34,14 +36,54 @@ bool gl::disableGl45() { #endif } +#ifdef Q_OS_MAC +#define SERIALIZE_GL_RENDERING +#endif + +#ifdef SERIALIZE_GL_RENDERING + +// This terrible terrible hack brought to you by the complete lack of reasonable +// OpenGL debugging tools on OSX. Without this serialization code, the UI textures +// frequently become 'glitchy' and get composited onto the main scene in what looks +// like a partially rendered state. +// This looks very much like either state bleeding across the contexts, or bad +// synchronization for the shared OpenGL textures. However, previous attempts to resolve +// it, even with gratuitous use of glFinish hasn't improved the situation + +static std::mutex _globalOpenGLLock; + +void gl::globalLock() { + _globalOpenGLLock.lock(); +} + +void gl::globalRelease(bool finish) { + if (finish) { + glFinish(); + } + _globalOpenGLLock.unlock(); +} + +#else + +void gl::globalLock() {} +void gl::globalRelease(bool finish) {} + +#endif + + void gl::getTargetVersion(int& major, int& minor) { #if defined(USE_GLES) major = 3; minor = 2; +#else +#if defined(Q_OS_MAC) + major = 4; + minor = 1; #else major = 4; minor = disableGl45() ? 1 : 5; #endif +#endif } const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { @@ -57,6 +99,9 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { #else format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); #endif + if (gl::Context::enableDebugLogger()) { + format.setOption(QSurfaceFormat::DebugContext); + } // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); @@ -64,7 +109,6 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { ::gl::getTargetVersion(major, minor); format.setMajorVersion(major); format.setMinorVersion(minor); - QSurfaceFormat::setDefaultFormat(format); }); return format; } diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 6252eba2f0..1865442ef6 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -35,6 +35,9 @@ int glVersionToInteger(QString glVersion); bool isRenderThread(); namespace gl { + void globalLock(); + void globalRelease(bool finish = true); + void withSavedContext(const std::function& f); bool checkGLError(const char* name); diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index a0d976d727..54a386313b 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -14,14 +14,17 @@ using namespace gl; void Uniform::load(GLuint glprogram, int index) { this->index = index; - const GLint NAME_LENGTH = 256; - GLchar glname[NAME_LENGTH]; - GLint length = 0; - glGetActiveUniform(glprogram, index, NAME_LENGTH, &length, &size, &type, glname); - // Length does NOT include the null terminator - // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetActiveUniform.xhtml - name = std::string(glname, length); - binding = glGetUniformLocation(glprogram, glname); + if (index >= 0) { + static const GLint NAME_LENGTH = 1024; + GLchar glname[NAME_LENGTH]; + memset(glname, 0, NAME_LENGTH); + GLint length = 0; + glGetActiveUniform(glprogram, index, NAME_LENGTH, &length, &size, &type, glname); + // Length does NOT include the null terminator + // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetActiveUniform.xhtml + name = std::string(glname, length); + binding = glGetUniformLocation(glprogram, name.c_str()); + } } bool isTextureType(GLenum type) { diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index 1c0ad1a85e..94702a9906 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -63,10 +63,10 @@ int GLWidget::getDeviceHeight() const { return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); } -void GLWidget::createContext() { +void GLWidget::createContext(QOpenGLContext* shareContext) { _context = new gl::Context(); _context->setWindow(windowHandle()); - _context->create(); + _context->create(shareContext); _context->makeCurrent(); _context->clear(); _context->doneCurrent(); diff --git a/libraries/gl/src/gl/GLWidget.h b/libraries/gl/src/gl/GLWidget.h index a0bf8ea0b0..777d43e8af 100644 --- a/libraries/gl/src/gl/GLWidget.h +++ b/libraries/gl/src/gl/GLWidget.h @@ -29,7 +29,7 @@ public: int getDeviceHeight() const; QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); } QPaintEngine* paintEngine() const override; - void createContext(); + void createContext(QOpenGLContext* shareContext = nullptr); bool makeCurrent(); void doneCurrent(); void swapBuffers(); diff --git a/libraries/gl/src/gl/GLWindow.cpp b/libraries/gl/src/gl/GLWindow.cpp index e1e6279b1c..7930e050de 100644 --- a/libraries/gl/src/gl/GLWindow.cpp +++ b/libraries/gl/src/gl/GLWindow.cpp @@ -22,7 +22,7 @@ void GLWindow::createContext(QOpenGLContext* shareContext) { void GLWindow::createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext) { _context = new gl::Context(); _context->setWindow(this); - _context->create(); + _context->create(shareContext); _context->makeCurrent(); _context->clear(); } diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 6598a26c99..f05acb50e9 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -33,6 +33,7 @@ OffscreenGLCanvas::OffscreenGLCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface) { + setFormat(getDefaultOpenGLSurfaceFormat()); } OffscreenGLCanvas::~OffscreenGLCanvas() { @@ -49,12 +50,15 @@ OffscreenGLCanvas::~OffscreenGLCanvas() { } +void OffscreenGLCanvas::setFormat(const QSurfaceFormat& format) { + _context->setFormat(format); +} + bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { if (nullptr != sharedContext) { sharedContext->doneCurrent(); _context->setShareContext(sharedContext); } - _context->setFormat(getDefaultOpenGLSurfaceFormat()); if (!_context->create()) { qFatal("Failed to create OffscreenGLCanvas context"); } @@ -69,38 +73,16 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { if (!_context->makeCurrent(_offscreenSurface)) { qFatal("Unable to make offscreen surface current"); } + _context->doneCurrent(); #else if (!_offscreenSurface->isValid()) { qFatal("Offscreen surface is invalid"); } #endif - if (gl::Context::enableDebugLogger()) { - _context->makeCurrent(_offscreenSurface); - QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); - connect(logger, &QOpenGLDebugLogger::messageLogged, this, &OffscreenGLCanvas::onMessageLogged); - logger->initialize(); - logger->enableMessages(); - logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); - _context->doneCurrent(); - } - return true; } -void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage) { - auto severity = debugMessage.severity(); - switch (severity) { - case QOpenGLDebugMessage::NotificationSeverity: - case QOpenGLDebugMessage::LowSeverity: - return; - default: - break; - } - qDebug(glLogging) << debugMessage; - return; -} - bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); if (glGetString) { diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index a4960ae234..3946f760cf 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -18,11 +18,13 @@ class QOpenGLContext; class QOffscreenSurface; class QOpenGLDebugMessage; +class QSurfaceFormat; class OffscreenGLCanvas : public QObject { public: OffscreenGLCanvas(); ~OffscreenGLCanvas(); + void setFormat(const QSurfaceFormat& format); bool create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); @@ -35,9 +37,6 @@ public: void setThreadContext(); static bool restoreThreadContext(); -private slots: - void onMessageLogged(const QOpenGLDebugMessage &debugMessage); - protected: void clearThreadContext(); diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 0b153a5ae8..fbebb1128d 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -13,6 +13,10 @@ #include +#ifdef Q_OS_WIN +#include +#endif + uint32_t QOpenGLContextWrapper::currentContextVersion() { QOpenGLContext* context = QOpenGLContext::currentContext(); if (!context) { @@ -45,6 +49,19 @@ void QOpenGLContextWrapper::setFormat(const QSurfaceFormat& format) { _context->setFormat(format); } +#ifdef Q_OS_WIN +void* QOpenGLContextWrapper::nativeContext(QOpenGLContext* context) { + HGLRC result = 0; + if (context != nullptr) { + auto nativeHandle = context->nativeHandle(); + if (nativeHandle.canConvert()) { + result = nativeHandle.value().context(); + } + } + return result; +} +#endif + bool QOpenGLContextWrapper::create() { return _context->create(); } diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index b09ad1a4c3..32ba7f22e8 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -13,6 +13,7 @@ #define hifi_QOpenGLContextWrapper_h #include +#include class QOpenGLContext; class QSurface; @@ -21,6 +22,10 @@ class QThread; class QOpenGLContextWrapper { public: +#ifdef Q_OS_WIN + static void* nativeContext(QOpenGLContext* context); +#endif + QOpenGLContextWrapper(); QOpenGLContextWrapper(QOpenGLContext* context); virtual ~QOpenGLContextWrapper(); diff --git a/libraries/gpu-gl-common/CMakeLists.txt b/libraries/gpu-gl-common/CMakeLists.txt index 2b6f8d4d40..70cf3536ed 100644 --- a/libraries/gpu-gl-common/CMakeLists.txt +++ b/libraries/gpu-gl-common/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu-gl-common) setup_hifi_library(Concurrent) -link_hifi_libraries(shared gl gpu) +link_hifi_libraries(shared gl gpu shaders) GroupSources("src") target_opengl() diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 13c21d89e7..c1ce05c18b 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -67,6 +67,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_clearFramebuffer), (&::gpu::gl::GLBackend::do_blit), (&::gpu::gl::GLBackend::do_generateTextureMips), + (&::gpu::gl::GLBackend::do_generateTextureMipsWithPipeline), (&::gpu::gl::GLBackend::do_advance), @@ -166,6 +167,10 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() {} void GLBackend::shutdown() { + if (_mipGenerationFramebufferId) { + glDeleteFramebuffers(1, &_mipGenerationFramebufferId); + _mipGenerationFramebufferId = 0; + } killInput(); killTransform(); killTextureManagementStage(); @@ -703,37 +708,37 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { void GLBackend::releaseBuffer(GLuint id, Size size) const { Lock lock(_trashMutex); - _buffersTrash.push_back({ id, size }); + _currentFrameTrash.buffersTrash.push_back({ id, size }); } void GLBackend::releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const { Lock lock(_trashMutex); - _externalTexturesTrash.push_back({ id, recycler }); + _currentFrameTrash.externalTexturesTrash.push_back({ id, recycler }); } void GLBackend::releaseTexture(GLuint id, Size size) const { Lock lock(_trashMutex); - _texturesTrash.push_back({ id, size }); + _currentFrameTrash.texturesTrash.push_back({ id, size }); } void GLBackend::releaseFramebuffer(GLuint id) const { Lock lock(_trashMutex); - _framebuffersTrash.push_back(id); + _currentFrameTrash.framebuffersTrash.push_back(id); } void GLBackend::releaseShader(GLuint id) const { Lock lock(_trashMutex); - _shadersTrash.push_back(id); + _currentFrameTrash.shadersTrash.push_back(id); } void GLBackend::releaseProgram(GLuint id) const { Lock lock(_trashMutex); - _programsTrash.push_back(id); + _currentFrameTrash.programsTrash.push_back(id); } void GLBackend::releaseQuery(GLuint id) const { Lock lock(_trashMutex); - _queriesTrash.push_back(id); + _currentFrameTrash.queriesTrash.push_back(id); } void GLBackend::queueLambda(const std::function lambda) const { @@ -741,6 +746,81 @@ void GLBackend::queueLambda(const std::function lambda) const { _lambdaQueue.push_back(lambda); } +void GLBackend::FrameTrash::cleanup() { + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + + { + std::vector ids; + ids.reserve(buffersTrash.size()); + for (auto pair : buffersTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteBuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + ids.reserve(framebuffersTrash.size()); + for (auto id : framebuffersTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); + } + } + + { + std::vector ids; + ids.reserve(texturesTrash.size()); + for (auto pair : texturesTrash) { + ids.push_back(pair.first); + } + if (!ids.empty()) { + glDeleteTextures((GLsizei)ids.size(), ids.data()); + } + } + + { + if (!externalTexturesTrash.empty()) { + std::vector fences; + fences.resize(externalTexturesTrash.size()); + for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { + fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + // External texture fences will be read in another thread/context, so we need a flush + glFlush(); + size_t index = 0; + for (auto pair : externalTexturesTrash) { + auto fence = fences[index++]; + pair.second(pair.first, fence); + } + } + } + + for (auto id : programsTrash) { + glDeleteProgram(id); + } + + for (auto id : shadersTrash) { + glDeleteShader(id); + } + + { + std::vector ids; + ids.reserve(queriesTrash.size()); + for (auto id : queriesTrash) { + ids.push_back(id); + } + if (!ids.empty()) { + glDeleteQueries((GLsizei)ids.size(), ids.data()); + } + } + +} + void GLBackend::recycle() const { PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { @@ -754,112 +834,16 @@ void GLBackend::recycle() const { } } - { - std::vector ids; - std::list> buffersTrash; - { - Lock lock(_trashMutex); - std::swap(_buffersTrash, buffersTrash); - } - ids.reserve(buffersTrash.size()); - for (auto pair : buffersTrash) { - ids.push_back(pair.first); - } - if (!ids.empty()) { - glDeleteBuffers((GLsizei)ids.size(), ids.data()); - } + while (!_previousFrameTrashes.empty()) { + _previousFrameTrashes.front().cleanup(); + _previousFrameTrashes.pop_front(); } + _previousFrameTrashes.emplace_back(); { - std::vector ids; - std::list framebuffersTrash; - { - Lock lock(_trashMutex); - std::swap(_framebuffersTrash, framebuffersTrash); - } - ids.reserve(framebuffersTrash.size()); - for (auto id : framebuffersTrash) { - ids.push_back(id); - } - if (!ids.empty()) { - glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); - } - } - - { - std::vector ids; - std::list> texturesTrash; - { - Lock lock(_trashMutex); - std::swap(_texturesTrash, texturesTrash); - } - ids.reserve(texturesTrash.size()); - for (auto pair : texturesTrash) { - ids.push_back(pair.first); - } - if (!ids.empty()) { - glDeleteTextures((GLsizei)ids.size(), ids.data()); - } - } - - { - std::list> externalTexturesTrash; - { - Lock lock(_trashMutex); - std::swap(_externalTexturesTrash, externalTexturesTrash); - } - if (!externalTexturesTrash.empty()) { - std::vector fences; - fences.resize(externalTexturesTrash.size()); - for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { - fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - } - // External texture fences will be read in another thread/context, so we need a flush - glFlush(); - size_t index = 0; - for (auto pair : externalTexturesTrash) { - auto fence = fences[index++]; - pair.second(pair.first, fence); - } - } - } - - { - std::list programsTrash; - { - Lock lock(_trashMutex); - std::swap(_programsTrash, programsTrash); - } - for (auto id : programsTrash) { - glDeleteProgram(id); - } - } - - { - std::list shadersTrash; - { - Lock lock(_trashMutex); - std::swap(_shadersTrash, shadersTrash); - } - for (auto id : shadersTrash) { - glDeleteShader(id); - } - } - - { - std::vector ids; - std::list queriesTrash; - { - Lock lock(_trashMutex); - std::swap(_queriesTrash, queriesTrash); - } - ids.reserve(queriesTrash.size()); - for (auto id : queriesTrash) { - ids.push_back(id); - } - if (!ids.empty()) { - glDeleteQueries((GLsizei)ids.size(), ids.data()); - } + Lock lock(_trashMutex); + _previousFrameTrashes.back().swap(_currentFrameTrash); + _previousFrameTrashes.back().fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); } _textureManagement._transferEngine->manageMemory(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 0b76ef17de..37dde5b08e 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -288,6 +288,7 @@ public: virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) final; // Transform Stage virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; @@ -407,6 +408,8 @@ public: protected: virtual GLint getRealUniformLocation(GLint location) const; + virtual void draw(GLenum mode, uint32 numVertices, uint32 startVertex) = 0; + void recycle() const override; // FIXME instead of a single flag, create a features struct similar to @@ -416,16 +419,34 @@ protected: static const size_t INVALID_OFFSET = (size_t)-1; bool _inRenderTransferPass{ false }; int _currentDraw{ -1 }; - - std::list profileRanges; + + struct FrameTrash { + GLsync fence = nullptr; + std::list> buffersTrash; + std::list> texturesTrash; + std::list> externalTexturesTrash; + std::list framebuffersTrash; + std::list shadersTrash; + std::list programsTrash; + std::list queriesTrash; + + void swap(FrameTrash& other) { + buffersTrash.swap(other.buffersTrash); + texturesTrash.swap(other.texturesTrash); + externalTexturesTrash.swap(other.externalTexturesTrash); + framebuffersTrash.swap(other.framebuffersTrash); + shadersTrash.swap(other.shadersTrash); + programsTrash.swap(other.programsTrash); + queriesTrash.swap(other.queriesTrash); + } + + void cleanup(); + }; + mutable Mutex _trashMutex; - mutable std::list> _buffersTrash; - mutable std::list> _texturesTrash; - mutable std::list> _externalTexturesTrash; - mutable std::list _framebuffersTrash; - mutable std::list _shadersTrash; - mutable std::list _programsTrash; - mutable std::list _queriesTrash; + mutable FrameTrash _currentFrameTrash; + mutable std::list _previousFrameTrashes; + std::list profileRanges; mutable std::list> _lambdaQueue; void renderPassTransfer(const Batch& batch); @@ -640,18 +661,21 @@ protected: } } _pipeline; - // Backend dependant compilation of the shader + // Backend dependent compilation of the shader virtual void postLinkProgram(ShaderObject& programObject, const Shader& program) const; virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler); virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler); - virtual std::string getBackendShaderHeader() const = 0; - // For a program, this will return a string containing all the source files (without any - // backend headers or defines). For a vertex, fragment or geometry shader, this will - // return the fully customized shader with all the version and backend specific + + // For a program, this will return a string containing all the source files (without any + // backend headers or defines). For a vertex, fragment or geometry shader, this will + // return the fully customized shader with all the version and backend specific // preprocessor directives // The program string returned can be used as a key for a cache of shader binaries // The shader strings can be reliably sent to the low level `compileShader` functions - virtual std::string getShaderSource(const Shader& shader, int version) final; + virtual std::string getShaderSource(const Shader& shader, shader::Variant version) final; + shader::Variant getShaderVariant() const { return isStereo() ? shader::Variant::Stereo : shader::Variant::Mono; } + virtual shader::Dialect getShaderDialect() const = 0; + class ElementResource { public: gpu::Element _element; @@ -696,6 +720,8 @@ protected: virtual void initTextureManagementStage(); virtual void killTextureManagementStage(); + GLuint _mipGenerationFramebufferId{ 0 }; + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; friend class GLState; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp index 7a06b3af86..1e811653f9 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp @@ -54,7 +54,7 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { // check the program cache // pick the program version #ifdef GPU_STEREO_CAMERA_BUFFER - GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version)isStereo()); + GLuint glprogram = pipelineObject->_program->getProgram(getShaderVariant()); #else GLuint glprogram = pipelineObject->_program->getProgram(); #endif diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 7267e29be2..f737842ec0 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -25,120 +25,53 @@ static const std::array SHADER_DOMAINS{ { GL_GEOMETRY_SHADER, } }; -// Domain specific defines -// Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES{ { - "#define GPU_VERTEX_SHADER", - "#define GPU_PIXEL_SHADER", - "#define GPU_GEOMETRY_SHADER", -} }; - -// Stereo specific defines -static const std::string stereoVersion{ -#ifdef GPU_STEREO_DRAWCALL_INSTANCED -R"SHADER( -#define GPU_TRANSFORM_IS_STEREO -#define GPU_TRANSFORM_STEREO_CAMERA -#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED -#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN -)SHADER" -#endif -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -#ifdef GPU_STEREO_CAMERA_BUFFER -R"SHADER( -#define GPU_TRANSFORM_IS_STEREO -#define GPU_TRANSFORM_STEREO_CAMERA -#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED -)SHADER" -#else -R"SHADER( -#define GPU_TRANSFORM_IS_STEREO -)SHADER" -#endif -#endif -}; - -// TextureTable specific defines -static const std::string textureTableVersion { - "#extension GL_ARB_bindless_texture : require\n#define GPU_TEXTURE_TABLE_BINDLESS\n" -}; - -// Versions specific of the shader -static const std::array VERSION_DEFINES { { - "", - stereoVersion -} }; - -static std::string getShaderTypeString(Shader::Type type) { - switch (type) { - case Shader::Type::VERTEX: - return "vertex"; - case Shader::Type::PIXEL: - return "pixel"; - case Shader::Type::GEOMETRY: - return "geometry"; - case Shader::Type::PROGRAM: - return "program"; - default: - qFatal("Unexpected shader type %d", type); - Q_UNREACHABLE(); - } -} - -std::string GLBackend::getShaderSource(const Shader& shader, int version) { +std::string GLBackend::getShaderSource(const Shader& shader, shader::Variant variant) { if (shader.isProgram()) { std::string result; - result.append("// VERSION " + std::to_string(version)); for (const auto& subShader : shader.getShaders()) { - result.append("//-------- "); - result.append(getShaderTypeString(subShader->getType())); - result.append("\n"); - result.append(subShader->getSource().getCode()); + if (subShader) { + result += subShader->getSource().getSource(getShaderDialect(), variant); + } } return result; - } - - std::string shaderDefines = getBackendShaderHeader() + "\n" - + (supportsBindless() ? textureTableVersion : "\n") - + DOMAIN_DEFINES[shader.getType()] + "\n" - + VERSION_DEFINES[version]; - - return shaderDefines + "\n" + shader.getSource().getCode(); + } + return shader.getSource().getSource(getShaderDialect(), variant); } GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) { // Any GLSLprogram ? normally yes... GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; GLShader::ShaderObjects shaderObjects; - Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + const auto& variants = shader::allVariants(); + Shader::CompilationLogs compilationLogs(variants.size()); shader.incrementCompilationAttempt(); - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& shaderObject = shaderObjects[version]; - auto shaderSource = getShaderSource(shader, version); + for (const auto& variant : variants) { + auto index = static_cast(variant); + auto shaderSource = getShaderSource(shader, variant); + auto& shaderObject = shaderObjects[index]; if (handler) { bool retest = true; std::string currentSrc = shaderSource; // When a Handler is specified, we can try multiple times to build the shader and let the handler change the source if the compilation fails. - // The retest bool is set to false as soon as the compilation succeed to wexit the while loop. + // The retest bool is set to false as soon as the compilation succeed to exit the while loop. // The handler tells us if we should retry or not while returning a modified version of the source. while (retest) { - bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderObject.glshader, compilationLogs[version].message); - compilationLogs[version].compiled = result; + bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderObject.glshader, compilationLogs[index].message); + compilationLogs[index].compiled = result; if (!result) { std::string newSrc; - retest = handler(shader, currentSrc, compilationLogs[version], newSrc); + retest = handler(shader, currentSrc, compilationLogs[index], newSrc); currentSrc = newSrc; } else { retest = false; } } } else { - compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderObject.glshader, compilationLogs[version].message); + compilationLogs[index].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderObject.glshader, compilationLogs[index].message); } - if (!compilationLogs[version].compiled) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); + if (!compilationLogs[index].compiled) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[index].message.c_str(); shader.setCompilationLogs(compilationLogs); return nullptr; } @@ -162,11 +95,13 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: GLShader::ShaderObjects programObjects; program.incrementCompilationAttempt(); - Shader::CompilationLogs compilationLogs(GLShader::NumVersions); + const auto& variants = shader::allVariants(); + Shader::CompilationLogs compilationLogs(variants.size()); - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& programObject = programObjects[version]; - auto programSource = getShaderSource(program, version); + for (const auto& variant : variants) { + auto index = static_cast(variant); + auto& programObject = programObjects[index]; + auto programSource = getShaderSource(program, variant); auto hash = ::gl::getShaderHash(programSource); CachedShader cachedBinary; @@ -199,11 +134,11 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: for (auto subShader : program.getShaders()) { auto object = GLShader::sync((*this), *subShader, handler); if (object) { - shaderGLObjects.push_back(object->_shaderObjects[version].glshader); + shaderGLObjects.push_back(object->_shaderObjects[index].glshader); } else { qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; - compilationLogs[version].compiled = false; - compilationLogs[version].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?"); + compilationLogs[index].compiled = false; + compilationLogs[index].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?"); program.setCompilationLogs(compilationLogs); return nullptr; } @@ -211,9 +146,9 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: glprogram = ::gl::buildProgram(shaderGLObjects); - if (!::gl::linkProgram(glprogram, compilationLogs[version].message)) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); - compilationLogs[version].compiled = false; + if (!::gl::linkProgram(glprogram, compilationLogs[index].message)) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[index].message.c_str(); + compilationLogs[index].compiled = false; glDeleteProgram(glprogram); glprogram = 0; return nullptr; @@ -228,12 +163,12 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: } if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[index].message.c_str(); program.setCompilationLogs(compilationLogs); return nullptr; } - compilationLogs[version].compiled = true; + compilationLogs[index].compiled = true; programObject.glprogram = glprogram; postLinkProgram(programObject, program); } @@ -249,7 +184,10 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: static const GLint INVALID_UNIFORM_INDEX = -1; GLint GLBackend::getRealUniformLocation(GLint location) const { - auto& shader = _pipeline._programShader->_shaderObjects[(GLShader::Version)isStereo()]; + auto variant = isStereo() ? shader::Variant::Stereo : shader::Variant::Mono; + auto index = static_cast(variant); + + auto& shader = _pipeline._programShader->_shaderObjects[index]; auto itr = shader.uniformRemap.find(location); if (itr == shader.uniformRemap.end()) { // This shouldn't happen, because we use reflection to determine all the possible @@ -264,20 +202,23 @@ GLint GLBackend::getRealUniformLocation(GLint location) const { void GLBackend::postLinkProgram(ShaderObject& shaderObject, const Shader& program) const { const auto& glprogram = shaderObject.glprogram; - const auto& expectedUniforms = program.getUniforms(); - const auto expectedLocationsByName = expectedUniforms.getLocationsByName(); - const auto uniforms = ::gl::Uniform::load(glprogram, expectedUniforms.getNames()); - auto& uniformRemap = shaderObject.uniformRemap; + const auto& expectedUniforms = program.getReflection().uniforms; - // Pre-initialize all the uniforms with an invalid location - for (const auto& entry : expectedLocationsByName) { + auto& uniformRemap = shaderObject.uniformRemap; + // initialize all the uniforms with an invalid location + for (const auto& entry : expectedUniforms) { uniformRemap[entry.second] = INVALID_UNIFORM_INDEX; } - // Now load up all the actual found uniform location + + // Get the actual uniform locations from the shader + const auto names = Shader::Reflection::getNames(expectedUniforms); + const auto uniforms = ::gl::Uniform::load(glprogram, names); + + // Now populate the remapping with the found locations for (const auto& uniform : uniforms) { const auto& name = uniform.name; - const auto& expectedLocation = expectedLocationsByName.at(name); + const auto& expectedLocation = expectedUniforms.at(name); const auto& location = uniform.binding; uniformRemap[expectedLocation] = location; } @@ -462,3 +403,4 @@ void GLBackend::initShaderBinaryCache() { void GLBackend::killShaderBinaryCache() { ::gl::saveShaderCache(_shaderBinaryCache._binaries); } + diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp index f4fb3fcf2c..b74ff079d7 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp @@ -79,3 +79,55 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { object->generateMips(); } + +void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + // Always make sure the GLObject is in sync + GLTexture* object = syncGPUObject(resourceTexture); + if (object) { + GLuint to = object->_texture; + glActiveTexture(GL_TEXTURE0 + gpu::slot::texture::MipCreationInput); + glBindTexture(object->_target, to); + (void)CHECK_GL_ERROR(); + } else { + return; + } + + auto numMips = batch._params[paramOffset + 1]._int; + if (numMips < 0) { + numMips = resourceTexture->getNumMips(); + } else { + numMips = std::min(numMips, (int)resourceTexture->getNumMips()); + } + + if (_mipGenerationFramebufferId == 0) { + glGenFramebuffers(1, &_mipGenerationFramebufferId); + Q_ASSERT(_mipGenerationFramebufferId > 0); + } + + glBindFramebuffer(GL_FRAMEBUFFER, _mipGenerationFramebufferId); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + + for (int level = 1; level < numMips; level++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, object->_id, level); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level - 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1); + + const auto mipDimensions = resourceTexture->evalMipDimensions(level); + glViewport(0, 0, mipDimensions.x, mipDimensions.y); + draw(GL_TRIANGLE_STRIP, 4, 0); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMips - 1); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + resetOutputStage(); + // Restore viewport + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); +} diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp index a099e6e66a..e00dc9fc25 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp @@ -52,7 +52,7 @@ GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { // Special case for view correction matrices, any pipeline that declares the correction buffer // uniform will automatically have it provided without any client code necessary. // Required for stable lighting in the HMD. - object->_cameraCorrection = shader->getUniformBuffers().isValid(gpu::slot::buffer::CameraCorrection); + object->_cameraCorrection = shader->getReflection().validUniformBuffer(gpu::slot::buffer::CameraCorrection); object->_program = programObject; object->_state = stateObject; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLShader.h b/libraries/gpu-gl-common/src/gpu/gl/GLShader.h index 5d5d8a4a3c..1d56bb2122 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLShader.h @@ -14,43 +14,45 @@ namespace gpu { namespace gl { struct ShaderObject { - GLuint glshader { 0 }; - GLuint glprogram { 0 }; + enum class BindingType + { + INPUT, + OUTPUT, + TEXTURE, + SAMPLER, + UNIFORM_BUFFER, + RESOURCE_BUFFER, + UNIFORM, + }; - using LocationMap = std::unordered_map ; - LocationMap uniformRemap; + using LocationMap = std::unordered_map; + using ReflectionMap = std::map; + using UniformMap = std::unordered_map; + + GLuint glshader{ 0 }; + GLuint glprogram{ 0 }; + + UniformMap uniformRemap; }; class GLShader : public GPUObject { public: static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); - - enum Version { - Mono = 0, - Stereo, - - NumVersions - }; - using ShaderObject = gpu::gl::ShaderObject; - using ShaderObjects = std::array< ShaderObject, NumVersions >; - - using UniformMapping = std::map; - using UniformMappingVersions = std::vector; + using ShaderObjects = std::array; GLShader(const std::weak_ptr& backend); ~GLShader(); ShaderObjects _shaderObjects; - GLuint getProgram(Version version = Mono) const { - return _shaderObjects[version].glprogram; + GLuint getProgram(shader::Variant version = shader::Variant::Mono) const { + return _shaderObjects[static_cast(version)].glprogram; } const std::weak_ptr _backend; }; -} } - +}} // namespace gpu::gl #endif diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLShared.h b/libraries/gpu-gl-common/src/gpu/gl/GLShared.h index f67439f96a..1f40f7b7da 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLShared.h @@ -19,7 +19,11 @@ Q_DECLARE_LOGGING_CATEGORY(gpugllogging) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) +#if defined(__clang__) +#define BUFFER_OFFSET(bytes) (reinterpret_cast(bytes)) +#else #define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes)) +#endif namespace gpu { namespace gl { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h index e9a55ad8e2..d3e8f386de 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h @@ -25,8 +25,10 @@ struct GLFilterMode { class GLTextureTransferEngine { public: - virtual ~GLTextureTransferEngine() {} using Pointer = std::shared_ptr; + + virtual ~GLTextureTransferEngine() = default; + /// Called once per frame to perform any require memory management or transfer work virtual void manageMemory() = 0; virtual void shutdown() = 0; diff --git a/libraries/gpu-gl/CMakeLists.txt b/libraries/gpu-gl/CMakeLists.txt index faddab8531..225c795754 100644 --- a/libraries/gpu-gl/CMakeLists.txt +++ b/libraries/gpu-gl/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu-gl) setup_hifi_library(Concurrent) -link_hifi_libraries(shared gl gpu gpu-gl-common) +link_hifi_libraries(shared gl gpu gpu-gl-common shaders) if (UNIX) target_link_libraries(${TARGET_NAME} pthread) endif(UNIX) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 88cf89ad99..43ae4691b9 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -20,6 +20,29 @@ using namespace gpu::gl41; const std::string GL41Backend::GL41_VERSION { "GL41" }; +void GL41Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index e5f7415107..881487c9db 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -130,6 +130,9 @@ public: }; protected: + + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; @@ -167,8 +170,7 @@ protected: // Output stage void do_blit(const Batch& batch, size_t paramOffset) override; - std::string getBackendShaderHeader() const override; - + shader::Dialect getShaderDialect() const override { return shader::Dialect::glsl410; } void postLinkProgram(ShaderObject& programObject, const Shader& program) const override; }; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp index 46f91fdc15..f33dd91d03 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp @@ -12,22 +12,13 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl41; -// GLSL version -std::string GL41Backend::getBackendShaderHeader() const { - static const std::string header( - R"SHADER(#version 410 core - #define GPU_GL410 - #define BITFIELD int - )SHADER"); - return header; -} - void GL41Backend::postLinkProgram(ShaderObject& programObject, const Shader& program) const { Parent::postLinkProgram(programObject, program); const auto& glprogram = programObject.glprogram; + const auto& reflection = program.getReflection(); // For the UBOs, use glUniformBlockBinding to fixup the locations based on the reflection { - const auto expectedUbos = program.getUniformBuffers().getLocationsByName(); + const auto& expectedUbos = reflection.uniformBuffers; auto ubos = ::gl::UniformBlock::load(glprogram); for (const auto& ubo : ubos) { const auto& name = ubo.name; @@ -41,7 +32,7 @@ void GL41Backend::postLinkProgram(ShaderObject& programObject, const Shader& pro // For the Textures, use glUniform1i to fixup the active texture slots based on the reflection { - const auto expectedTextures = program.getTextures().getLocationsByName(); + const auto& expectedTextures = reflection.textures; for (const auto& expectedTexture : expectedTextures) { auto location = glGetUniformLocation(glprogram, expectedTexture.first.c_str()); if (location < 0) { @@ -53,11 +44,12 @@ void GL41Backend::postLinkProgram(ShaderObject& programObject, const Shader& pro // For the resource buffers, do the same as for the textures, since in GL 4.1 that's how they're implemented { - const auto expectedResourceBuffers = program.getResourceBuffers().getLocationsByName(); - const auto resourceBufferUniforms = ::gl::Uniform::loadByName(glprogram, program.getResourceBuffers().getNames()); + const auto& expectedResourceBuffers = reflection.resourceBuffers; + const auto names = Shader::Reflection::getNames(expectedResourceBuffers); + const auto resourceBufferUniforms = ::gl::Uniform::loadByName(glprogram, names); for (const auto& resourceBuffer : resourceBufferUniforms) { const auto& targetBinding = expectedResourceBuffers.at(resourceBuffer.name); - glProgramUniform1i(glprogram, resourceBuffer.binding, targetBinding + GL41Backend::RESOURCE_BUFFER_SLOT0_TEX_UNIT); + glProgramUniform1i(glprogram, resourceBuffer.binding, targetBinding); } } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index bbe011d237..e86eae2c2d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -42,6 +42,30 @@ void GL45Backend::recycle() const { Parent::recycle(); } +void GL45Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 30656b47c7..c1ce074188 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -23,7 +23,7 @@ #define GPU_BINDLESS_TEXTURES 0 namespace gpu { namespace gl45 { - + using namespace gpu::gl; using TextureWeakPointer = std::weak_ptr; @@ -56,6 +56,7 @@ public: using Parent = GLTexture; friend class GL45Backend; static GLuint allocate(const Texture& texture); + protected: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; @@ -88,6 +89,7 @@ public: virtual const Bindless& getBindless() const; void releaseBindless() const; void recreateBindless() const; + private: mutable Bindless _bindless; #endif @@ -98,10 +100,11 @@ public: mutable Sampler _cachedSampler{ getInvalidSampler() }; }; -#if GPU_BINDLESS_TEXTURES +#if GPU_BINDLESS_TEXTURES class GL45TextureTable : public GLObject { static GLuint allocate(); using Parent = GLObject; + public: using BindlessArray = std::array; @@ -116,7 +119,6 @@ public: }; #endif - // // Textures that have fixed allocation sizes and cannot be managed at runtime // @@ -134,12 +136,13 @@ public: void allocateStorage() const; void syncSampler() const override; - const Size _size { 0 }; + const Size _size{ 0 }; }; class GL45AttachmentTexture : public GL45FixedAllocationTexture { using Parent = GL45FixedAllocationTexture; friend class GL45Backend; + protected: GL45AttachmentTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45AttachmentTexture(); @@ -148,6 +151,7 @@ public: class GL45StrictResourceTexture : public GL45FixedAllocationTexture { using Parent = GL45FixedAllocationTexture; friend class GL45Backend; + protected: GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45StrictResourceTexture(); @@ -179,6 +183,7 @@ public: class GL45ResourceTexture : public GL45VariableAllocationTexture { using Parent = GL45VariableAllocationTexture; friend class GL45Backend; + protected: GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture); @@ -186,7 +191,6 @@ public: size_t promote() override; size_t demote() override; void populateTransferQueue(TransferQueue& pendingTransfers) override; - void allocateStorage(uint16 mip); Size copyMipsFromTexture(); @@ -226,9 +230,9 @@ public: }; #endif - protected: + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; void recycle() const override; GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; @@ -243,7 +247,6 @@ protected: GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; - // Draw Stage void do_draw(const Batch& batch, size_t paramOffset) override; void do_drawIndexed(const Batch& batch, size_t paramOffset) override; @@ -269,7 +272,7 @@ protected: void do_blit(const Batch& batch, size_t paramOffset) override; // Shader Stage - std::string getBackendShaderHeader() const override; + shader::Dialect getShaderDialect() const override; // Texture Management Stage void initTextureManagementStage() override; @@ -281,9 +284,8 @@ protected: #endif }; -} } +}} // namespace gpu::gl45 Q_DECLARE_LOGGING_CATEGORY(gpugl45logging) #endif - diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index 6cc0d226d6..cf8279b8e6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -7,22 +7,16 @@ // #include "GL45Backend.h" #include -//#include using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -// GLSL version -std::string GL45Backend::getBackendShaderHeader() const { - static const std::string header( - R"SHADER(#version 450 core - #define GPU_GL450 - #define BITFIELD int - )SHADER" -#ifdef GPU_SSBO_TRANSFORM_OBJECT - R"SHADER(#define GPU_SSBO_TRANSFORM_OBJECT)SHADER" +shader::Dialect GL45Backend::getShaderDialect() const { +#if defined(Q_OS_MAC) + // We build, but don't actually use GL 4.5 on OSX + throw std::runtime_error("GL 4.5 unavailable on OSX"); +#else + return shader::Dialect::glsl450; #endif - ); - return header; } diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index 82bf670781..3e529f7dcd 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gpu-gles) setup_hifi_library(Gui Concurrent) -link_hifi_libraries(shared gl gpu gpu-gl-common) +link_hifi_libraries(shared shaders gl gpu gpu-gl-common) GroupSources("src") target_opengl() diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp index b277889771..cb40b4fc9b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp @@ -20,6 +20,29 @@ using namespace gpu::gles; const std::string GLESBackend::GLES_VERSION { "GLES" }; +void GLESBackend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index 56ae41da31..aaa1be5892 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -27,11 +27,11 @@ class GLESBackend : public GLBackend { friend class Context; public: - static const GLint TRANSFORM_OBJECT_SLOT { 31 }; static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 }; static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 }; static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 }; static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 }; + explicit GLESBackend(bool syncCache) : Parent(syncCache) {} GLESBackend() : Parent() {} virtual ~GLESBackend() { @@ -126,6 +126,9 @@ public: }; protected: + + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; @@ -163,7 +166,7 @@ protected: // Output stage void do_blit(const Batch& batch, size_t paramOffset) override; - std::string getBackendShaderHeader() const override; + shader::Dialect getShaderDialect() const override { return shader::Dialect::glsl310es; } }; } } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp index 5e4da4d1fe..8cadb1a43f 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -10,7 +10,6 @@ namespace gpu { namespace gles { - class GLESBuffer : public gpu::gl::GLBuffer { using Parent = gpu::gl::GLBuffer; static GLuint allocate() { @@ -19,11 +18,20 @@ namespace gpu { return result; } + ~GLESBuffer() { + if (_texBuffer) { + auto backend = _backend.lock(); + if (backend) { + backend->releaseTexture(_texBuffer, 0); + } + } + } + public: GLESBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLESBuffer* original) : Parent(backend, buffer, allocate()) { - glBindBuffer(GL_ARRAY_BUFFER, _buffer); - glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); + glBufferData(GL_COPY_WRITE_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); if (original && original->_size) { glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); @@ -37,20 +45,34 @@ namespace gpu { } void transfer() override { - glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); (void)CHECK_GL_ERROR(); Size offset; Size size; Size currentPage { 0 }; auto data = _gpuObject._renderSysmem.readData(); while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { - glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset); + glBufferSubData(GL_COPY_WRITE_BUFFER, offset, size, data + offset); (void)CHECK_GL_ERROR(); } - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); (void)CHECK_GL_ERROR(); _gpuObject._renderPages._flags &= ~PageManager::DIRTY; } + + // REsource BUffer are implemented with TextureBuffer + GLuint _texBuffer { 0 }; + GLuint getTexBufferId() { + if (!_texBuffer) { + glGenTextures(1, &_texBuffer); + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_BUFFER_TEXBUF_TEX_UNIT); + glBindTexture(GL_TEXTURE_BUFFER, _texBuffer); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _buffer); + glBindTexture(GL_TEXTURE_BUFFER, 0); + (void)CHECK_GL_ERROR(); + } + return _texBuffer; + } }; } } @@ -68,14 +90,24 @@ GLuint GLESBackend::getBufferIDUnsynced(const Buffer& buffer) { return GLESBuffer::getIdUnsynced(*this, buffer); } +GLuint GLESBackend::getResourceBufferID(const Buffer& buffer) { + auto* object = GLESBuffer::sync(*this, buffer); + if (object) { + return object->getTexBufferId(); + } else { + return 0; + } +} + GLBuffer* GLESBackend::syncGPUObject(const Buffer& buffer) { return GLESBuffer::sync(*this, buffer); } bool GLESBackend::bindResourceBuffer(uint32_t slot, const BufferPointer& buffer) { - GLBuffer* object = syncGPUObject((*buffer)); - if (object) { - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, object->_id); + GLuint texBuffer = GLESBackend::getResourceBufferID((*buffer)); + if (texBuffer) { + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT + slot); + glBindTexture(GL_TEXTURE_BUFFER, texBuffer); (void)CHECK_GL_ERROR(); @@ -89,9 +121,10 @@ bool GLESBackend::bindResourceBuffer(uint32_t slot, const BufferPointer& buffer) void GLESBackend::releaseResourceBuffer(uint32_t slot) { auto& bufferReference = _resource._buffers[slot]; - if (valid(bufferReference)) { - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, 0); + auto buffer = acquire(bufferReference); + if (buffer) { + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT + slot); + glBindTexture(GL_TEXTURE_BUFFER, 0); reset(bufferReference); } } - diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp index ee8408c533..dded307249 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp @@ -12,15 +12,3 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gles; -// GLSL version -std::string GLESBackend::getBackendShaderHeader() const { - static const std::string header( - R"SHADER(#version 310 es - #extension GL_EXT_texture_buffer : enable - precision highp float; - precision highp samplerBuffer; - precision highp sampler2DShadow; - #define BITFIELD highp int - )SHADER"); - return header; -} diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp index 661eb0de99..7e1ee0da3b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp @@ -60,12 +60,11 @@ void GLESBackend::transferTransformState(const Batch& batch) const { glBindBuffer(GL_ARRAY_BUFFER, 0); } - glActiveTexture(GL_TEXTURE0 + GLESBackend::TRANSFORM_OBJECT_SLOT); + glActiveTexture(GL_TEXTURE0 + slot::texture::ObjectTransforms); glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture); if (!batch._objects.empty()) { glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _transform._objectBuffer); } - CHECK_GL_ERROR(); // Make sure the current Camera offset is unknown before render Draw diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 018ca8f02f..e3ea210ecb 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -426,6 +426,13 @@ void Batch::generateTextureMips(const TexturePointer& texture) { _params.emplace_back(_textures.cache(texture)); } +void Batch::generateTextureMipsWithPipeline(const TexturePointer& texture, int numMips) { + ADD_COMMAND(generateTextureMipsWithPipeline); + + _params.emplace_back(_textures.cache(texture)); + _params.emplace_back(numMips); +} + void Batch::beginQuery(const QueryPointer& query) { ADD_COMMAND(beginQuery); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 0f9c2f554b..96a45d3111 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -226,6 +226,8 @@ public: // Generate the mips for a texture void generateTextureMips(const TexturePointer& texture); + // Generate the mips for a texture using the current pipeline + void generateTextureMipsWithPipeline(const TexturePointer& destTexture, int numMips = -1); // Query Section void beginQuery(const QueryPointer& query); @@ -326,6 +328,7 @@ public: COMMAND_clearFramebuffer, COMMAND_blit, COMMAND_generateTextureMips, + COMMAND_generateTextureMipsWithPipeline, COMMAND_advance, diff --git a/libraries/gpu/src/gpu/DrawColor.slf b/libraries/gpu/src/gpu/DrawColor.slf index fdea26fa68..3d5b569662 100644 --- a/libraries/gpu/src/gpu/DrawColor.slf +++ b/libraries/gpu/src/gpu/DrawColor.slf @@ -17,7 +17,7 @@ struct DrawColorParams { vec4 color; }; -layout(binding=0) uniform drawColorParamsBuffer { +LAYOUT(binding=0) uniform drawColorParamsBuffer { DrawColorParams params; }; diff --git a/libraries/gpu/src/gpu/DrawColoredTexture.slf b/libraries/gpu/src/gpu/DrawColoredTexture.slf index 0fe3707b1c..a4f03f925d 100755 --- a/libraries/gpu/src/gpu/DrawColoredTexture.slf +++ b/libraries/gpu/src/gpu/DrawColoredTexture.slf @@ -13,13 +13,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; struct DrawColorParams { vec4 color; }; -layout(binding=0) uniform drawColorParams { +LAYOUT(binding=0) uniform drawColorParams { DrawColorParams params; }; diff --git a/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv b/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv index 8849cb494a..a59180ec31 100755 --- a/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv +++ b/libraries/gpu/src/gpu/DrawTexcoordRectTransformUnitQuad.slv @@ -25,7 +25,7 @@ struct TexCoordRectParams { vec4 texcoordRect; }; -layout(binding=0) uniform texcoordRectBuffer { +LAYOUT(binding=0) uniform texcoordRectBuffer { TexCoordRectParams params; }; diff --git a/libraries/gpu/src/gpu/DrawTexture.slf b/libraries/gpu/src/gpu/DrawTexture.slf index 4298729b8b..f8f06eb6ca 100755 --- a/libraries/gpu/src/gpu/DrawTexture.slf +++ b/libraries/gpu/src/gpu/DrawTexture.slf @@ -14,7 +14,7 @@ // -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; diff --git a/libraries/gpu/src/gpu/drawTexture.slp b/libraries/gpu/src/gpu/DrawTexture.slp similarity index 57% rename from libraries/gpu/src/gpu/drawTexture.slp rename to libraries/gpu/src/gpu/DrawTexture.slp index e04be84618..f922364b75 100644 --- a/libraries/gpu/src/gpu/drawTexture.slp +++ b/libraries/gpu/src/gpu/DrawTexture.slp @@ -1,2 +1 @@ VERTEX DrawUnitQuadTexcoord -FRAGMENT DrawTexture diff --git a/libraries/gpu/src/gpu/DrawTextureMirroredX.slf b/libraries/gpu/src/gpu/DrawTextureMirroredX.slf index ab6333f08d..abb52cbe7f 100644 --- a/libraries/gpu/src/gpu/DrawTextureMirroredX.slf +++ b/libraries/gpu/src/gpu/DrawTextureMirroredX.slf @@ -14,7 +14,7 @@ // -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; diff --git a/libraries/gpu/src/gpu/DrawTextureMirroredX.slp b/libraries/gpu/src/gpu/DrawTextureMirroredX.slp new file mode 100644 index 0000000000..db9a4a4fac --- /dev/null +++ b/libraries/gpu/src/gpu/DrawTextureMirroredX.slp @@ -0,0 +1 @@ +VERTEX DrawUnitQuadTexcoord \ No newline at end of file diff --git a/libraries/gpu/src/gpu/DrawTextureOpaque.slf b/libraries/gpu/src/gpu/DrawTextureOpaque.slf index b3227325bf..e23529e851 100755 --- a/libraries/gpu/src/gpu/DrawTextureOpaque.slf +++ b/libraries/gpu/src/gpu/DrawTextureOpaque.slf @@ -16,7 +16,7 @@ <@include gpu/ShaderConstants.h@> -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; diff --git a/libraries/gpu/src/gpu/DrawTransformedTexture.slp b/libraries/gpu/src/gpu/DrawTransformedTexture.slp new file mode 100644 index 0000000000..daeafe6012 --- /dev/null +++ b/libraries/gpu/src/gpu/DrawTransformedTexture.slp @@ -0,0 +1,2 @@ +VERTEX DrawTransformUnitQuad +FRAGMENT DrawTexture diff --git a/libraries/gpu/src/gpu/MipGeneration.slh b/libraries/gpu/src/gpu/MipGeneration.slh new file mode 100644 index 0000000000..b5d4ab3303 --- /dev/null +++ b/libraries/gpu/src/gpu/MipGeneration.slh @@ -0,0 +1,20 @@ + +<@if not MIP_GENERATION_SLH@> +<@def MIP_GENERATION_SLH@> + +<@include gpu/ShaderConstants.h@> + +LAYOUT(binding=GPU_TEXTURE_MIP_CREATION_INPUT) uniform sampler2D texMap; + +in vec2 varTexCoord0; + +<@endif@> \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 0191d9d4f1..d4236ac66c 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -10,44 +10,49 @@ // #include "Shader.h" -#include -#include -#include - -#include -#include - -#include #include "Context.h" using namespace gpu; -std::atomic Shader::_nextShaderID( 1 ); -Shader::DomainShaderMaps Shader::_domainShaderMaps; Shader::ProgramMap Shader::_programMap; -Shader::Shader(Type type, const Source& source) : - _source(source), - _type(type), - _ID(_nextShaderID++) +Shader::Shader(Type type, const Source& source, bool dynamic) : + _type(type) { + auto& thisSource = const_cast(_source); + thisSource = source; + if (!dynamic) { + thisSource.id = source.id; + } } -Shader::Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel): - _type(type), - _ID(_nextShaderID++) +Shader::Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel) : + _type(type) { + + auto& shaders = const_cast(_shaders); if (geometry) { - _shaders.resize(3); - _shaders[VERTEX] = vertex; - _shaders[GEOMETRY] = geometry; - _shaders[PIXEL] = pixel; + shaders.resize(3); + shaders[VERTEX] = vertex; + shaders[GEOMETRY] = geometry; + shaders[PIXEL] = pixel; } else { - _shaders.resize(2); - _shaders[VERTEX] = vertex; - _shaders[PIXEL] = pixel; + shaders.resize(2); + shaders[VERTEX] = vertex; + shaders[PIXEL] = pixel; + } + + auto& reflection = const_cast(getReflection()); + for (const auto& subShader : _shaders) { + reflection.merge(subShader->getReflection()); + } + if (_shaders[VERTEX]) { + reflection.inputs = _shaders[VERTEX]->getReflection().inputs; + } + if (_shaders[PIXEL]) { + reflection.outputs = _shaders[PIXEL]->getReflection().outputs; } } @@ -55,46 +60,27 @@ Shader::~Shader() { } -void populateSlotSet(Shader::SlotSet& slotSet, const Shader::LocationMap& map) { - for (const auto& entry : map) { - const auto& name = entry.first; - const auto& location = entry.second; - slotSet.insert({ name, location, Element() }); - } +static std::unordered_map> _shaderCache; + +Shader::ID Shader::getID() const { + if (isProgram()) { + return (_shaders[VERTEX]->getID() << 16) | (_shaders[PIXEL]->getID()); + } + + return _source.id; } -Shader::Pointer Shader::createOrReuseDomainShader(Type type, const Source& source) { - auto found = _domainShaderMaps[type].find(source); - if (found != _domainShaderMaps[type].end()) { +Shader::Pointer Shader::createOrReuseDomainShader(Type type, uint32_t sourceId) { + // Don't attempt to cache non-static shaders + auto found = _shaderCache.find(sourceId); + if (found != _shaderCache.end()) { auto sharedShader = (*found).second.lock(); if (sharedShader) { return sharedShader; } } - auto shader = Pointer(new Shader(type, source)); - const auto& reflection = source.getReflection(); - if (0 != reflection.count(BindingType::INPUT)) { - populateSlotSet(shader->_inputs, reflection.find(BindingType::INPUT)->second); - } - if (0 != reflection.count(BindingType::OUTPUT)) { - populateSlotSet(shader->_outputs, reflection.find(BindingType::OUTPUT)->second); - } - if (0 != reflection.count(BindingType::UNIFORM_BUFFER)) { - populateSlotSet(shader->_uniformBuffers, reflection.find(BindingType::UNIFORM_BUFFER)->second); - } - if (0 != reflection.count(BindingType::RESOURCE_BUFFER)) { - populateSlotSet(shader->_resourceBuffers, reflection.find(BindingType::RESOURCE_BUFFER)->second); - } - if (0 != reflection.count(BindingType::TEXTURE)) { - populateSlotSet(shader->_textures, reflection.find(BindingType::TEXTURE)->second); - } - if (0 != reflection.count(BindingType::SAMPLER)) { - populateSlotSet(shader->_samplers, reflection.find(BindingType::SAMPLER)->second); - } - if (0 != reflection.count(BindingType::UNIFORM)) { - populateSlotSet(shader->_uniforms, reflection.find(BindingType::UNIFORM)->second); - } - _domainShaderMaps[type].emplace(source, std::weak_ptr(shader)); + auto shader = Pointer(new Shader(type, getShaderSource(sourceId), false)); + _shaderCache.insert({ sourceId, shader }); return shader; } @@ -137,28 +123,6 @@ ShaderPointer Shader::createOrReuseProgramShader(Type type, const Pointer& verte // Program is a new one, let's create it auto program = Pointer(new Shader(type, vertexShader, geometryShader, pixelShader)); - - // Combine the slots from the sub-shaders - for (const auto& shader : program->_shaders) { - const auto& reflection = shader->_source.getReflection(); - if (0 != reflection.count(BindingType::UNIFORM_BUFFER)) { - populateSlotSet(program->_uniformBuffers, reflection.find(BindingType::UNIFORM_BUFFER)->second); - } - if (0 != reflection.count(BindingType::RESOURCE_BUFFER)) { - populateSlotSet(program->_resourceBuffers, reflection.find(BindingType::RESOURCE_BUFFER)->second); - } - if (0 != reflection.count(BindingType::TEXTURE)) { - populateSlotSet(program->_textures, reflection.find(BindingType::TEXTURE)->second); - } - if (0 != reflection.count(BindingType::SAMPLER)) { - populateSlotSet(program->_samplers, reflection.find(BindingType::SAMPLER)->second); - } - if (0 != reflection.count(BindingType::UNIFORM)) { - populateSlotSet(program->_uniforms, reflection.find(BindingType::UNIFORM)->second); - } - - } - _programMap.emplace(key, std::weak_ptr(program)); return program; } @@ -175,24 +139,21 @@ void Shader::incrementCompilationAttempt() const { } Shader::Pointer Shader::createVertex(const Source& source) { - return createOrReuseDomainShader(VERTEX, source); + return Pointer(new Shader(VERTEX, source, true)); } Shader::Pointer Shader::createPixel(const Source& source) { - return createOrReuseDomainShader(FRAGMENT, source); + return Pointer(new Shader(FRAGMENT, source, true)); } Shader::Pointer Shader::createVertex(uint32_t id) { - return createVertex(getShaderSource(id)); + return createOrReuseDomainShader(VERTEX, id); } Shader::Pointer Shader::createPixel(uint32_t id) { - return createPixel(getShaderSource(id)); + return createOrReuseDomainShader(FRAGMENT, id); } -Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { - return createOrReuseProgramShader(PROGRAM, vertexShader, nullptr, pixelShader); -} Shader::Pointer Shader::createProgram(uint32_t programId) { auto vertexShader = createVertex(shader::getVertexId(programId)); @@ -200,98 +161,15 @@ Shader::Pointer Shader::createProgram(uint32_t programId) { return createOrReuseProgramShader(PROGRAM, vertexShader, nullptr, fragmentShader); } +Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { + return Pointer(new Shader(PROGRAM, vertexShader, nullptr, pixelShader)); +} + +// Dynamic program, bypass caching Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader) { - return createOrReuseProgramShader(PROGRAM, vertexShader, geometryShader, pixelShader); + return Pointer(new Shader(PROGRAM, vertexShader, geometryShader, pixelShader)); } -static const std::string IGNORED_BINDING = "transformObjectBuffer"; - -void updateBindingsFromJsonObject(Shader::LocationMap& inOutSet, const QJsonObject& json) { - for (const auto& key : json.keys()) { - auto keyStr = key.toStdString(); - if (IGNORED_BINDING == keyStr) { - continue; - } - inOutSet[keyStr] = json[key].toInt(); - } -} - -void updateTextureAndResourceBuffersFromJsonObjects(Shader::LocationMap& inOutTextures, Shader::LocationMap& inOutResourceBuffers, - const QJsonObject& json, const QJsonObject& types) { - static const std::string RESOURCE_BUFFER_TEXTURE_TYPE = "samplerBuffer"; - for (const auto& key : json.keys()) { - auto keyStr = key.toStdString(); - if (keyStr == IGNORED_BINDING) { - continue; - } - auto location = json[key].toInt(); - auto type = types[key].toString().toStdString(); - if (type == RESOURCE_BUFFER_TEXTURE_TYPE) { - inOutResourceBuffers[keyStr] = location; - } else { - inOutTextures[key.toStdString()] = location; - } - } -} - -Shader::ReflectionMap getShaderReflection(const std::string& reflectionJson) { - if (reflectionJson.empty() && reflectionJson != std::string("null")) { - return {}; - } - -#define REFLECT_KEY_INPUTS "inputs" -#define REFLECT_KEY_OUTPUTS "outputs" -#define REFLECT_KEY_UBOS "uniformBuffers" -#define REFLECT_KEY_SSBOS "storageBuffers" -#define REFLECT_KEY_UNIFORMS "uniforms" -#define REFLECT_KEY_TEXTURES "textures" -#define REFLECT_KEY_TEXTURE_TYPES "textureTypes" - - auto doc = QJsonDocument::fromJson(reflectionJson.c_str()); - if (doc.isNull()) { - qWarning() << "Invalid shader reflection JSON" << reflectionJson.c_str(); - return {}; - } - - Shader::ReflectionMap result; - auto json = doc.object(); - if (json.contains(REFLECT_KEY_INPUTS)) { - updateBindingsFromJsonObject(result[Shader::BindingType::INPUT], json[REFLECT_KEY_INPUTS].toObject()); - } - if (json.contains(REFLECT_KEY_OUTPUTS)) { - updateBindingsFromJsonObject(result[Shader::BindingType::OUTPUT], json[REFLECT_KEY_OUTPUTS].toObject()); - } - // FIXME eliminate the last of the uniforms - if (json.contains(REFLECT_KEY_UNIFORMS)) { - updateBindingsFromJsonObject(result[Shader::BindingType::UNIFORM], json[REFLECT_KEY_UNIFORMS].toObject()); - } - if (json.contains(REFLECT_KEY_UBOS)) { - updateBindingsFromJsonObject(result[Shader::BindingType::UNIFORM_BUFFER], json[REFLECT_KEY_UBOS].toObject()); - } - - // SSBOs need to come BEFORE the textures. In GL 4.5 the reflection slots aren't really used, but in 4.1 the slots - // are used to explicitly setup bindings after shader linkage, so we want the resource buffer slots to contain the - // texture locations, not the SSBO locations - if (json.contains(REFLECT_KEY_SSBOS)) { - updateBindingsFromJsonObject(result[Shader::BindingType::RESOURCE_BUFFER], json[REFLECT_KEY_SSBOS].toObject()); - } - - // samplerBuffer textures map to gpu ResourceBuffer, while all other textures map to regular gpu Texture - if (json.contains(REFLECT_KEY_TEXTURES)) { - updateTextureAndResourceBuffersFromJsonObjects( - result[Shader::BindingType::TEXTURE], - result[Shader::BindingType::RESOURCE_BUFFER], - json[REFLECT_KEY_TEXTURES].toObject(), - json[REFLECT_KEY_TEXTURE_TYPES].toObject()); - } - - - return result; -} - -Shader::Source Shader::getShaderSource(uint32_t id) { - auto source = shader::loadShaderSource(id); - auto reflectionJson = shader::loadShaderReflection(id); - auto reflection = getShaderReflection(reflectionJson); - return { source, reflection }; +const Shader::Source& Shader::getShaderSource(uint32_t id) { + return shader::Source::get(id); } diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index ad828a0cff..35426bed20 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace gpu { @@ -42,58 +43,10 @@ public: typedef std::shared_ptr Pointer; typedef std::vector Shaders; - // Needs to match values in shaders/Shaders.h - enum class BindingType - { - INVALID = -1, - INPUT = 0, - OUTPUT, - TEXTURE, - SAMPLER, - UNIFORM_BUFFER, - RESOURCE_BUFFER, - UNIFORM, - }; - - using LocationMap = std::unordered_map; - using ReflectionMap = std::map; - - class Source { - public: - enum Language - { - INVALID = -1, - GLSL = 0, - SPIRV = 1, - MSL = 2, - HLSL = 3, - }; - - Source() {} - Source(const std::string& code, const ReflectionMap& reflection, Language lang = GLSL) : - _code(code), _reflection(reflection), _lang(lang) {} - Source(const Source& source) : _code(source._code), _reflection(source._reflection), _lang(source._lang) {} - virtual ~Source() {} - - virtual const std::string& getCode() const { return _code; } - virtual const ReflectionMap& getReflection() const { return _reflection; } - - class Less { - public: - bool operator()(const Source& x, const Source& y) const { - if (x._lang == y._lang) { - return x._code < y._code; - } else { - return (x._lang < y._lang); - } - } - }; - - protected: - std::string _code; - ReflectionMap _reflection; - Language _lang; - }; + using Source = shader::Source; + using Reflection = shader::Reflection; + using Dialect = shader::Dialect; + using Variant = shader::Variant; struct CompilationLog { std::string message; @@ -112,85 +65,13 @@ public: bool operator()(const T& x, const T& y) const { return x._name < y._name; } }; - class Slot { - public: - std::string _name; - int32 _location{ INVALID_LOCATION }; - Element _element; - uint16 _resourceType{ Resource::BUFFER }; - uint32 _size{ 0 }; - - Slot(const Slot& s) : - _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {} - Slot(Slot&& s) : - _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {} - Slot(const std::string& name, - int32 location, - const Element& element, - uint16 resourceType = Resource::BUFFER, - uint32 size = 0) : - _name(name), - _location(location), _element(element), _resourceType(resourceType), _size(size) {} - Slot(const std::string& name) : _name(name) {} - - Slot& operator=(const Slot& s) { - _name = s._name; - _location = s._location; - _element = s._element; - _resourceType = s._resourceType; - _size = s._size; - return (*this); - } - }; - - class SlotSet : protected std::set> { - using Parent = std::set>; - - public: - void insert(const Parent::value_type& value) { - Parent::insert(value); - if (value._location != INVALID_LOCATION) { - _validSlots.insert(value._location); - } - } - - using Parent::begin; - using Parent::empty; - using Parent::end; - using Parent::size; - - using LocationMap = std::unordered_map; - using NameVector = std::vector; - - NameVector getNames() const { - NameVector result; - for (const auto& entry : *this) { - result.push_back(entry._name); - } - return result; - } - - LocationMap getLocationsByName() const { - LocationMap result; - for (const auto& entry : *this) { - result.insert({ entry._name, entry._location }); - } - return result; - } - - bool isValid(int32 slot) const { return 0 != _validSlots.count(slot); } - - protected: - std::unordered_set _validSlots; - }; - - static Source getShaderSource(uint32_t id); - static Source getVertexShaderSource(uint32_t id) { return getShaderSource(id); } - static Source getFragmentShaderSource(uint32_t id) { return getShaderSource(id); } - + static const Source& getShaderSource(uint32_t id); + static const Source& getVertexShaderSource(uint32_t id) { return getShaderSource(id); } + static const Source& getFragmentShaderSource(uint32_t id) { return getShaderSource(id); } static Pointer createVertex(const Source& source); static Pointer createPixel(const Source& source); static Pointer createGeometry(const Source& source); + static Pointer createVertex(uint32_t shaderId); static Pointer createPixel(uint32_t shaderId); static Pointer createGeometry(uint32_t shaderId); @@ -201,8 +82,7 @@ public: ~Shader(); - ID getID() const { return _ID; } - + ID getID() const; Type getType() const { return _type; } bool isProgram() const { return getType() > NUM_DOMAINS; } bool isDomain() const { return getType() < NUM_DOMAINS; } @@ -211,17 +91,12 @@ public: const Shaders& getShaders() const { return _shaders; } - // Access the exposed uniform, input and output slot - const SlotSet& getUniforms() const { return _uniforms; } - const SlotSet& getUniformBuffers() const { return _uniformBuffers; } - const SlotSet& getResourceBuffers() const { return _resourceBuffers; } - const SlotSet& getTextures() const { return _textures; } - const SlotSet& getSamplers() const { return _samplers; } + const Reflection& getReflection() const { return _source.reflection; } // Compilation Handler can be passed while compiling a shader (in the makeProgram call) to be able to give the hand to - // the caller thread if the comilation fails and to prvide a different version of the source for it + // the caller thread if the compilation fails and to provide a different version of the source for it // @param0 the Shader object that just failed to compile - // @param1 the original source code as submited to the compiler + // @param1 the original source code as submitted to the compiler // @param2 the compilation log containing the error message // @param3 a new string ready to be filled with the new version of the source that could be proposed from the handler functor // @return boolean true if the backend should keep trying to compile the shader with the new source returned or false to stop and fail that shader compilation @@ -240,32 +115,21 @@ public: const GPUObjectPointer gpuObject{}; protected: - Shader(Type type, const Source& source); + Shader(Type type, const Source& source, bool dynamic); Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel); Shader(const Shader& shader); // deep copy of the sysmem shader Shader& operator=(const Shader& shader); // deep copy of the sysmem texture // Source contains the actual source code or nothing if the shader is a program - Source _source; + const Source _source; // if shader is composed of sub shaders, here they are - Shaders _shaders; - - // List of exposed uniform, input and output slots - SlotSet _uniforms; - SlotSet _uniformBuffers; - SlotSet _resourceBuffers; - SlotSet _textures; - SlotSet _samplers; - SlotSet _inputs; - SlotSet _outputs; + const Shaders _shaders; + // The type of the shader, the master key - Type _type; - - // The unique identifier of a shader in the GPU lib - uint32_t _ID{ 0 }; + const Type _type; // Number of attempts to compile the shader mutable uint32_t _numCompilationAttempts{ 0 }; @@ -277,13 +141,9 @@ protected: // Global maps of the shaders // Unique shader ID - static std::atomic _nextShaderID; + //static std::atomic _nextShaderID; - using ShaderMap = std::map, Source::Less>; - using DomainShaderMaps = std::array; - static DomainShaderMaps _domainShaderMaps; - - static ShaderPointer createOrReuseDomainShader(Type type, const Source& source); + static ShaderPointer createOrReuseDomainShader(Type type, uint32_t sourceId); using ProgramMapKey = glm::uvec3; // The IDs of the shaders in a program make its key class ProgramKeyLess { diff --git a/libraries/gpu/src/gpu/ShaderConstants.h b/libraries/gpu/src/gpu/ShaderConstants.h index 13dfd1be9c..1a37c69784 100644 --- a/libraries/gpu/src/gpu/ShaderConstants.h +++ b/libraries/gpu/src/gpu/ShaderConstants.h @@ -21,6 +21,15 @@ #define GPU_TEXTURE_TRANSFORM_OBJECT 31 + +#define GPU_RESOURCE_BUFFER_SLOT0_TEXTURE 35 +#define GPU_RESOURCE_BUFFER_SLOT1_TEXTURE 36 +#define GPU_RESOURCE_BUFFER_SLOT0_STORAGE 0 +#define GPU_RESOURCE_BUFFER_SLOT1_STORAGE 1 + +// Mip creation +#define GPU_TEXTURE_MIP_CREATION_INPUT 30 + #define GPU_STORAGE_TRANSFORM_OBJECT 7 #define GPU_ATTR_POSITION 0 @@ -67,7 +76,8 @@ enum Buffer { namespace texture { enum Texture { ObjectTransforms = GPU_TEXTURE_TRANSFORM_OBJECT, -}; + MipCreationInput = GPU_TEXTURE_MIP_CREATION_INPUT, +}; } // namespace texture namespace storage { @@ -94,21 +104,6 @@ enum Attribute { }; } // namespace attr -namespace uniform { -enum Uniform { - Extra0 = GPU_UNIFORM_EXTRA0, - Extra1 = GPU_UNIFORM_EXTRA1, - Extra2 = GPU_UNIFORM_EXTRA2, - Extra3 = GPU_UNIFORM_EXTRA3, - Extra4 = GPU_UNIFORM_EXTRA4, - Extra5 = GPU_UNIFORM_EXTRA5, - Extra6 = GPU_UNIFORM_EXTRA6, - Extra7 = GPU_UNIFORM_EXTRA7, - Extra8 = GPU_UNIFORM_EXTRA8, - Extra9 = GPU_UNIFORM_EXTRA9, -}; -} // namespace uniform - } } // namespace gpu::slot // !> diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h index 9bcd69dcdb..e68e829fb5 100755 --- a/libraries/gpu/src/gpu/State.h +++ b/libraries/gpu/src/gpu/State.h @@ -118,11 +118,13 @@ public: uint8 _function = LESS; uint8 _writeMask = true; uint8 _enabled = false; - uint8 _spare = 0; // _spare is here to to affect alignment +#if defined(__clang__) + __attribute__((unused)) +#endif + uint8 _spare = 0; // Padding public: DepthTest(bool enabled = false, bool writeMask = true, ComparisonFunction func = LESS) : _function(func), _writeMask(writeMask), _enabled(enabled) { - (void)_spare; // to avoid unusued variable warning } bool isEnabled() const { return _enabled != 0; } diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index fd2cb86177..43205ba4c2 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -16,7 +16,7 @@ #define TransformCamera _TransformCamera -layout(std140, binding=GPU_BUFFER_TRANSFORM_CAMERA) uniform transformCameraBuffer { +LAYOUT_STD140(binding=GPU_BUFFER_TRANSFORM_CAMERA) uniform transformCameraBuffer { #ifdef GPU_TRANSFORM_IS_STEREO #ifdef GPU_TRANSFORM_STEREO_CAMERA TransformCamera _camera[2]; @@ -26,7 +26,7 @@ layout(std140, binding=GPU_BUFFER_TRANSFORM_CAMERA) uniform transformCameraBuffe #else TransformCamera _camera; #endif -}; +} _cameraBlock; #ifdef GPU_VERTEX_SHADER #ifdef GPU_TRANSFORM_IS_STEREO @@ -76,12 +76,12 @@ TransformCamera getTransformCamera() { _stereoSide = gl_InstanceID % 2; #endif #endif - return _camera[_stereoSide]; + return _cameraBlock._camera[_stereoSide]; #else - return _camera; + return _cameraBlock._camera; #endif #else - return _camera; + return _cameraBlock._camera; #endif } @@ -93,7 +93,7 @@ bool cam_isStereo() { #ifdef GPU_TRANSFORM_IS_STEREO return getTransformCamera()._stereoInfo.x > 0.0; #else - return _camera._stereoInfo.x > 0.0; + return _cameraBlock._camera._stereoInfo.x > 0.0; #endif } @@ -102,10 +102,10 @@ float cam_getStereoSide() { #ifdef GPU_TRANSFORM_STEREO_CAMERA return getTransformCamera()._stereoInfo.y; #else - return _camera._stereoInfo.y; + return _cameraBlock._camera._stereoInfo.y; #endif #else - return _camera._stereoInfo.y; + return _cameraBlock._camera._stereoInfo.y; #endif } @@ -120,7 +120,7 @@ struct TransformObject { layout(location=GPU_ATTR_DRAW_CALL_INFO) in ivec2 _drawCallInfo; #if defined(GPU_SSBO_TRANSFORM_OBJECT) -layout(std140, binding=GPU_STORAGE_TRANSFORM_OBJECT) buffer transformObjectBuffer { +LAYOUT_STD140(binding=GPU_STORAGE_TRANSFORM_OBJECT) buffer transformObjectBuffer { TransformObject _object[]; }; TransformObject getTransformObject() { @@ -128,7 +128,7 @@ TransformObject getTransformObject() { return transformObject; } #else -layout(binding=GPU_TEXTURE_TRANSFORM_OBJECT) uniform samplerBuffer transformObjectBuffer; +LAYOUT(binding=GPU_TEXTURE_TRANSFORM_OBJECT) uniform samplerBuffer transformObjectBuffer; TransformObject getTransformObject() { int offset = 8 * _drawCallInfo.x; @@ -167,7 +167,9 @@ TransformObject getTransformObject() { vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1)); vec2 eyeOffsetScale = vec2(-0.5, +0.5); uint eyeIndex = uint(_stereoSide); +#ifndef GPU_GLES gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); +#endif float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; <$clipPos$>.x = newClipPosX; #endif diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index f2afc1e7d4..67f79db6b7 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -367,8 +367,8 @@ namespace scriptable { obj.setProperty("metallic", material.metallic); obj.setProperty("scattering", material.scattering); obj.setProperty("unlit", material.unlit); - obj.setProperty("emissive", vec3toScriptValue(engine, material.emissive)); - obj.setProperty("albedo", vec3toScriptValue(engine, material.albedo)); + obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive)); + obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); obj.setProperty("emissiveMap", material.emissiveMap); obj.setProperty("albedoMap", material.albedoMap); obj.setProperty("opacityMap", material.opacityMap); diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index 026e7b53a3..8a48c17007 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -10,7 +10,6 @@ #include #include #include -#include #include "GpuHelpers.h" #include "GLMHelpers.h" diff --git a/libraries/graphics/src/graphics/Light.slh b/libraries/graphics/src/graphics/Light.slh index d22da44c66..c00bfea6a2 100644 --- a/libraries/graphics/src/graphics/Light.slh +++ b/libraries/graphics/src/graphics/Light.slh @@ -51,7 +51,7 @@ float getLightAmbientMapNumMips(LightAmbient l) { return l._ambient.y; } <@if N@> -layout(binding=GRAPHICS_BUFFER_LIGHT) uniform lightBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_LIGHT) uniform lightBuffer { Light lightArray[<$N$>]; }; Light getLight(int index) { @@ -59,7 +59,7 @@ Light getLight(int index) { } <@else@> -layout(binding=GRAPHICS_BUFFER_KEY_LIGHT) uniform keyLightBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_KEY_LIGHT) uniform keyLightBuffer { Light light; }; Light getKeyLight() { @@ -79,7 +79,7 @@ Light getKeyLight() { <@if N@> -layout(binding=GRAPHICS_BUFFER_AMBIENT_LIGHT) uniform lightAmbientBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_AMBIENT_LIGHT) uniform lightAmbientBuffer { LightAmbient lightAmbientArray[<$N$>]; }; @@ -88,7 +88,7 @@ LightAmbient getLightAmbient(int index) { } <@else@> -layout(binding=GRAPHICS_BUFFER_AMBIENT_LIGHT) uniform lightAmbientBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_AMBIENT_LIGHT) uniform lightAmbientBuffer { LightAmbient lightAmbient; }; diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 63481e0f5e..0db78ab349 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -320,7 +320,10 @@ public: float _scattering{ 0.0f }; // Scattering info - glm::vec2 _spare{ 0.0f }; +#if defined(__clang__) + __attribute__((unused)) +#endif + glm::vec2 _spare{ 0.0f }; // Padding uint32_t _key{ 0 }; // a copy of the materialKey diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index ea59059cf1..d2055b9a59 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -48,7 +48,7 @@ struct Material { vec4 _scatteringSpare2Key; }; -layout(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { Material _mat; TexMapArray _texMapArray; }; diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index 0a60feccfc..db329c3852 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -11,6 +11,7 @@ <@if not MODEL_MATERIAL_TEXTURES_SLH@> <@def MODEL_MATERIAL_TEXTURES_SLH@> +<@include graphics/ShaderConstants.h@> <@include graphics/Material.slh@> <@func declareMaterialTextures(withAlbedo, withRoughness, withNormal, withMetallic, withEmissive, withOcclusion, withScattering)@> @@ -91,21 +92,21 @@ float fetchScatteringMap(vec2 uv) { #else <@if withAlbedo@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_ALBEDO) uniform sampler2D albedoMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ALBEDO) uniform sampler2D albedoMap; vec4 fetchAlbedoMap(vec2 uv) { return texture(albedoMap, uv, TAA_TEXTURE_LOD_BIAS); } <@endif@> <@if withRoughness@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS) uniform sampler2D roughnessMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_ROUGHNESS) uniform sampler2D roughnessMap; float fetchRoughnessMap(vec2 uv) { return (texture(roughnessMap, uv, TAA_TEXTURE_LOD_BIAS).r); } <@endif@> <@if withNormal@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_NORMAL) uniform sampler2D normalMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_NORMAL) uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out vec2 t = 2.0 * (texture(normalMap, uv, TAA_TEXTURE_LOD_BIAS).rg - vec2(0.5, 0.5)); @@ -115,28 +116,28 @@ vec3 fetchNormalMap(vec2 uv) { <@endif@> <@if withMetallic@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_METALLIC) uniform sampler2D metallicMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_METALLIC) uniform sampler2D metallicMap; float fetchMetallicMap(vec2 uv) { return (texture(metallicMap, uv, TAA_TEXTURE_LOD_BIAS).r); } <@endif@> <@if withEmissive@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_EMISSIVE_LIGHTMAP) uniform sampler2D emissiveMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_EMISSIVE_LIGHTMAP) uniform sampler2D emissiveMap; vec3 fetchEmissiveMap(vec2 uv) { return texture(emissiveMap, uv, TAA_TEXTURE_LOD_BIAS).rgb; } <@endif@> <@if withOcclusion@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_OCCLUSION) uniform sampler2D occlusionMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_OCCLUSION) uniform sampler2D occlusionMap; float fetchOcclusionMap(vec2 uv) { return texture(occlusionMap, uv).r; } <@endif@> <@if withScattering@> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_SCATTERING) uniform sampler2D scatteringMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_SCATTERING) uniform sampler2D scatteringMap; float fetchScatteringMap(vec2 uv) { float scattering = texture(scatteringMap, uv, TAA_TEXTURE_LOD_BIAS).r; // boolean scattering for now return max(((scattering - 0.1) / 0.9), 0.0); @@ -185,7 +186,7 @@ float fetchScatteringMap(vec2 uv) { <$declareMaterialTexMapArrayBuffer()$> -layout(binding=GRAPHICS_TEXTURE_MATERIAL_EMISSIVE_LIGHTMAP) uniform sampler2D emissiveMap; +LAYOUT(binding=GRAPHICS_TEXTURE_MATERIAL_EMISSIVE_LIGHTMAP) uniform sampler2D emissiveMap; vec3 fetchLightmapMap(vec2 uv) { vec2 lightmapParams = getTexMapArray()._lightmapParams.xy; return (vec3(lightmapParams.x) + lightmapParams.y * texture(emissiveMap, uv).rgb); diff --git a/libraries/graphics/src/graphics/skybox.slf b/libraries/graphics/src/graphics/skybox.slf index 2b81a433f1..b24bf0f583 100755 --- a/libraries/graphics/src/graphics/skybox.slf +++ b/libraries/graphics/src/graphics/skybox.slf @@ -12,13 +12,13 @@ // <@include graphics/ShaderConstants.h@> -layout(binding=GRAPHICS_TEXTURE_SKYBOX) uniform samplerCube cubeMap; +LAYOUT(binding=GRAPHICS_TEXTURE_SKYBOX) uniform samplerCube cubeMap; struct Skybox { vec4 color; }; -layout(binding=GRAPHICS_BUFFER_SKYBOX_PARAMS) uniform skyboxBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_SKYBOX_PARAMS) uniform skyboxBuffer { Skybox skybox; }; diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 1355a24bf4..f450366417 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -32,8 +32,8 @@ using namespace gpu; #include #undef _CRT_SECURE_NO_WARNINGS -#include -#include +#include +#include static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE_GLES(2048); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index ce2bea0853..b94acb8b71 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -96,7 +96,6 @@ protected: class InputDevice : public controller::InputDevice { public: InputDevice() : controller::InputDevice("Keyboard") {} - virtual ~InputDevice() {} private: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index 9a4bc780a6..12181651db 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME model-networking) setup_hifi_library() -link_hifi_libraries(shared networking graphics fbx ktx image gl) +link_hifi_libraries(shared shaders networking graphics fbx ktx image gl) include_hifi_library_headers(gpu) diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index 823602d939..e6e3b0e812 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -113,11 +113,11 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @typedef {object} Material * @property {string} name="" - A name for the material. * @property {string} model="hifi_pbr" - Currently not used. - * @property {Vec3Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Vec3Color} value + * @property {Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value * is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. * @property {number} opacity=1.0 - The opacity, 0.01.0. * @property {boolean} unlit=false - If true, the material is not lit. - * @property {Vec3Color|RGBS} albedo - The albedo color. A {@link Vec3Color} value is treated as sRGB. A {@link RGBS} value can + * @property {Color|RGBS} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can * be either RGB or sRGB. * @property {number} roughness - The roughness, 0.01.0. * @property {number} metallic - The metallicness, 0.01.0. diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 4d6297303b..dd58b0e75e 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -128,7 +128,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { void GeometryMappingResource::onGeometryMappingLoaded(bool success) { if (success && _geometryResource) { - _fbxGeometry = _geometryResource->_fbxGeometry; + _hfmModel = _geometryResource->_hfmModel; _meshParts = _geometryResource->_meshParts; _meshes = _geometryResource->_meshes; _materials = _geometryResource->_materials; @@ -193,38 +193,38 @@ void GeometryReader::run() { _url.path().toLower().endsWith(".obj.gz") || _url.path().toLower().endsWith(".gltf"))) { - FBXGeometry::Pointer fbxGeometry; + HFMModel::Pointer hfmModel; if (_url.path().toLower().endsWith(".fbx")) { - fbxGeometry.reset(readFBX(_data, _mapping, _url.path())); - if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { + hfmModel.reset(readFBX(_data, _mapping, _url.path())); + if (hfmModel->meshes.size() == 0 && hfmModel->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); + hfmModel = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); } else if (_url.path().toLower().endsWith(".obj.gz")) { QByteArray uncompressedData; if (gunzip(_data, uncompressedData)){ - fbxGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); + hfmModel = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); } else { throw QString("failed to decompress .obj.gz"); } } else if (_url.path().toLower().endsWith(".gltf")) { std::shared_ptr glreader = std::make_shared(); - fbxGeometry.reset(glreader->readGLTF(_data, _mapping, _url)); - if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { + hfmModel.reset(glreader->readGLTF(_data, _mapping, _url)); + if (hfmModel->meshes.size() == 0 && hfmModel->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported GLTF version"); } } else { throw QString("unsupported format"); } - // Add scripts to fbxgeometry + // Add scripts to hfmModel if (!_mapping.value(SCRIPT_FIELD).isNull()) { QVariantList scripts = _mapping.values(SCRIPT_FIELD); for (auto &script : scripts) { - fbxGeometry->scripts.push_back(script.toString()); + hfmModel->scripts.push_back(script.toString()); } } @@ -234,7 +234,7 @@ void GeometryReader::run() { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; } else { QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(FBXGeometry::Pointer, fbxGeometry)); + Q_ARG(HFMModel::Pointer, hfmModel)); } } else { throw QString("url is invalid"); @@ -262,7 +262,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; protected: - Q_INVOKABLE void setGeometryDefinition(FBXGeometry::Pointer fbxGeometry); + Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel); private: QVariantHash _mapping; @@ -277,13 +277,13 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_self, _effectiveBaseURL, _mapping, data, _combineParts)); } -void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) { - // Assume ownership of the geometry pointer - _fbxGeometry = fbxGeometry; +void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel) { + // Assume ownership of the HFMModel pointer + _hfmModel = hfmModel; // Copy materials QHash materialIDAtlas; - for (const FBXMaterial& material : _fbxGeometry->materials) { + for (const HFMMaterial& material : _hfmModel->materials) { materialIDAtlas[material.materialID] = _materials.size(); _materials.push_back(std::make_shared(material, _textureBaseUrl)); } @@ -291,11 +291,11 @@ void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxG std::shared_ptr meshes = std::make_shared(); std::shared_ptr parts = std::make_shared(); int meshID = 0; - for (const FBXMesh& mesh : _fbxGeometry->meshes) { + for (const HFMMesh& mesh : _hfmModel->meshes) { // Copy mesh pointers meshes->emplace_back(mesh._mesh); int partID = 0; - for (const FBXMeshPart& part : mesh.parts) { + for (const HFMMeshPart& part : mesh.parts) { // Construct local parts parts->push_back(std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID])); partID++; @@ -371,7 +371,7 @@ const QVariantMap Geometry::getTextures() const { // FIXME: The materials should only be copied when modified, but the Model currently caches the original Geometry::Geometry(const Geometry& geometry) { - _fbxGeometry = geometry._fbxGeometry; + _hfmModel = geometry._hfmModel; _meshes = geometry._meshes; _meshParts = geometry._meshParts; @@ -414,33 +414,13 @@ bool Geometry::areTexturesLoaded() const { if (!_areTexturesLoaded) { for (auto& material : _materials) { // Check if material textures are loaded - bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(), - [](const NetworkMaterial::Textures::value_type& it) { - auto texture = it.texture; - if (!texture) { - return false; - } - // Failed texture downloads need to be considered as 'loaded' - // or the object will never fade in - bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined()); - if (!finished) { - return true; - } - return false; - }); + bool materialMissingTexture = material->isMissingTexture(); if (materialMissingTexture) { return false; } - // If material textures are loaded, check the material translucency - // FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap. - // However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail. - // Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now - const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP]; - if (albedoTexture.texture) { - material->resetOpacityMap(); - } + material->checkResetOpacityMap(); } _areTexturesLoaded = true; @@ -464,8 +444,8 @@ void GeometryResource::deleter() { } void GeometryResource::setTextures() { - if (_fbxGeometry) { - for (const FBXMaterial& material : _fbxGeometry->materials) { + if (_hfmModel) { + for (const HFMMaterial& material : _hfmModel->materials) { _materials.push_back(std::make_shared(material, _textureBaseUrl)); } } @@ -532,7 +512,7 @@ const QString& NetworkMaterial::getTextureName(MapChannel channel) { return NO_TEXTURE; } -QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& texture) { +QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) { if (texture.content.isEmpty()) { // External file: search relative to the baseUrl, in case filename is relative return baseUrl.resolved(QUrl(texture.filename)); @@ -549,29 +529,29 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu } } -graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, +graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel) { if (baseUrl.isEmpty()) { return nullptr; } - const auto url = getTextureUrl(baseUrl, fbxTexture); - const auto texture = DependencyManager::get()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels); - _textures[channel] = Texture { fbxTexture.name, texture }; + const auto url = getTextureUrl(baseUrl, hfmTexture); + const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels); + _textures[channel] = Texture { hfmTexture.name, texture }; auto map = std::make_shared(); if (texture) { map->setTextureSource(texture->_textureSource); } - map->setTextureTransform(fbxTexture.transform); + map->setTextureTransform(hfmTexture.transform); return map; } graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) { auto textureCache = DependencyManager::get(); - if (textureCache) { + if (textureCache && !url.isEmpty()) { auto texture = textureCache->getTexture(url, type); _textures[channel].texture = texture; @@ -644,21 +624,23 @@ void NetworkMaterial::setLightmapMap(const QUrl& url) { } } -NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : +NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) : graphics::Material(*material._material), _textures(MapChannel::NUM_MAP_CHANNELS) { _name = material.name.toStdString(); if (!material.albedoTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); - _albedoTransform = material.albedoTexture.transform; - map->setTextureTransform(_albedoTransform); + if (map) { + _albedoTransform = material.albedoTexture.transform; + map->setTextureTransform(_albedoTransform); - if (!material.opacityTexture.filename.isEmpty()) { - if (material.albedoTexture.filename == material.opacityTexture.filename) { - // Best case scenario, just indicating that the albedo map contains transparency - // TODO: Different albedo/opacity maps are not currently supported - map->setUseAlphaChannel(true); + if (!material.opacityTexture.filename.isEmpty()) { + if (material.albedoTexture.filename == material.opacityTexture.filename) { + // Best case scenario, just indicating that the albedo map contains transparency + // TODO: Different albedo/opacity maps are not currently supported + map->setUseAlphaChannel(true); + } } } @@ -690,7 +672,9 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur if (!material.occlusionTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); - map->setTextureTransform(material.occlusionTexture.transform); + if (map) { + map->setTextureTransform(material.occlusionTexture.transform); + } setTextureMap(MapChannel::OCCLUSION_MAP, map); } @@ -706,10 +690,12 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur if (!material.lightmapTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); - _lightmapTransform = material.lightmapTexture.transform; - _lightmapParams = material.lightmapParams; - map->setTextureTransform(_lightmapTransform); - map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); + if (map) { + _lightmapTransform = material.lightmapTexture.transform; + _lightmapParams = material.lightmapParams; + map->setTextureTransform(_lightmapTransform); + map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); + } setTextureMap(MapChannel::LIGHTMAP_MAP, map); } } @@ -729,9 +715,11 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!albedoName.isEmpty()) { auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl(); auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); - map->setTextureTransform(_albedoTransform); - // when reassigning the albedo texture we also check for the alpha channel used as opacity - map->setUseAlphaChannel(true); + if (map) { + map->setTextureTransform(_albedoTransform); + // when reassigning the albedo texture we also check for the alpha channel used as opacity + map->setUseAlphaChannel(true); + } setTextureMap(MapChannel::ALBEDO_MAP, map); } @@ -777,10 +765,39 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!lightmapName.isEmpty()) { auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl(); auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); - map->setTextureTransform(_lightmapTransform); - map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); + if (map) { + map->setTextureTransform(_lightmapTransform); + map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); + } setTextureMap(MapChannel::LIGHTMAP_MAP, map); } } +bool NetworkMaterial::isMissingTexture() { + for (auto& networkTexture : _textures) { + auto& texture = networkTexture.texture; + if (!texture) { + continue; + } + // Failed texture downloads need to be considered as 'loaded' + // or the object will never fade in + bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined()); + if (!finished) { + return true; + } + } + return false; +} + +void NetworkMaterial::checkResetOpacityMap() { + // If material textures are loaded, check the material translucency + // FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap. + // However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail. + // Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now + const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP]; + if (albedoTexture.texture) { + resetOpacityMap(); + } +} + #include "ModelCache.moc" diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index dfe26788f2..1bb340b83c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -36,7 +36,7 @@ public: Geometry() = default; Geometry(const Geometry& geometry); - virtual ~Geometry() {} + virtual ~Geometry() = default; // Immutable over lifetime using GeometryMeshes = std::vector>; @@ -45,9 +45,9 @@ public: // Mutable, but must retain structure of vector using NetworkMaterials = std::vector>; - bool isGeometryLoaded() const { return (bool)_fbxGeometry; } + bool isHFMModelLoaded() const { return (bool)_hfmModel; } - const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; } + const HFMModel& getHFMModel() const { return *_hfmModel; } const GeometryMeshes& getMeshes() const { return *_meshes; } const std::shared_ptr getShapeMaterial(int shapeID) const; @@ -62,7 +62,7 @@ protected: friend class GeometryMappingResource; // Shared across all geometries, constant throughout lifetime - std::shared_ptr _fbxGeometry; + std::shared_ptr _hfmModel; std::shared_ptr _meshes; std::shared_ptr _meshParts; @@ -94,7 +94,7 @@ protected: // Geometries may not hold onto textures while cached - that is for the texture cache // Instead, these methods clear and reset textures from the geometry when caching/loading - bool shouldSetTextures() const { return _fbxGeometry && _materials.empty(); } + bool shouldSetTextures() const { return _hfmModel && _materials.empty(); } void setTextures(); void resetTextures(); @@ -165,7 +165,7 @@ public: using MapChannel = graphics::Material::MapChannel; NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {} - NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); + NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl); NetworkMaterial(const NetworkMaterial& material); void setAlbedoMap(const QUrl& url, bool useAlphaChannel); @@ -177,6 +177,9 @@ public: void setScatteringMap(const QUrl& url); void setLightmapMap(const QUrl& url); + bool isMissingTexture(); + void checkResetOpacityMap(); + protected: friend class Geometry; @@ -198,8 +201,8 @@ protected: private: // Helpers for the ctors - QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); - graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, + QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); + graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel); graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e8aec5e60e..337ad68a99 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -329,7 +329,7 @@ _maxNumPixels(100) static bool isLocalUrl(const QUrl& url) { auto scheme = url.scheme(); - return (scheme == URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME); + return (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME); } NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : @@ -456,7 +456,8 @@ void NetworkTexture::makeRequest() { // Add a fragment to the base url so we can identify the section of the ktx being requested when debugging // The actual requested url is _activeUrl and will not contain the fragment _url.setFragment("head"); - _ktxHeaderRequest = DependencyManager::get()->createResourceRequest(this, _activeUrl); + _ktxHeaderRequest = DependencyManager::get()->createResourceRequest( + this, _activeUrl, true, -1, "NetworkTexture::makeRequest"); if (!_ktxHeaderRequest) { qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); @@ -502,7 +503,7 @@ void NetworkTexture::handleLocalRequestCompleted() { void NetworkTexture::makeLocalRequest() { const QString scheme = _activeUrl.scheme(); QString path; - if (scheme == URL_SCHEME_FILE) { + if (scheme == HIFI_URL_SCHEME_FILE) { path = PathUtils::expandToLocalDataAbsolutePath(_activeUrl).toLocalFile(); } else { path = ":" + _activeUrl.path(); @@ -617,7 +618,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; - _ktxMipRequest = DependencyManager::get()->createResourceRequest(this, _activeUrl); + _ktxMipRequest = DependencyManager::get()->createResourceRequest( + this, _activeUrl, true, -1, "NetworkTexture::startMipRangeRequest"); if (!_ktxMipRequest) { qCWarning(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c914ad91af..3933e3ae56 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -75,8 +75,6 @@ protected: void makeLocalRequest(); Q_INVOKABLE void handleLocalRequestCompleted(); - virtual bool isCacheable() const override { return _loaded; } - Q_INVOKABLE virtual void downloadFinished(const QByteArray& data) override; bool handleFailedRequest(ResourceRequest::Result result) override; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index d99c0020da..2d1a0c0afb 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -837,3 +837,7 @@ void AccountManager::handleKeypairGenerationError() { // reset our waiting state for keypair response _isWaitingForKeypairResponse = false; } + +void AccountManager::setLimitedCommerce(bool isLimited) { + _limitedCommerce = isLimited; +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index f3b81cf1c9..093ac9a27c 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -98,6 +98,9 @@ public: void removeAccountFromFile(); + bool getLimitedCommerce() { return _limitedCommerce; } + void setLimitedCommerce(bool isLimited); + public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); @@ -121,6 +124,7 @@ signals: void loginFailed(); void logoutComplete(); void newKeypair(); + void limitedCommerceChanged(); private slots: void handleKeypairGenerationError(); @@ -150,6 +154,8 @@ private: QByteArray _pendingPrivateKey; QUuid _sessionID { QUuid::createUuid() }; + + bool _limitedCommerce { false }; }; #endif // hifi_AccountManager_h diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index f8ab8ceaec..e6957728e8 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -155,12 +155,12 @@ void AddressManager::goForward() { void AddressManager::storeCurrentAddress() { auto url = currentAddress(); - if (url.scheme() == URL_SCHEME_FILE || + if (url.scheme() == HIFI_URL_SCHEME_FILE || (url.scheme() == URL_SCHEME_HIFI && !url.host().isEmpty())) { // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can // be loaded over http(s) - // url.scheme() == URL_SCHEME_HTTP || - // url.scheme() == URL_SCHEME_HTTPS || + // url.scheme() == HIFI_URL_SCHEME_HTTP || + // url.scheme() == HIFI_URL_SCHEME_HTTPS || bool isInErrorState = DependencyManager::get()->getDomainHandler().isInErrorState(); if (isConnected()) { if (isInErrorState) { @@ -331,11 +331,11 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { emit lookupResultsFinished(); return true; - } else if (lookupUrl.scheme() == URL_SCHEME_FILE) { + } else if (lookupUrl.scheme() == HIFI_URL_SCHEME_FILE) { // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can // be loaded over http(s) // lookupUrl.scheme() == URL_SCHEME_HTTP || - // lookupUrl.scheme() == URL_SCHEME_HTTPS || + // lookupUrl.scheme() == HIFI_URL_SCHEME_HTTPS || // TODO once a file can return a connection refusal if there were to be some kind of load error, we'd // need to store the previous domain tried in _lastVisitedURL. For now , do not store it. diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 6d5bbb3ac5..6f5b13f98d 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -24,8 +24,12 @@ static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5; -AssetResourceRequest::AssetResourceRequest(const QUrl& url) : - ResourceRequest(url) +AssetResourceRequest::AssetResourceRequest( + const QUrl& url, + const bool isObservable, + const qint64 callerId, + const QString& extra) : + ResourceRequest(url, isObservable, callerId, extra) { _lastProgressDebug = p_high_resolution_clock::now() - std::chrono::seconds(DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS); } diff --git a/libraries/networking/src/AssetResourceRequest.h b/libraries/networking/src/AssetResourceRequest.h index 2fe79040ca..07baca5416 100644 --- a/libraries/networking/src/AssetResourceRequest.h +++ b/libraries/networking/src/AssetResourceRequest.h @@ -22,7 +22,11 @@ class AssetResourceRequest : public ResourceRequest { Q_OBJECT public: - AssetResourceRequest(const QUrl& url); + AssetResourceRequest( + const QUrl& url, + const bool isObservable = true, + const qint64 callerId = -1, + const QString& extra = ""); virtual ~AssetResourceRequest() override; protected: diff --git a/libraries/networking/src/AtpReply.cpp b/libraries/networking/src/AtpReply.cpp index b2b7e8bee7..3ec9b23f5f 100644 --- a/libraries/networking/src/AtpReply.cpp +++ b/libraries/networking/src/AtpReply.cpp @@ -14,7 +14,8 @@ #include "ResourceManager.h" AtpReply::AtpReply(const QUrl& url, QObject* parent) : - _resourceRequest(DependencyManager::get()->createResourceRequest(parent, url)) { + _resourceRequest(DependencyManager::get()->createResourceRequest( + parent, url, true, -1, "AtpReply::AtpReply")) { setOperation(QNetworkAccessManager::GetOperation); connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress); diff --git a/libraries/networking/src/BandwidthRecorder.cpp b/libraries/networking/src/BandwidthRecorder.cpp index 5ad3494017..80276dba5a 100644 --- a/libraries/networking/src/BandwidthRecorder.cpp +++ b/libraries/networking/src/BandwidthRecorder.cpp @@ -18,7 +18,7 @@ BandwidthRecorder::Channel::Channel() { } -float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() { +float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() const { float averageTimeBetweenPackets = _input.getEventDeltaAverage(); if (averageTimeBetweenPackets > 0.0f) { return (1.0f / averageTimeBetweenPackets); @@ -26,7 +26,7 @@ float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() { return 0.0f; } -float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() { +float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() const { float averageTimeBetweenPackets = _output.getEventDeltaAverage(); if (averageTimeBetweenPackets > 0.0f) { return (1.0f / averageTimeBetweenPackets); @@ -34,11 +34,11 @@ float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() { return 0.0f; } -float BandwidthRecorder::Channel::getAverageInputKilobitsPerSecond() { +float BandwidthRecorder::Channel::getAverageInputKilobitsPerSecond() const { return (_input.getAverageSampleValuePerSecond() * (8.0f / 1000)); } -float BandwidthRecorder::Channel::getAverageOutputKilobitsPerSecond() { +float BandwidthRecorder::Channel::getAverageOutputKilobitsPerSecond() const { return (_output.getAverageSampleValuePerSecond() * (8.0f / 1000)); } @@ -77,35 +77,35 @@ void BandwidthRecorder::updateOutboundData(const quint8 channelType, const int s _channels[channelType]->updateOutputAverage(sample); } -float BandwidthRecorder::getAverageInputPacketsPerSecond(const quint8 channelType) { +float BandwidthRecorder::getAverageInputPacketsPerSecond(const quint8 channelType) const { if (! _channels[channelType]) { return 0.0f; } return _channels[channelType]->getAverageInputPacketsPerSecond(); } -float BandwidthRecorder::getAverageOutputPacketsPerSecond(const quint8 channelType) { +float BandwidthRecorder::getAverageOutputPacketsPerSecond(const quint8 channelType) const { if (! _channels[channelType]) { return 0.0f; } return _channels[channelType]->getAverageOutputPacketsPerSecond(); } -float BandwidthRecorder::getAverageInputKilobitsPerSecond(const quint8 channelType) { +float BandwidthRecorder::getAverageInputKilobitsPerSecond(const quint8 channelType) const { if (! _channels[channelType]) { return 0.0f; } return _channels[channelType]->getAverageInputKilobitsPerSecond(); } -float BandwidthRecorder::getAverageOutputKilobitsPerSecond(const quint8 channelType) { +float BandwidthRecorder::getAverageOutputKilobitsPerSecond(const quint8 channelType) const { if (! _channels[channelType]) { return 0.0f; } return _channels[channelType]->getAverageOutputKilobitsPerSecond(); } -float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() { +float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() const { float result = 0.0f; for (uint i=0; i _enableInterstitialMode{ "enableInterstitialMode", true }; +#ifdef Q_OS_ANDROID + Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; +#else + Setting::Handle _enableInterstitialMode { "enableInterstitialMode", true }; +#endif QSet _domainConnectionRefusals; bool _hasCheckedForAccessToken { false }; diff --git a/libraries/networking/src/FileResourceRequest.h b/libraries/networking/src/FileResourceRequest.h index 547b754cb5..fa9a1617a9 100644 --- a/libraries/networking/src/FileResourceRequest.h +++ b/libraries/networking/src/FileResourceRequest.h @@ -19,7 +19,12 @@ class FileResourceRequest : public ResourceRequest { Q_OBJECT public: - FileResourceRequest(const QUrl& url) : ResourceRequest(url) { } + FileResourceRequest( + const QUrl& url, + const bool isObservable = true, + const qint64 callerId = -1, + const QString& extra = "" + ) : ResourceRequest(url, isObservable, callerId, extra) { } protected: virtual void doSend() override; diff --git a/libraries/networking/src/HTTPResourceRequest.h b/libraries/networking/src/HTTPResourceRequest.h index cc628d8855..c725934f2f 100644 --- a/libraries/networking/src/HTTPResourceRequest.h +++ b/libraries/networking/src/HTTPResourceRequest.h @@ -21,7 +21,12 @@ class HTTPResourceRequest : public ResourceRequest { Q_OBJECT public: - HTTPResourceRequest(const QUrl& url) : ResourceRequest(url) { } + HTTPResourceRequest( + const QUrl& url, + const bool isObservable = true, + const qint64 callerId = -1, + const QString& = "" + ) : ResourceRequest(url, isObservable, callerId) { } ~HTTPResourceRequest(); protected: diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index db6ed15792..37b914143e 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -1187,12 +1187,24 @@ void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSock SharedNodePointer LimitedNodeList::findNodeWithAddr(const HifiSockAddr& addr) { QReadLocker locker(&_nodeMutex); - auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&](const UUIDNodePair& pair) { - return pair.second->getActiveSocket() ? (*pair.second->getActiveSocket() == addr) : false; + auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&addr](const UUIDNodePair& pair) { + return pair.second->getPublicSocket() == addr + || pair.second->getLocalSocket() == addr + || pair.second->getSymmetricSocket() == addr; }); return (it != std::end(_nodeHash)) ? it->second : SharedNodePointer(); } +bool LimitedNodeList::sockAddrBelongsToNode(const HifiSockAddr& sockAddr) { + QReadLocker locker(&_nodeMutex); + auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&sockAddr](const UUIDNodePair& pair) { + return pair.second->getPublicSocket() == sockAddr + || pair.second->getLocalSocket() == sockAddr + || pair.second->getSymmetricSocket() == sockAddr; + }); + return it != std::end(_nodeHash); +} + void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID) { auto icePacket = NLPacket::create(packetType); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index cffc49521a..dacefa8e40 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -386,7 +386,7 @@ protected: void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerRequestID = QUuid()); - bool sockAddrBelongsToNode(const HifiSockAddr& sockAddr) { return findNodeWithAddr(sockAddr) != SharedNodePointer(); } + bool sockAddrBelongsToNode(const HifiSockAddr& sockAddr); NodeHash _nodeHash; mutable QReadWriteLock _nodeMutex { QReadWriteLock::Recursive }; diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index 6d384ba558..35956c4789 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -18,7 +18,6 @@ #include #include -#include "BandwidthRecorder.h" #include "NetworkLogging.h" #include #include "NodeType.h" @@ -230,35 +229,18 @@ QDebug operator<<(QDebug debug, const NetworkPeer &peer) { return debug; } - -// FIXME this is a temporary implementation to determine if this is the right approach. -// If so, migrate the BandwidthRecorder into the NetworkPeer class -using BandwidthRecorderPtr = QSharedPointer; -static QHash PEER_BANDWIDTH; - -BandwidthRecorder& getBandwidthRecorder(const QUuid & uuid) { - if (!PEER_BANDWIDTH.count(uuid)) { - PEER_BANDWIDTH.insert(uuid, QSharedPointer::create()); - } - return *PEER_BANDWIDTH[uuid].data(); -} - void NetworkPeer::recordBytesSent(int count) const { - auto& bw = getBandwidthRecorder(_uuid); - bw.updateOutboundData(0, count); + _bandwidthRecorder.updateOutboundData(0, count); } void NetworkPeer::recordBytesReceived(int count) const { - auto& bw = getBandwidthRecorder(_uuid); - bw.updateInboundData(0, count); + _bandwidthRecorder.updateInboundData(0, count); } float NetworkPeer::getOutboundBandwidth() const { - auto& bw = getBandwidthRecorder(_uuid); - return bw.getAverageOutputKilobitsPerSecond(0); + return _bandwidthRecorder.getAverageOutputKilobitsPerSecond(0); } float NetworkPeer::getInboundBandwidth() const { - auto& bw = getBandwidthRecorder(_uuid); - return bw.getAverageInputKilobitsPerSecond(0); + return _bandwidthRecorder.getAverageInputKilobitsPerSecond(0); } diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 462daa1ed2..4688498a96 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -18,8 +18,9 @@ #include #include -#include "UUID.h" +#include "BandwidthRecorder.h" #include "HifiSockAddr.h" +#include "UUID.h" const QString ICE_SERVER_HOSTNAME = "localhost"; const quint16 ICE_SERVER_DEFAULT_PORT = 7337; @@ -113,6 +114,8 @@ protected: HifiSockAddr _symmetricSocket; HifiSockAddr* _activeSocket; + mutable BandwidthRecorder _bandwidthRecorder; + quint64 _wakeTimestamp; std::atomic_ullong _lastHeardMicrostamp; diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 839e269fd4..302e0efa02 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -30,14 +30,14 @@ namespace NetworkingConstants { QUrl METAVERSE_SERVER_URL(); } -const QString URL_SCHEME_ABOUT = "about"; +const QString HIFI_URL_SCHEME_ABOUT = "about"; const QString URL_SCHEME_HIFI = "hifi"; const QString URL_SCHEME_HIFIAPP = "hifiapp"; const QString URL_SCHEME_QRC = "qrc"; -const QString URL_SCHEME_FILE = "file"; -const QString URL_SCHEME_HTTP = "http"; -const QString URL_SCHEME_HTTPS = "https"; -const QString URL_SCHEME_FTP = "ftp"; +const QString HIFI_URL_SCHEME_FILE = "file"; +const QString HIFI_URL_SCHEME_HTTP = "http"; +const QString HIFI_URL_SCHEME_HTTPS = "https"; +const QString HIFI_URL_SCHEME_FTP = "ftp"; const QString URL_SCHEME_ATP = "atp"; #endif // hifi_NetworkingConstants_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 917a4ca791..5e8909db2b 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -427,8 +427,10 @@ void NodeList::sendDomainServerCheckIn() { flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn); // Send duplicate check-ins in the exponentially increasing sequence 1, 1, 2, 4, ... + static const int MAX_CHECKINS_TOGETHER = 20; int outstandingCheckins = _domainHandler.getCheckInPacketsSinceLastReply(); int checkinCount = outstandingCheckins > 1 ? std::pow(2, outstandingCheckins - 2) : 1; + checkinCount = std::min(checkinCount, MAX_CHECKINS_TOGETHER); for (int i = 1; i < checkinCount; ++i) { auto packetCopy = domainPacket->createCopy(*domainPacket); sendPacket(std::move(packetCopy), _domainHandler.getSockAddr()); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index aed9f3b0e5..8e67d7e633 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -10,6 +10,7 @@ // #include "ResourceCache.h" +#include "ResourceRequestObserver.h" #include #include @@ -27,28 +28,35 @@ #include "NetworkLogging.h" #include "NodeList.h" - -#define clamp(x, min, max) (((x) < (min)) ? (min) :\ - (((x) > (max)) ? (max) :\ - (x))) - -void ResourceCacheSharedItems::appendActiveRequest(QWeakPointer resource) { +bool ResourceCacheSharedItems::appendRequest(QWeakPointer resource) { Lock lock(_mutex); - _loadingRequests.append(resource); + if ((uint32_t)_loadingRequests.size() < _requestLimit) { + _loadingRequests.append(resource); + return true; + } else { + _pendingRequests.append(resource); + return false; + } } -void ResourceCacheSharedItems::appendPendingRequest(QWeakPointer resource) { +void ResourceCacheSharedItems::setRequestLimit(uint32_t limit) { Lock lock(_mutex); - _pendingRequests.append(resource); + _requestLimit = limit; } -QList> ResourceCacheSharedItems::getPendingRequests() { +uint32_t ResourceCacheSharedItems::getRequestLimit() const { + Lock lock(_mutex); + return _requestLimit; +} + +QList> ResourceCacheSharedItems::getPendingRequests() const { QList> result; Lock lock(_mutex); - foreach(QSharedPointer resource, _pendingRequests) { - if (resource) { - result.append(resource); + foreach (QWeakPointer resource, _pendingRequests) { + auto locked = resource.lock(); + if (locked) { + result.append(locked); } } @@ -60,16 +68,12 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const { return _pendingRequests.size(); } -QList> ResourceCacheSharedItems::getLoadingRequests() { +QList> ResourceCacheSharedItems::getLoadingRequests() const { QList> result; - Lock lock(_mutex); - - foreach(QSharedPointer resource, _loadingRequests) { - if (resource) { - result.append(resource); - } + { + Lock lock(_mutex); + result = _loadingRequests; } - return result; } @@ -114,7 +118,7 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { // Check load priority float priority = resource->getLoadPriority(); - bool isFile = resource->getURL().scheme() == URL_SCHEME_FILE; + bool isFile = resource->getURL().scheme() == HIFI_URL_SCHEME_FILE; if (priority >= highestPriority && (isFile || !currentHighestIsFile)) { highestPriority = priority; highestIndex = i; @@ -131,6 +135,11 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { return highestResource; } +void ResourceCacheSharedItems::clear() { + Lock lock(_mutex); + _pendingRequests.clear(); + _loadingRequests.clear(); +} ScriptableResourceCache::ScriptableResourceCache(QSharedPointer resourceCache) { _resourceCache = resourceCache; @@ -260,6 +269,7 @@ void ResourceCache::clearATPAssets() { if (auto strongRef = resource.lock()) { // Make sure the resource won't reinsert itself strongRef->setCache(nullptr); + _totalResourcesSize -= strongRef->getBytes(); } } } @@ -269,28 +279,18 @@ void ResourceCache::clearATPAssets() { for (auto& resource : _unusedResources.values()) { if (resource->getURL().scheme() == URL_SCHEME_ATP) { _unusedResources.remove(resource->getLRUKey()); - } - } - } - { - QWriteLocker locker(&_resourcesToBeGottenLock); - auto it = _resourcesToBeGotten.begin(); - while (it != _resourcesToBeGotten.end()) { - if (it->scheme() == URL_SCHEME_ATP) { - it = _resourcesToBeGotten.erase(it); - } else { - ++it; + _unusedResourcesSize -= resource->getBytes(); } } } - + resetResourceCounters(); } void ResourceCache::refreshAll() { // Clear all unused resources so we don't have to reload them clearUnusedResources(); - resetResourceCounters(); + resetUnusedResourceCounter(); QHash> resources; { @@ -306,21 +306,6 @@ void ResourceCache::refreshAll() { } } -void ResourceCache::refresh(const QUrl& url) { - QSharedPointer resource; - { - QReadLocker locker(&_resourcesLock); - resource = _resources.value(url).lock(); - } - - if (resource) { - resource->refresh(); - } else { - removeResource(url); - resetResourceCounters(); - } -} - QVariantList ResourceCache::getResourceList() { QVariantList list; if (QThread::currentThread() != thread()) { @@ -338,12 +323,13 @@ QVariantList ResourceCache::getResourceList() { return list; } -void ResourceCache::setRequestLimit(int limit) { - _requestLimit = limit; +void ResourceCache::setRequestLimit(uint32_t limit) { + auto sharedItems = DependencyManager::get(); + sharedItems->setRequestLimit(limit); // Now go fill any new request spots - while (attemptHighestPriorityRequest()) { - // just keep looping until we reach the new limit or no more pending requests + while (sharedItems->getLoadingRequestsCount() < limit && sharedItems->getPendingRequestsCount() > 0) { + attemptHighestPriorityRequest(); } } @@ -355,35 +341,38 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& } if (resource) { removeUnusedResource(resource); - return resource; } - if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { - return getResource(fallback, QUrl()); + if (!resource && !url.isValid() && !url.isEmpty() && fallback.isValid()) { + resource = getResource(fallback, QUrl()); } - resource = createResource( - url, - fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer(), - extra); - resource->setSelf(resource); - resource->setCache(this); - resource->moveToThread(qApp->thread()); - connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); - { - QWriteLocker locker(&_resourcesLock); - _resources.insert(url, resource); + if (!resource) { + resource = createResource( + url, + fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer(), + extra); + resource->setSelf(resource); + resource->setCache(this); + resource->moveToThread(qApp->thread()); + connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); + { + QWriteLocker locker(&_resourcesLock); + _resources.insert(url, resource); + } + removeUnusedResource(resource); + resource->ensureLoading(); } - removeUnusedResource(resource); - resource->ensureLoading(); + DependencyManager::get()->update( + resource->getURL(), -1, "ResourceCache::getResource"); return resource; } void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) { - _unusedResourcesMaxSize = clamp(unusedResourcesMaxSize, MIN_UNUSED_MAX_SIZE, MAX_UNUSED_MAX_SIZE); + _unusedResourcesMaxSize = glm::clamp(unusedResourcesMaxSize, MIN_UNUSED_MAX_SIZE, MAX_UNUSED_MAX_SIZE); reserveUnusedResource(0); - resetResourceCounters(); + resetUnusedResourceCounter(); } void ResourceCache::addUnusedResource(const QSharedPointer& resource) { @@ -391,18 +380,20 @@ void ResourceCache::addUnusedResource(const QSharedPointer& resource) if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) { resource->setCache(nullptr); removeResource(resource->getURL(), resource->getBytes()); - resetResourceCounters(); + resetTotalResourceCounter(); return; } reserveUnusedResource(resource->getBytes()); resource->setLRUKey(++_lastLRUKey); - _unusedResourcesSize += resource->getBytes(); - resetResourceCounters(); + { + QWriteLocker locker(&_unusedResourcesLock); + _unusedResources.insert(resource->getLRUKey(), resource); + _unusedResourcesSize += resource->getBytes(); + } - QWriteLocker locker(&_unusedResourcesLock); - _unusedResources.insert(resource->getLRUKey(), resource); + resetUnusedResourceCounter(); } void ResourceCache::removeUnusedResource(const QSharedPointer& resource) { @@ -412,7 +403,7 @@ void ResourceCache::removeUnusedResource(const QSharedPointer& resourc _unusedResourcesSize -= resource->getBytes(); locker.unlock(); - resetResourceCounters(); + resetUnusedResourceCounter(); } } @@ -445,14 +436,19 @@ void ResourceCache::clearUnusedResources() { } _unusedResources.clear(); } + _unusedResourcesSize = 0; } -void ResourceCache::resetResourceCounters() { +void ResourceCache::resetTotalResourceCounter() { { QReadLocker locker(&_resourcesLock); _numTotalResources = _resources.size(); } + emit dirty(); +} + +void ResourceCache::resetUnusedResourceCounter() { { QReadLocker locker(&_unusedResourcesLock); _numUnusedResources = _unusedResources.size(); @@ -461,6 +457,13 @@ void ResourceCache::resetResourceCounters() { emit dirty(); } +void ResourceCache::resetResourceCounters() { + resetTotalResourceCounter(); + resetUnusedResourceCounter(); + + emit dirty(); +} + void ResourceCache::removeResource(const QUrl& url, qint64 size) { QWriteLocker locker(&_resourcesLock); _resources.remove(url); @@ -481,38 +484,34 @@ QList> ResourceCache::getLoadingRequests() { return DependencyManager::get()->getLoadingRequests(); } -int ResourceCache::getPendingRequestCount() { +uint32_t ResourceCache::getPendingRequestCount() { return DependencyManager::get()->getPendingRequestsCount(); } -int ResourceCache::getLoadingRequestCount() { +uint32_t ResourceCache::getLoadingRequestCount() { return DependencyManager::get()->getLoadingRequestsCount(); } bool ResourceCache::attemptRequest(QSharedPointer resource) { Q_ASSERT(!resource.isNull()); - auto sharedItems = DependencyManager::get(); - if (_requestsActive >= _requestLimit) { - // wait until a slot becomes available - sharedItems->appendPendingRequest(resource); - return false; + if (sharedItems->appendRequest(resource)) { + resource->makeRequest(); + return true; } - - ++_requestsActive; - sharedItems->appendActiveRequest(resource); - resource->makeRequest(); - return true; + return false; } void ResourceCache::requestCompleted(QWeakPointer resource) { auto sharedItems = DependencyManager::get(); sharedItems->removeRequest(resource); - --_requestsActive; - attemptHighestPriorityRequest(); + // Now go fill any new request spots + while (sharedItems->getLoadingRequestsCount() < sharedItems->getRequestLimit() && sharedItems->getPendingRequestsCount() > 0) { + attemptHighestPriorityRequest(); + } } bool ResourceCache::attemptHighestPriorityRequest() { @@ -521,10 +520,6 @@ bool ResourceCache::attemptHighestPriorityRequest() { return (resource && attemptRequest(resource)); } -const int DEFAULT_REQUEST_LIMIT = 10; -int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT; -int ResourceCache::_requestsActive = 0; - static int requestID = 0; Resource::Resource(const QUrl& url) : @@ -550,7 +545,7 @@ void Resource::ensureLoading() { } void Resource::setLoadPriority(const QPointer& owner, float priority) { - if (!(_failedToLoad)) { + if (!_failedToLoad) { _loadPriorities.insert(owner, priority); } } @@ -566,7 +561,7 @@ void Resource::setLoadPriorities(const QHash, float>& prioriti } void Resource::clearLoadPriority(const QPointer& owner) { - if (!(_failedToLoad)) { + if (!_failedToLoad) { _loadPriorities.remove(owner); } } @@ -592,6 +587,7 @@ void Resource::refresh() { if (_request && !(_loaded || _failedToLoad)) { return; } + if (_request) { _request->disconnect(this); _request->deleteLater(); @@ -613,7 +609,7 @@ void Resource::allReferencesCleared() { if (_cache && isCacheable()) { // create and reinsert new shared pointer - QSharedPointer self(this, &Resource::allReferencesCleared); + QSharedPointer self(this, &Resource::deleter); setSelf(self); reinsert(); @@ -623,7 +619,7 @@ void Resource::allReferencesCleared() { if (_cache) { // remove from the cache _cache->removeResource(getURL(), getBytes()); - _cache->resetResourceCounters(); + _cache->resetTotalResourceCounter(); } deleteLater(); @@ -692,7 +688,8 @@ void Resource::makeRequest() { PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); - _request = DependencyManager::get()->createResourceRequest(this, _activeUrl); + _request = DependencyManager::get()->createResourceRequest( + this, _activeUrl, true, -1, "Resource::makeRequest"); if (!_request) { qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); @@ -732,6 +729,7 @@ void Resource::handleReplyFinished() { { "from_cache", false }, { "size_mb", _bytesTotal / 1000000.0 } }); + ResourceCache::requestCompleted(_self); return; } @@ -742,6 +740,8 @@ void Resource::handleReplyFinished() { setSize(_bytesTotal); + // Make sure we keep the Resource alive here + auto self = _self.lock(); ResourceCache::requestCompleted(_self); auto result = _request->getResult(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 2c0baad3f7..c632399ad4 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -62,25 +62,29 @@ static const qint64 MAX_UNUSED_MAX_SIZE = MAXIMUM_CACHE_SIZE; class ResourceCacheSharedItems : public Dependency { SINGLETON_DEPENDENCY - using Mutex = std::mutex; + using Mutex = std::recursive_mutex; using Lock = std::unique_lock; public: - void appendPendingRequest(QWeakPointer newRequest); - void appendActiveRequest(QWeakPointer newRequest); + bool appendRequest(QWeakPointer newRequest); void removeRequest(QWeakPointer doneRequest); - QList> getPendingRequests(); - uint32_t getPendingRequestsCount() const; - QList> getLoadingRequests(); + void setRequestLimit(uint32_t limit); + uint32_t getRequestLimit() const; + QList> getPendingRequests() const; QSharedPointer getHighestPendingRequest(); + uint32_t getPendingRequestsCount() const; + QList> getLoadingRequests() const; uint32_t getLoadingRequestsCount() const; + void clear(); private: ResourceCacheSharedItems() = default; mutable Mutex _mutex; QList> _pendingRequests; - QList> _loadingRequests; + QList> _loadingRequests; + const uint32_t DEFAULT_REQUEST_LIMIT = 10; + uint32_t _requestLimit { DEFAULT_REQUEST_LIMIT }; }; /// Wrapper to expose resources to JS/QML @@ -200,25 +204,20 @@ public: Q_INVOKABLE QVariantList getResourceList(); - static void setRequestLimit(int limit); - static int getRequestLimit() { return _requestLimit; } - - static int getRequestsActive() { return _requestsActive; } + static void setRequestLimit(uint32_t limit); + static uint32_t getRequestLimit() { return DependencyManager::get()->getRequestLimit(); } void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize); qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; } static QList> getLoadingRequests(); - - static int getPendingRequestCount(); - - static int getLoadingRequestCount(); + static uint32_t getPendingRequestCount(); + static uint32_t getLoadingRequestCount(); ResourceCache(QObject* parent = nullptr); virtual ~ResourceCache(); void refreshAll(); - void refresh(const QUrl& url); void clearUnusedResources(); signals: @@ -272,11 +271,11 @@ private: friend class ScriptableResourceCache; void reserveUnusedResource(qint64 resourceSize); - void resetResourceCounters(); void removeResource(const QUrl& url, qint64 size = 0); - static int _requestLimit; - static int _requestsActive; + void resetTotalResourceCounter(); + void resetUnusedResourceCounter(); + void resetResourceCounters(); // Resources QHash> _resources; @@ -293,10 +292,6 @@ private: std::atomic _numUnusedResources { 0 }; std::atomic _unusedResourcesSize { 0 }; - - // Pending resources - QQueue _resourcesToBeGotten; - QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive }; }; /// Wrapper to expose resource caches to JS/QML @@ -455,7 +450,7 @@ protected: virtual void makeRequest(); /// Checks whether the resource is cacheable. - virtual bool isCacheable() const { return true; } + virtual bool isCacheable() const { return _loaded; } /// Called when the download has finished. /// This should be overridden by subclasses that need to process the data once it is downloaded. diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 553f0d0a61..f4f5525ddc 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -82,10 +82,10 @@ const QSet& getKnownUrls() { static std::once_flag once; std::call_once(once, [] { knownUrls.insert(URL_SCHEME_QRC); - knownUrls.insert(URL_SCHEME_FILE); - knownUrls.insert(URL_SCHEME_HTTP); - knownUrls.insert(URL_SCHEME_HTTPS); - knownUrls.insert(URL_SCHEME_FTP); + knownUrls.insert(HIFI_URL_SCHEME_FILE); + knownUrls.insert(HIFI_URL_SCHEME_HTTP); + knownUrls.insert(HIFI_URL_SCHEME_HTTPS); + knownUrls.insert(HIFI_URL_SCHEME_FTP); knownUrls.insert(URL_SCHEME_ATP); }); return knownUrls; @@ -97,7 +97,7 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) { if (!getKnownUrls().contains(scheme)) { // check the degenerative file case: on windows we can often have urls of the form c:/filename // this checks for and works around that case. - QUrl urlWithFileScheme{ URL_SCHEME_FILE + ":///" + url.toString() }; + QUrl urlWithFileScheme{ HIFI_URL_SCHEME_FILE + ":///" + url.toString() }; if (!urlWithFileScheme.toLocalFile().isEmpty()) { return urlWithFileScheme; } @@ -112,22 +112,28 @@ void ResourceManager::cleanup() { _thread.wait(); } -ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) { +ResourceRequest* ResourceManager::createResourceRequest( + QObject* parent, + const QUrl& url, + const bool isObservable, + const qint64 callerId, + const QString& extra +) { auto normalizedURL = normalizeURL(url); auto scheme = normalizedURL.scheme(); ResourceRequest* request = nullptr; - if (scheme == URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { - request = new FileResourceRequest(normalizedURL); - } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { - request = new HTTPResourceRequest(normalizedURL); + if (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { + request = new FileResourceRequest(normalizedURL, isObservable, callerId, extra); + } else if (scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || scheme == HIFI_URL_SCHEME_FTP) { + request = new HTTPResourceRequest(normalizedURL, isObservable, callerId, extra); } else if (scheme == URL_SCHEME_ATP) { if (!_atpSupportEnabled) { qCDebug(networking) << "ATP support not enabled, unable to create request for URL: " << url.url(); return nullptr; } - request = new AssetResourceRequest(normalizedURL); + request = new AssetResourceRequest(normalizedURL, isObservable, callerId, extra); } else { qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); return nullptr; @@ -143,10 +149,10 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q bool ResourceManager::resourceExists(const QUrl& url) { auto scheme = url.scheme(); - if (scheme == URL_SCHEME_FILE) { + if (scheme == HIFI_URL_SCHEME_FILE) { QFileInfo file{ url.toString() }; return file.exists(); - } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { + } else if (scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || scheme == HIFI_URL_SCHEME_FTP) { auto& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request{ url }; @@ -163,7 +169,7 @@ bool ResourceManager::resourceExists(const QUrl& url) { return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200; } else if (scheme == URL_SCHEME_ATP && _atpSupportEnabled) { - auto request = new AssetResourceRequest(url); + auto request = new AssetResourceRequest(url, ResourceRequest::IS_NOT_OBSERVABLE); ByteRange range; range.fromInclusive = 1; range.toExclusive = 1; diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index a79222d2d8..adaa1cf886 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -34,7 +34,12 @@ public: QString normalizeURL(const QString& urlString); QUrl normalizeURL(const QUrl& url); - ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url); + ResourceRequest* createResourceRequest( + QObject* parent, + const QUrl& url, + const bool isObservable = true, + const qint64 callerId = -1, + const QString& extra = ""); void init(); void cleanup(); diff --git a/libraries/networking/src/ResourceRequest.cpp b/libraries/networking/src/ResourceRequest.cpp index 115e665b77..c63bd4c563 100644 --- a/libraries/networking/src/ResourceRequest.cpp +++ b/libraries/networking/src/ResourceRequest.cpp @@ -10,13 +10,13 @@ // #include "ResourceRequest.h" +#include "ResourceRequestObserver.h" #include #include #include -ResourceRequest::ResourceRequest(const QUrl& url) : _url(url) { } void ResourceRequest::send() { if (QThread::currentThread() != thread()) { @@ -24,6 +24,10 @@ void ResourceRequest::send() { return; } + if (_isObservable) { + DependencyManager::get()->update(_url, _callerId, _extra + " => ResourceRequest::send"); + } + Q_ASSERT(_state == NotStarted); _state = InProgress; diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 8dd09ccc49..eb306ca5be 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -40,7 +40,20 @@ const QString STAT_FILE_RESOURCE_TOTAL_BYTES = "FILEBytesDownloaded"; class ResourceRequest : public QObject { Q_OBJECT public: - ResourceRequest(const QUrl& url); + static const bool IS_OBSERVABLE = true; + static const bool IS_NOT_OBSERVABLE = false; + + ResourceRequest( + const QUrl& url, + const bool isObservable = IS_OBSERVABLE, + const qint64 callerId = -1, + const QString& extra = "" + ) : _url(url), + _isObservable(isObservable), + _callerId(callerId), + _extra(extra) + { } + virtual ~ResourceRequest() = default; enum State { @@ -99,6 +112,9 @@ protected: bool _rangeRequestSuccessful { false }; uint64_t _totalSizeOfResource { 0 }; int64_t _lastRecordedBytesDownloaded { 0 }; + bool _isObservable; + qint64 _callerId; + QString _extra; }; #endif diff --git a/libraries/networking/src/udt/CongestionControl.h b/libraries/networking/src/udt/CongestionControl.h index 7093e8bd96..bfe7f552d1 100644 --- a/libraries/networking/src/udt/CongestionControl.h +++ b/libraries/networking/src/udt/CongestionControl.h @@ -45,8 +45,10 @@ public: virtual void onTimeout() {} virtual void onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {} + virtual void onPacketReSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {} virtual int estimatedTimeout() const = 0; + protected: void setMSS(int mss) { _mss = mss; } virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) = 0; diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 24e294881a..4798288a18 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -195,7 +195,7 @@ void Connection::recordSentPackets(int wireSize, int payloadSize, void Connection::recordRetransmission(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { _stats.record(ConnectionStats::Stats::Retransmission); - _congestionControl->onPacketSent(wireSize, seqNum, timePoint); + _congestionControl->onPacketReSent(wireSize, seqNum, timePoint); } void Connection::sendACK() { @@ -303,7 +303,7 @@ void Connection::processControl(ControlPacketPointer controlPacket) { // where the other end expired our connection. Let's reset. #ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue"; + qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue"; #endif _hasReceivedHandshakeACK = false; stopSendQueue(); @@ -327,19 +327,19 @@ void Connection::processACK(ControlPacketPointer controlPacket) { return; } - if (ack <= _lastReceivedACK) { + if (ack < _lastReceivedACK) { // this is an out of order ACK, bail - // or - // processing an already received ACK, bail return; } - - _lastReceivedACK = ack; - - // ACK the send queue so it knows what was received - getSendQueue().ack(ack); - + if (ack > _lastReceivedACK) { + // this is not a repeated ACK, so update our member and tell the send queue + _lastReceivedACK = ack; + + // ACK the send queue so it knows what was received + getSendQueue().ack(ack); + } + // give this ACK to the congestion control and update the send queue parameters updateCongestionControlAndSendQueue([this, ack, &controlPacket] { if (_congestionControl->onACK(ack, controlPacket->getReceiveTime())) { diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8cb7c9aaa4..9eed463d2d 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,14 +33,14 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::BloomEffect); + return static_cast(EntityVersion::FixedLightSerialization); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::MigrateAvatarEntitiesToTraits); + return static_cast(AvatarMixerPacketVersion::FarGrabJointsRedux); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 84a93ac939..e5efe05ad0 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -126,14 +126,13 @@ public: EntityScriptCallMethod, ChallengeOwnershipRequest, ChallengeOwnershipReply, - OctreeDataFileRequest, OctreeDataFileReply, OctreeDataPersist, - EntityClone, EntityQueryInitialResultsComplete, BulkAvatarTraits, + AudioSoloRequest, NUM_PACKET_TYPE }; @@ -242,7 +241,10 @@ enum class EntityVersion : PacketVersion { YieldSimulationOwnership, ParticleEntityFix, ParticleSpin, - BloomEffect + BloomEffect, + GrabProperties, + ScriptGlmVectors, + FixedLightSerialization }; enum class EntityScriptCallMethodVersion : PacketVersion { @@ -293,7 +295,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { ProceduralFaceMovementFlagsAndBlendshapes, FarGrabJoints, MigrateSkeletonURLToTraits, - MigrateAvatarEntitiesToTraits + MigrateAvatarEntitiesToTraits, + FarGrabJointsRedux }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index b1dfb9a8cf..9cba4970ac 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -481,6 +481,7 @@ bool SendQueue::isInactive(bool attemptedToSendPacket) { auto cvStatus = _emptyCondition.wait_for(locker, EMPTY_QUEUES_INACTIVE_TIMEOUT); if (cvStatus == std::cv_status::timeout && (_packets.isEmpty() || isFlowWindowFull()) && _naks.isEmpty()) { + #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "SendQueue to" << _destination << "has been empty for" << EMPTY_QUEUES_INACTIVE_TIMEOUT.count() diff --git a/libraries/networking/src/udt/SequenceNumber.h b/libraries/networking/src/udt/SequenceNumber.h index 2c82eccfa3..13bd83d4f2 100644 --- a/libraries/networking/src/udt/SequenceNumber.h +++ b/libraries/networking/src/udt/SequenceNumber.h @@ -14,7 +14,7 @@ #include -#include +#include namespace udt { diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 44220df8f8..25e6fae023 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -37,7 +37,6 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) : _shouldChangeSocketOptions(shouldChangeSocketOptions) { connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams); - connect(this, &Socket::pendingDatagrams, this, &Socket::processPendingDatagrams, Qt::QueuedConnection); // make sure we hear about errors and state changes from the underlying socket connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), @@ -229,13 +228,13 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc return bytesWritten; } -Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) { +Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreate) { auto it = _connectionsHash.find(sockAddr); if (it == _connectionsHash.end()) { // we did not have a matching connection, time to see if we should make one - if (_connectionCreationFilterOperator && !_connectionCreationFilterOperator(sockAddr)) { + if (filterCreate && _connectionCreationFilterOperator && !_connectionCreationFilterOperator(sockAddr)) { // the connection creation filter did not allow us to create a new connection #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "Socket::findOrCreateConnection refusing to create connection for" << sockAddr @@ -316,89 +315,68 @@ void Socket::checkForReadyReadBackup() { } void Socket::readPendingDatagrams() { - int packetsRead = 0; - + using namespace std::chrono; + static const auto MAX_PROCESS_TIME { 100ms }; + const auto abortTime = system_clock::now() + MAX_PROCESS_TIME; int packetSizeWithHeader = -1; - // Max datagrams to read before processing: - static const int MAX_DATAGRAMS_CONSECUTIVELY = 10000; - while (_udpSocket.hasPendingDatagrams() - && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1 - && packetsRead <= MAX_DATAGRAMS_CONSECUTIVELY) { - // grab a time point we can mark as the receive time of this packet - auto receiveTime = p_high_resolution_clock::now(); - - // setup a buffer to read the packet into - auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); - - QHostAddress senderAddress; - quint16 senderPort; - - // pull the datagram - auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, - &senderAddress, &senderPort); - - // we either didn't pull anything for this packet or there was an error reading (this seems to trigger - // on windows even if there's not a packet available) - if (sizeRead < 0) { - continue; + while (_udpSocket.hasPendingDatagrams() && + (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) { + if (system_clock::now() > abortTime) { + // We've been running for too long, stop processing packets for now + // Once we've processed the event queue, we'll come back to packet processing + break; } - _incomingDatagrams.push_back({ senderAddress, senderPort, packetSizeWithHeader, - std::move(buffer), receiveTime }); - ++packetsRead; - - } - - if (packetsRead > _maxDatagramsRead) { - _maxDatagramsRead = packetsRead; - qCDebug(networking) << "readPendingDatagrams: Datagrams read:" << packetsRead; - } - emit pendingDatagrams(packetsRead); -} - -void Socket::processPendingDatagrams(int) { - // setup a HifiSockAddr to read into - HifiSockAddr senderSockAddr; - - while (!_incomingDatagrams.empty()) { - auto& datagram = _incomingDatagrams.front(); - senderSockAddr.setAddress(datagram._senderAddress); - senderSockAddr.setPort(datagram._senderPort); - int datagramSize = datagram._datagramLength; - auto receiveTime = datagram._receiveTime; - // we're reading a packet so re-start the readyRead backup timer _readyReadBackupTimer->start(); + // grab a time point we can mark as the receive time of this packet + auto receiveTime = p_high_resolution_clock::now(); + + // setup a HifiSockAddr to read into + HifiSockAddr senderSockAddr; + + // setup a buffer to read the packet into + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); + + // pull the datagram + auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader, + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + // save information for this packet, in case it is the one that sticks readyRead - _lastPacketSizeRead = datagramSize; + _lastPacketSizeRead = sizeRead; _lastPacketSockAddr = senderSockAddr; - // Process unfiltered packets first. + if (sizeRead <= 0) { + // we either didn't pull anything for this packet or there was an error reading (this seems to trigger + // on windows even if there's not a packet available) + continue; + } + auto it = _unfilteredHandlers.find(senderSockAddr); + if (it != _unfilteredHandlers.end()) { - // we have a registered unfiltered handler for this HifiSockAddr (eg. STUN packet) - call that and return + // we have a registered unfiltered handler for this HifiSockAddr - call that and return if (it->second) { - auto basePacket = BasePacket::fromReceivedPacket(std::move(datagram._datagram), - datagramSize, senderSockAddr); + auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); basePacket->setReceiveTime(receiveTime); it->second(std::move(basePacket)); } - _incomingDatagrams.pop_front(); + continue; } // check if this was a control packet or a data packet - bool isControlPacket = *reinterpret_cast(datagram._datagram.get()) & CONTROL_BIT_MASK; + bool isControlPacket = *reinterpret_cast(buffer.get()) & CONTROL_BIT_MASK; if (isControlPacket) { // setup a control packet from the data we just read - auto controlPacket = ControlPacket::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr); + auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); controlPacket->setReceiveTime(receiveTime); // move this control packet to the matching connection, if there is one - auto connection = findOrCreateConnection(senderSockAddr); + auto connection = findOrCreateConnection(senderSockAddr, true); if (connection) { connection->processControl(move(controlPacket)); @@ -406,17 +384,17 @@ void Socket::processPendingDatagrams(int) { } else { // setup a Packet from the data we just read - auto packet = Packet::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr); + auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr); packet->setReceiveTime(receiveTime); // save the sequence number in case this is the packet that sticks readyRead _lastReceivedSequenceNumber = packet->getSequenceNumber(); - // call our hash verification operator to see if this packet is verified + // call our verification operator to see if this packet is verified if (!_packetFilterOperator || _packetFilterOperator(*packet)) { if (packet->isReliable()) { // if this was a reliable packet then signal the matching connection with the sequence number - auto connection = findOrCreateConnection(senderSockAddr); + auto connection = findOrCreateConnection(senderSockAddr, true); if (!connection || !connection->processReceivedSequenceNumber(packet->getSequenceNumber(), packet->getDataSize(), @@ -426,13 +404,12 @@ void Socket::processPendingDatagrams(int) { qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet) << ", type" << NLPacket::typeInHeader(*packet); #endif - _incomingDatagrams.pop_front(); continue; } } if (packet->isPartOfMessage()) { - auto connection = findOrCreateConnection(senderSockAddr); + auto connection = findOrCreateConnection(senderSockAddr, true); if (connection) { connection->queueReceivedMessagePacket(std::move(packet)); } @@ -442,8 +419,6 @@ void Socket::processPendingDatagrams(int) { } } } - - _incomingDatagrams.pop_front(); } } diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index ef4a457116..99266e105e 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -95,7 +95,6 @@ public: signals: void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr); - void pendingDatagrams(int datagramCount); public slots: void cleanupConnection(HifiSockAddr sockAddr); @@ -103,7 +102,6 @@ public slots: private slots: void readPendingDatagrams(); - void processPendingDatagrams(int datagramCount); void checkForReadyReadBackup(); void handleSocketError(QAbstractSocket::SocketError socketError); @@ -111,7 +109,7 @@ private slots: private: void setSystemBufferSizes(); - Connection* findOrCreateConnection(const HifiSockAddr& sockAddr); + Connection* findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreation = false); bool socketMatchesNodeOrDomain(const HifiSockAddr& sockAddr); // privatized methods used by UDTTest - they are private since they must be called on the Socket thread @@ -147,17 +145,6 @@ private: int _lastPacketSizeRead { 0 }; SequenceNumber _lastReceivedSequenceNumber; HifiSockAddr _lastPacketSockAddr; - - struct Datagram { - QHostAddress _senderAddress; - int _senderPort; - int _datagramLength; - std::unique_ptr _datagram; - p_high_resolution_clock::time_point _receiveTime; - }; - - std::list _incomingDatagrams; - int _maxDatagramsRead { 0 }; friend UDTTest; }; diff --git a/libraries/networking/src/udt/TCPVegasCC.cpp b/libraries/networking/src/udt/TCPVegasCC.cpp index 4842e5a204..f2119237c2 100644 --- a/libraries/networking/src/udt/TCPVegasCC.cpp +++ b/libraries/networking/src/udt/TCPVegasCC.cpp @@ -27,112 +27,106 @@ TCPVegasCC::TCPVegasCC() { _baseRTT = std::numeric_limits::max(); } +bool TCPVegasCC::calculateRTT(p_high_resolution_clock::time_point sendTime, p_high_resolution_clock::time_point receiveTime) { + // calculate the RTT (receive time - time ACK sent) + int lastRTT = duration_cast(receiveTime - sendTime).count(); + + const int MAX_RTT_SAMPLE_MICROSECONDS = 10000000; + + if (lastRTT < 0) { + Q_ASSERT_X(false, __FUNCTION__, "calculated an RTT that is not > 0"); + return false; + } else if (lastRTT == 0) { + // we do not allow a zero microsecond RTT (as per the UNIX kernel implementation of TCP Vegas) + lastRTT = 1; + } else if (lastRTT > MAX_RTT_SAMPLE_MICROSECONDS) { + // we cap the lastRTT to MAX_RTT_SAMPLE_MICROSECONDS to avoid overflows in window size calculations + lastRTT = MAX_RTT_SAMPLE_MICROSECONDS; + } + + if (_ewmaRTT == -1) { + // first RTT sample - set _ewmaRTT to the value and set the variance to half the value + _ewmaRTT = lastRTT; + _rttVariance = lastRTT / 2; + } else { + // This updates the RTT using exponential weighted moving average + // This is the Jacobson's forumla for RTT estimation + // http://www.mathcs.emory.edu/~cheung/Courses/455/Syllabus/7-transport/Jacobson-88.pdf + + // Estimated RTT = (1 - x)(estimatedRTT) + (x)(sampleRTT) + // (where x = 0.125 via Jacobson) + + // Deviation = (1 - x)(deviation) + x |sampleRTT - estimatedRTT| + // (where x = 0.25 via Jacobson) + + static const int RTT_ESTIMATION_ALPHA = 8; + static const int RTT_ESTIMATION_VARIANCE_ALPHA = 4; + + _ewmaRTT = (_ewmaRTT * (RTT_ESTIMATION_ALPHA - 1) + lastRTT) / RTT_ESTIMATION_ALPHA; + _rttVariance = (_rttVariance * (RTT_ESTIMATION_VARIANCE_ALPHA- 1) + + abs(lastRTT - _ewmaRTT)) / RTT_ESTIMATION_VARIANCE_ALPHA; + } + + // keep track of the lowest RTT during connection + _baseRTT = std::min(_baseRTT, lastRTT); + + // find the min RTT during the last RTT + _currentMinRTT = std::min(_currentMinRTT, lastRTT); + + // add 1 to the number of RTT samples collected during this RTT window + ++_numRTTs; + + return true; +} + bool TCPVegasCC::onACK(SequenceNumber ack, p_high_resolution_clock::time_point receiveTime) { - auto it = _sentPacketTimes.find(ack); auto previousAck = _lastACK; _lastACK = ack; - if (it != _sentPacketTimes.end()) { + bool wasDuplicateACK = (ack == previousAck); - // calculate the RTT (receive time - time ACK sent) - int lastRTT = duration_cast(receiveTime - it->second).count(); + auto it = std::find_if(_sentPacketDatas.begin(), _sentPacketDatas.end(), [ack](SentPacketData& packetTime){ + return packetTime.sequenceNumber == ack; + }); - const int MAX_RTT_SAMPLE_MICROSECONDS = 10000000; + if (!wasDuplicateACK && it != _sentPacketDatas.end()) { + // check if we can unambigiously calculate an RTT from this ACK - if (lastRTT < 0) { - Q_ASSERT_X(false, __FUNCTION__, "calculated an RTT that is not > 0"); + // for that to be the case, + // any of the packets this ACK covers (from the current ACK back to our previous ACK) + // must not have been re-sent + bool canBeUsedForRTT = std::none_of(_sentPacketDatas.begin(), _sentPacketDatas.end(), + [ack, previousAck](SentPacketData& sentPacketData) + { + return sentPacketData.sequenceNumber > previousAck + && sentPacketData.sequenceNumber <= ack + && sentPacketData.wasResent; + }); + + auto sendTime = it->timePoint; + + // remove all sent packet times up to this sequence number + it = _sentPacketDatas.erase(_sentPacketDatas.begin(), it + 1); + + // if we can use this ACK for an RTT calculation then do so + // returning false if we calculate an invalid RTT + if (canBeUsedForRTT && !calculateRTT(sendTime, receiveTime)) { return false; - } else if (lastRTT == 0) { - // we do not allow a zero microsecond RTT (as per the UNIX kernel implementation of TCP Vegas) - lastRTT = 1; - } else if (lastRTT > MAX_RTT_SAMPLE_MICROSECONDS) { - // we cap the lastRTT to MAX_RTT_SAMPLE_MICROSECONDS to avoid overflows in window size calculations - lastRTT = MAX_RTT_SAMPLE_MICROSECONDS; } + } - if (_ewmaRTT == -1) { - // first RTT sample - set _ewmaRTT to the value and set the variance to half the value - _ewmaRTT = lastRTT; - _rttVariance = lastRTT / 2; - } else { - // This updates the RTT using exponential weighted moving average - // This is the Jacobson's forumla for RTT estimation - // http://www.mathcs.emory.edu/~cheung/Courses/455/Syllabus/7-transport/Jacobson-88.pdf - - // Estimated RTT = (1 - x)(estimatedRTT) + (x)(sampleRTT) - // (where x = 0.125 via Jacobson) - - // Deviation = (1 - x)(deviation) + x |sampleRTT - estimatedRTT| - // (where x = 0.25 via Jacobson) - - static const int RTT_ESTIMATION_ALPHA = 8; - static const int RTT_ESTIMATION_VARIANCE_ALPHA = 4; - - _ewmaRTT = (_ewmaRTT * (RTT_ESTIMATION_ALPHA - 1) + lastRTT) / RTT_ESTIMATION_ALPHA; - _rttVariance = (_rttVariance * (RTT_ESTIMATION_VARIANCE_ALPHA- 1) - + abs(lastRTT - _ewmaRTT)) / RTT_ESTIMATION_VARIANCE_ALPHA; - } - - // add 1 to the number of ACKs during this RTT - ++_numACKs; - - // keep track of the lowest RTT during connection - _baseRTT = std::min(_baseRTT, lastRTT); - - // find the min RTT during the last RTT - _currentMinRTT = std::min(_currentMinRTT, lastRTT); - - auto sinceLastAdjustment = duration_cast(p_high_resolution_clock::now() - _lastAdjustmentTime).count(); - if (sinceLastAdjustment >= _ewmaRTT) { - performCongestionAvoidance(ack); - } - - // remove this sent packet time from the hash - _sentPacketTimes.erase(it); + auto sinceLastAdjustment = duration_cast(p_high_resolution_clock::now() - _lastAdjustmentTime).count(); + if (sinceLastAdjustment >= _ewmaRTT) { + performCongestionAvoidance(ack); } ++_numACKSinceFastRetransmit; // perform the fast re-transmit check if this is a duplicate ACK or if this is the first or second ACK // after a previous fast re-transmit - if (ack == previousAck || _numACKSinceFastRetransmit < 3) { - // we may need to re-send ackNum + 1 if it has been more than our estimated timeout since it was sent - - auto it = _sentPacketTimes.find(ack + 1); - if (it != _sentPacketTimes.end()) { - - auto now = p_high_resolution_clock::now(); - auto sinceSend = duration_cast(now - it->second).count(); - - if (sinceSend >= estimatedTimeout()) { - // break out of slow start, we've decided this is loss - _slowStart = false; - - // reset the fast re-transmit counter - _numACKSinceFastRetransmit = 0; - - // return true so the caller knows we needed a fast re-transmit - return true; - } - } - - // if this is the 3rd duplicate ACK, we fallback to Reno's fast re-transmit - static const int RENO_FAST_RETRANSMIT_DUPLICATE_COUNT = 3; - - ++_duplicateACKCount; - - if (ack == previousAck && _duplicateACKCount == RENO_FAST_RETRANSMIT_DUPLICATE_COUNT) { - // break out of slow start, we just hit loss - _slowStart = false; - - // reset our fast re-transmit counters - _numACKSinceFastRetransmit = 0; - _duplicateACKCount = 0; - - // return true so the caller knows we needed a fast re-transmit - return true; - } + if (wasDuplicateACK || _numACKSinceFastRetransmit < 3) { + return needsFastRetransmit(ack, wasDuplicateACK); } else { _duplicateACKCount = 0; } @@ -141,6 +135,49 @@ bool TCPVegasCC::onACK(SequenceNumber ack, p_high_resolution_clock::time_point r return false; } +bool TCPVegasCC::needsFastRetransmit(SequenceNumber ack, bool wasDuplicateACK) { + // we may need to re-send ackNum + 1 if it has been more than our estimated timeout since it was sent + + auto nextIt = std::find_if(_sentPacketDatas.begin(), _sentPacketDatas.end(), [ack](SentPacketData& packetTime){ + return packetTime.sequenceNumber == ack + 1; + }); + + if (nextIt != _sentPacketDatas.end()) { + auto now = p_high_resolution_clock::now(); + auto sinceSend = duration_cast(now - nextIt->timePoint).count(); + + if (sinceSend >= estimatedTimeout()) { + // break out of slow start, we've decided this is loss + _slowStart = false; + + // reset the fast re-transmit counter + _numACKSinceFastRetransmit = 0; + + // return true so the caller knows we needed a fast re-transmit + return true; + } + } + + // if this is the 3rd duplicate ACK, we fallback to Reno's fast re-transmit + static const int RENO_FAST_RETRANSMIT_DUPLICATE_COUNT = 3; + + ++_duplicateACKCount; + + if (wasDuplicateACK && _duplicateACKCount == RENO_FAST_RETRANSMIT_DUPLICATE_COUNT) { + // break out of slow start, we just hit loss + _slowStart = false; + + // reset our fast re-transmit counters + _numACKSinceFastRetransmit = 0; + _duplicateACKCount = 0; + + // return true so the caller knows we needed a fast re-transmit + return true; + } + + return false; +} + void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) { static int VEGAS_ALPHA_SEGMENTS = 4; static int VEGAS_BETA_SEGMENTS = 6; @@ -158,7 +195,7 @@ void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) { int64_t windowSizeDiff = (int64_t) _congestionWindowSize * (rtt - _baseRTT) / _baseRTT; - if (_numACKs <= 2) { + if (_numRTTs <= 2) { performRenoCongestionAvoidance(ack); } else { if (_slowStart) { @@ -209,7 +246,7 @@ void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) { _currentMinRTT = std::numeric_limits::max(); // reset our count of collected RTT samples - _numACKs = 0; + _numRTTs = 0; } @@ -230,29 +267,29 @@ void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) { return; } - int numAcked = _numACKs; + int numRTTCollected = _numRTTs; if (_slowStart) { // while in slow start we grow the congestion window by the number of ACKed packets // allowing it to grow as high as the slow start threshold - int congestionWindow = _congestionWindowSize + numAcked; + int congestionWindow = _congestionWindowSize + numRTTCollected; if (congestionWindow > udt::MAX_PACKETS_IN_FLIGHT) { // we're done with slow start, set the congestion window to the slow start threshold _congestionWindowSize = udt::MAX_PACKETS_IN_FLIGHT; // figure out how many left over ACKs we should apply using the regular reno congestion avoidance - numAcked = congestionWindow - udt::MAX_PACKETS_IN_FLIGHT; + numRTTCollected = congestionWindow - udt::MAX_PACKETS_IN_FLIGHT; } else { _congestionWindowSize = congestionWindow; - numAcked = 0; + numRTTCollected = 0; } } // grab the size of the window prior to reno additive increase int preAIWindowSize = _congestionWindowSize; - if (numAcked > 0) { + if (numRTTCollected > 0) { // Once we are out of slow start, we use additive increase to grow the window slowly. // We grow the congestion window by a single packet everytime the entire congestion window is sent. @@ -263,7 +300,7 @@ void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) { } // increase the window size by (1 / window size) for every ACK received - _ackAICount += numAcked; + _ackAICount += numRTTCollected; if (_ackAICount >= preAIWindowSize) { // when _ackAICount % preAIWindowSize == 0 then _ackAICount is 0 // when _ackAICount % preAIWindowSize != 0 then _ackAICount is _ackAICount - (_ackAICount % preAIWindowSize) @@ -277,8 +314,19 @@ void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) { } void TCPVegasCC::onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { - if (_sentPacketTimes.find(seqNum) == _sentPacketTimes.end()) { - _sentPacketTimes[seqNum] = timePoint; + _sentPacketDatas.emplace_back(seqNum, timePoint); +} + +void TCPVegasCC::onPacketReSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { + // look for our information for this sent packet + auto it = std::find_if(_sentPacketDatas.begin(), _sentPacketDatas.end(), [seqNum](SentPacketData& sentPacketInfo){ + return sentPacketInfo.sequenceNumber == seqNum; + }); + + // if we found information for this packet (it hasn't been erased because it hasn't yet been ACKed) + // then mark it as re-sent so we know it cannot be used for RTT calculations + if (it != _sentPacketDatas.end()) { + it->wasResent = true; } } diff --git a/libraries/networking/src/udt/TCPVegasCC.h b/libraries/networking/src/udt/TCPVegasCC.h index bb14728d4b..1d83c4c992 100644 --- a/libraries/networking/src/udt/TCPVegasCC.h +++ b/libraries/networking/src/udt/TCPVegasCC.h @@ -30,6 +30,7 @@ public: virtual void onTimeout() override {}; virtual void onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) override; + virtual void onPacketReSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) override; virtual int estimatedTimeout() const override; @@ -37,11 +38,23 @@ protected: virtual void performCongestionAvoidance(SequenceNumber ack); virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) override { _lastACK = seqNum - 1; } private: + bool calculateRTT(p_high_resolution_clock::time_point sendTime, p_high_resolution_clock::time_point receiveTime); + bool needsFastRetransmit(SequenceNumber ack, bool wasDuplicateACK); + bool isCongestionWindowLimited(); void performRenoCongestionAvoidance(SequenceNumber ack); - using PacketTimeList = std::map; - PacketTimeList _sentPacketTimes; // Map of sequence numbers to sent time + struct SentPacketData { + SentPacketData(SequenceNumber seqNum, p_high_resolution_clock::time_point tPoint) + : sequenceNumber(seqNum), timePoint(tPoint) {}; + + SequenceNumber sequenceNumber; + p_high_resolution_clock::time_point timePoint; + bool wasResent { false }; + }; + + using PacketTimeList = std::vector; + PacketTimeList _sentPacketDatas; // association of sequence numbers to sent time, for RTT calc p_high_resolution_clock::time_point _lastAdjustmentTime; // Time of last congestion control adjustment @@ -56,7 +69,7 @@ private: int _ewmaRTT { -1 }; // Exponential weighted moving average RTT int _rttVariance { 0 }; // Variance in collected RTT values - int _numACKs { 0 }; // Number of ACKs received during the last RTT (since last performed congestion avoidance) + int _numRTTs { 0 }; // Number of RTTs calculated during the last RTT (since last performed congestion avoidance) int _ackAICount { 0 }; // Counter for number of ACKs received for Reno additive increase int _duplicateACKCount { 0 }; // Counter for duplicate ACKs received diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index c184fe122d..e5d852ccab 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -734,11 +734,17 @@ QString getMarketplaceID(const QString& urlString) { return QString(); } -bool Octree::readFromURL(const QString& urlString) { +bool Octree::readFromURL( + const QString& urlString, + const bool isObservable, + const qint64 callerId +) { QString trimmedUrl = urlString.trimmed(); QString marketplaceID = getMarketplaceID(trimmedUrl); - auto request = - std::unique_ptr(DependencyManager::get()->createResourceRequest(this, trimmedUrl)); + qDebug() << "!!!!! going to createResourceRequest " << callerId; + auto request = std::unique_ptr( + DependencyManager::get()->createResourceRequest( + this, trimmedUrl, isObservable, callerId, "Octree::readFromURL")); if (!request) { return false; @@ -768,7 +774,11 @@ bool Octree::readFromURL(const QString& urlString) { } -bool Octree::readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID) { +bool Octree::readFromStream( + uint64_t streamLength, + QDataStream& inputStream, + const QString& marketplaceID +) { // decide if this is binary SVO or JSON-formatted SVO QIODevice *device = inputStream.device(); char firstChar; @@ -807,7 +817,11 @@ QVariantMap addMarketplaceIDToDocumentEntities(QVariantMap& doc, const QString& } // Unnamed namepsace const int READ_JSON_BUFFER_SIZE = 2048; -bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) { +bool Octree::readJSONFromStream( + uint64_t streamLength, + QDataStream& inputStream, + const QString& marketplaceID /*=""*/ +) { // if the data is gzipped we may not have a useful bytesAvailable() result, so just keep reading until // we get an eof. Leave streamLength parameter for consistency. diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 678d8aa5de..eef23493f6 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -212,7 +212,7 @@ public: // Octree importers bool readFromFile(const char* filename); - bool readFromURL(const QString& url); // will support file urls as well... + bool readFromURL(const QString& url, const bool isObservable = true, const qint64 callerId = -1); // will support file urls as well... bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); bool readSVOFromStream(uint64_t streamLength, QDataStream& inputStream); bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index b938850684..88e83c01c8 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -198,17 +198,17 @@ const unsigned char* OctreePacketData::getFinalizedData() { int OctreePacketData::getFinalizedSize() { if (!_enableCompression) { - return _bytesInUse; + return _bytesInUse; } if (_dirty) { if (_debug) { qCDebug(octree, "getFinalizedSize() _compressedBytes=%d _bytesInUse=%d",_compressedBytes, _bytesInUse); } - compressContent(); + compressContent(); } - return _compressedBytes; + return _compressedBytes; } @@ -297,14 +297,6 @@ bool OctreePacketData::appendValue(const nodeColor& color) { return appendColor(color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX]); } -bool OctreePacketData::appendValue(const xColor& color) { - return appendColor(color.red, color.green, color.blue); -} - -bool OctreePacketData::appendValue(const rgbColor& color) { - return appendColor(color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX]); -} - bool OctreePacketData::appendColor(colorPart red, colorPart green, colorPart blue) { // eventually we can make this use a dictionary... bool success = false; @@ -371,7 +363,6 @@ bool OctreePacketData::appendValue(quint64 value) { } bool OctreePacketData::appendValue(float value) { - const unsigned char* data = (const unsigned char*)&value; int length = sizeof(value); bool success = append(data, length); @@ -384,7 +375,7 @@ bool OctreePacketData::appendValue(float value) { bool OctreePacketData::appendValue(const glm::vec2& value) { const unsigned char* data = (const unsigned char*)&value; - int length = sizeof(value); + int length = sizeof(glm::vec2); bool success = append(data, length); if (success) { _bytesOfValues += length; @@ -395,7 +386,7 @@ bool OctreePacketData::appendValue(const glm::vec2& value) { bool OctreePacketData::appendValue(const glm::vec3& value) { const unsigned char* data = (const unsigned char*)&value; - int length = sizeof(value); + int length = sizeof(glm::vec3); bool success = append(data, length); if (success) { _bytesOfValues += length; @@ -404,6 +395,10 @@ bool OctreePacketData::appendValue(const glm::vec3& value) { return success; } +bool OctreePacketData::appendValue(const glm::u8vec3& color) { + return appendColor(color.x, color.y, color.z); +} + bool OctreePacketData::appendValue(const QVector& value) { uint16_t qVecSize = value.size(); bool success = appendValue(qVecSize); @@ -604,6 +599,9 @@ bool OctreePacketData::compressContent() { memcpy(_compressed, compressedData.constData(), _compressedBytes); _dirty = false; success = true; + } else { + qCWarning(octree) << "OctreePacketData::compressContent -- compressedData.size >= MAX_OCTREE_PACKET_DATA_SIZE"; + assert(false); } return success; } @@ -623,11 +621,16 @@ void OctreePacketData::loadFinalizedContent(const unsigned char* data, int lengt memcpy(compressedData.data(), data, _compressedBytes); QByteArray uncompressedData = qUncompress(compressedData); - if (uncompressedData.size() <= _bytesAvailable) { - _bytesInUse = uncompressedData.size(); - _bytesAvailable -= uncompressedData.size(); - memcpy(_uncompressed, uncompressedData.constData(), _bytesInUse); + if (uncompressedData.size() > _bytesAvailable) { + int moreNeeded = uncompressedData.size() - _bytesAvailable; + _uncompressedByteArray.resize(_uncompressedByteArray.size() + moreNeeded); + _uncompressed = (unsigned char*)_uncompressedByteArray.data(); + _bytesAvailable += moreNeeded; } + + _bytesInUse = uncompressedData.size(); + _bytesAvailable -= uncompressedData.size(); + memcpy(_uncompressed, uncompressedData.constData(), _bytesInUse); } else { memcpy(_uncompressed, data, length); memcpy(_compressed, data, length); @@ -673,6 +676,21 @@ void OctreePacketData::debugBytes() { qCDebug(octree) << " _bytesReserved=" << _bytesReserved; } +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); @@ -695,14 +713,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QUuid& return sizeof(length) + length; } -int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, xColor& result) { - result.red = dataBytes[RED_INDEX]; - result.green = dataBytes[GREEN_INDEX]; - result.blue = dataBytes[BLUE_INDEX]; - return sizeof(rgbColor); -} - - int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVector& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(uint16_t)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 8d8f599fea..46726d83a6 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -143,12 +143,6 @@ public: /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const nodeColor& color); - /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet - bool appendValue(const xColor& color); - - /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet - bool appendValue(const rgbColor& color); - /// appends a unsigned 8 bit int to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(uint8_t value); @@ -170,6 +164,9 @@ public: /// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::vec3& value); + /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const glm::u8vec3& value); + /// appends a QVector of vec3s to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QVector& value); @@ -253,20 +250,19 @@ public: static quint64 getTotalBytesOfColor() { return _totalBytesOfColor; } /// total bytes of color static int unpackDataFromBytes(const unsigned char* dataBytes, float& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, bool& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, quint64& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint32_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint16_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint8_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, MaterialMappingMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result); - static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& 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); diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index b716d5c0d1..8fd6d4eada 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -701,7 +701,7 @@ void CharacterController::updateState() { const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = _scaleFactor * DEFAULT_AVATAR_MIN_HOVER_HEIGHT; - const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; + const quint64 JUMP_TO_HOVER_PERIOD = _scaleFactor < 1.0f ? _scaleFactor * 1100 * MSECS_PER_SECOND : 1100 * MSECS_PER_SECOND; // scan for distant floor // rayStart is at center of bottom sphere @@ -766,14 +766,16 @@ void CharacterController::updateState() { SET_STATE(State::InAir, "takeoff done"); // compute jumpSpeed based on the scaled jump height for the default avatar in default gravity. - float jumpSpeed = sqrtf(2.0f * DEFAULT_AVATAR_GRAVITY * _scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT); + const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); + const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); velocity += jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } break; case State::InAir: { - const float JUMP_SPEED = _scaleFactor * DEFAULT_AVATAR_JUMP_SPEED; - if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { + const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT); + const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight); + if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); } else if (_flyingAllowed) { btVector3 desiredVelocity = _targetVelocity; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8162bf4e18..c920665279 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -502,36 +502,18 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s properties.setVelocity(linearVelocity); properties.setAcceleration(_entity->getAcceleration()); properties.setAngularVelocity(angularVelocity); - if (_entity->dynamicDataNeedsTransmit()) { - _entity->setDynamicDataNeedsTransmit(false); - properties.setActionData(_entity->getDynamicData()); - } - - if (_entity->updateQueryAACube()) { - // due to parenting, the server may not know where something is in world-space, so include the bounding cube. - properties.setQueryAACube(_entity->getQueryAACube()); - } - - // set the LastEdited of the properties but NOT the entity itself - quint64 now = usecTimestampNow(); - properties.setLastEdited(now); // we don't own the simulation for this entity yet, but we're sending a bid for it + quint64 now = usecTimestampNow(); uint8_t finalBidPriority = computeFinalBidPriority(); - _entity->clearScriptSimulationPriority(); - properties.setSimulationOwner(Physics::getSessionUUID(), finalBidPriority); - _entity->setPendingOwnershipPriority(finalBidPriority); + _entity->prepareForSimulationOwnershipBid(properties, now, finalBidPriority); EntityTreeElementPointer element = _entity->getElement(); EntityTreePointer tree = element ? element->getTree() : nullptr; - properties.setClientOnly(_entity->getClientOnly()); - properties.setOwningAvatarID(_entity->getOwningAvatarID()); - EntityItemID id(_entity->getID()); EntityEditPacketSender* entityPacketSender = static_cast(packetSender); entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties); - _entity->setLastBroadcast(now); // for debug/physics status icons // NOTE: we don't descend to children for ownership bid. Instead, if we win ownership of the parent // then in sendUpdate() we'll walk descendents and send updates for their QueryAACubes if necessary. diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index c814140930..acfb0c9236 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -305,15 +305,16 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* } return true; } - } - if (_shape == newShape) { - // the shape didn't actually change, so we clear the DIRTY_SHAPE flag - flags &= ~Simulation::DIRTY_SHAPE; - // and clear the reference we just created - getShapeManager()->releaseShape(_shape); } else { - _body->setCollisionShape(const_cast(newShape)); - setShape(newShape); + if (_shape == newShape) { + // the shape didn't actually change, so we clear the DIRTY_SHAPE flag + flags &= ~Simulation::DIRTY_SHAPE; + // and clear the reference we just created + getShapeManager()->releaseShape(_shape); + } else { + _body->setCollisionShape(const_cast(newShape)); + setShape(newShape); + } } } if (flags & EASY_DIRTY_PHYSICS_FLAGS) { diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 8343919ae1..bf6e2463e5 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -289,6 +289,12 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) bumpAndPruneContacts(object); btRigidBody* body = object->getRigidBody(); if (body) { + if (body->isStaticObject() && _activeStaticBodies.size() > 0) { + std::set::iterator itr = _activeStaticBodies.find(body); + if (itr != _activeStaticBodies.end()) { + _activeStaticBodies.erase(itr); + } + } removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 8057eb0e0c..d7ba2f0661 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -307,21 +307,21 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; - float height = 2.0f * halfExtents.y; + float height = 2.0f * (halfExtents.y - radius); shape = new btCapsuleShape(radius, height); } break; case SHAPE_TYPE_CAPSULE_X: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.y; - float height = 2.0f * halfExtents.x; + float height = 2.0f * (halfExtents.x - radius); shape = new btCapsuleShapeX(radius, height); } break; case SHAPE_TYPE_CAPSULE_Z: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; - float height = 2.0f * halfExtents.z; + float height = 2.0f * (halfExtents.z - radius); shape = new btCapsuleShapeZ(radius, height); } break; diff --git a/libraries/plugins/src/plugins/InputPlugin.h b/libraries/plugins/src/plugins/InputPlugin.h index 344f07d64c..23fbb6cac6 100644 --- a/libraries/plugins/src/plugins/InputPlugin.h +++ b/libraries/plugins/src/plugins/InputPlugin.h @@ -19,7 +19,6 @@ namespace controller { class InputPlugin : public Plugin { public: - virtual ~InputPlugin() {} virtual void pluginFocusOutEvent() = 0; virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) = 0; diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index bde38575ee..9ca0f14c5f 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -109,7 +109,7 @@ class PickResult { public: PickResult() {} PickResult(const QVariantMap& pickVariant) : pickVariant(pickVariant) {} - virtual ~PickResult() {} + virtual ~PickResult() = default; virtual QVariantMap toVariantMap() const { return pickVariant; @@ -135,6 +135,7 @@ class PickQuery : protected ReadWriteLockable { Q_GADGET public: PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled); + virtual ~PickQuery() = default; /**jsdoc * Enum for different types of Picks and Pointers. @@ -145,9 +146,11 @@ public: * @hifi-interface * @hifi-client-entity * - * @property {number} Ray Ray Picks intersect a ray with the nearest object in front of them, along a given direction. - * @property {number} Stylus Stylus Picks provide "tapping" functionality on/into flat surfaces. - * @property {number} Parabola Parabola Picks intersect a parabola with the nearest object in front of them, with a given initial velocity and acceleration. + * @property {number} Ray Ray picks intersect a ray with the nearest object in front of them, along a given direction. + * @property {number} Stylus Stylus picks provide "tapping" functionality on/into flat surfaces. + * @property {number} Parabola Parabola picks intersect a parabola with the nearest object in front of them, with a given + * initial velocity and acceleration. + * @property {number} Collision Collision picks intersect a collision volume with avatars and entities that have collisions. */ /**jsdoc * @@ -158,6 +161,7 @@ public: * * * + * * *
{@link PickType(0)|PickType.Ray}
{@link PickType(0)|PickType.Stylus}
{@link PickType(0)|PickType.Parabola}
{@link PickType(0)|PickType.Collision}
* @typedef {number} PickType @@ -245,7 +249,6 @@ template class Pick : public PickQuery { public: Pick(const T& mathPick, const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled), _mathPick(mathPick) {} - virtual ~Pick() {} virtual T getMathematicalPick() const = 0; virtual PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const = 0; diff --git a/libraries/pointers/src/PickManager.cpp b/libraries/pointers/src/PickManager.cpp index caa62f3397..d3326ea8b4 100644 --- a/libraries/pointers/src/PickManager.cpp +++ b/libraries/pointers/src/PickManager.cpp @@ -38,11 +38,11 @@ std::shared_ptr PickManager::findPick(unsigned int uid) const { void PickManager::removePick(unsigned int uid) { withWriteLock([&] { - auto type = _typeMap.find(uid); - if (type != _typeMap.end()) { - _picks[type->second].erase(uid); - _typeMap.erase(uid); - _totalPickCounts[type->second]--; + auto typeIt = _typeMap.find(uid); + if (typeIt != _typeMap.end()) { + _picks[typeIt->second].erase(uid); + _totalPickCounts[typeIt->second]--; + _typeMap.erase(typeIt); } }); } diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 79c0b31dff..1c082ed250 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -27,7 +27,7 @@ Q_LOGGING_CATEGORY(proceduralLog, "hifi.gpu.procedural") -// Userdata parsing constants +// User-data parsing constants static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity"; static const QString URL_KEY = "shaderUrl"; static const QString VERSION_KEY = "version"; @@ -39,10 +39,7 @@ static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK"; static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION"; bool operator==(const ProceduralData& a, const ProceduralData& b) { - return ( - (a.version == b.version) && - (a.shaderUrl == b.shaderUrl) && - (a.uniforms == b.uniforms) && + return ((a.version == b.version) && (a.shaderUrl == b.shaderUrl) && (a.uniforms == b.uniforms) && (a.channels == b.channels)); } @@ -108,7 +105,9 @@ Procedural::Procedural() { _transparentState->setCullMode(gpu::State::CULL_NONE); _transparentState->setDepthTest(true, true, gpu::LESS_EQUAL); _transparentState->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + _standardInputsBuffer = std::make_shared(sizeof(StandardInputs), nullptr); } void Procedural::setProceduralData(const ProceduralData& proceduralData) { @@ -119,7 +118,7 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) { _dirty = true; _enabled = false; - if (proceduralData.version != _data.version ) { + if (proceduralData.version != _data.version) { _data.version = proceduralData.version; _shaderDirty = true; } @@ -144,7 +143,6 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) { _channels[channel] = textureCache->getTexture(QUrl()); } } - _channelsDirty = true; } if (proceduralData.shaderUrl != _data.shaderUrl) { @@ -212,28 +210,11 @@ bool Procedural::isReady() const { return true; } -std::string Procedural::replaceProceduralBlock(const std::string& fragmentSource) { - std::string result = fragmentSource; - auto replaceIndex = result.find(PROCEDURAL_VERSION); - if (replaceIndex != std::string::npos) { - if (_data.version == 1) { - result.replace(replaceIndex, PROCEDURAL_VERSION.size(), "#define PROCEDURAL_V1 1"); - } else if (_data.version == 2) { - result.replace(replaceIndex, PROCEDURAL_VERSION.size(), "#define PROCEDURAL_V2 1"); - } - } - replaceIndex = result.find(PROCEDURAL_BLOCK); - if (replaceIndex != std::string::npos) { - result.replace(replaceIndex, PROCEDURAL_BLOCK.size(), _shaderSource.toLocal8Bit().data()); - } - return result; -} - void Procedural::prepare(gpu::Batch& batch, - const glm::vec3& position, - const glm::vec3& size, - const glm::quat& orientation, - const glm::vec4& color) { + const glm::vec3& position, + const glm::vec3& size, + const glm::quat& orientation, + const glm::vec4& color) { _entityDimensions = size; _entityPosition = position; _entityOrientation = glm::mat3_cast(orientation); @@ -256,19 +237,21 @@ void Procedural::prepare(gpu::Batch& batch, } // Build the fragment shader - std::string opaqueShaderSource = replaceProceduralBlock(_opaquefragmentSource.getCode()); - auto opaqueReflection = _opaquefragmentSource.getReflection(); - auto& opaqueUniforms = opaqueReflection[gpu::Shader::BindingType::UNIFORM]; - std::string transparentShaderSource = replaceProceduralBlock(_transparentfragmentSource.getCode()); - auto transparentReflection = _transparentfragmentSource.getReflection(); - auto& transparentUniforms = transparentReflection[gpu::Shader::BindingType::UNIFORM]; + _opaqueFragmentSource.replacements.clear(); + if (_data.version == 1) { + _opaqueFragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V1 1"; + } else if (_data.version == 2) { + _opaqueFragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V2 1"; + } + _opaqueFragmentSource.replacements[PROCEDURAL_BLOCK] = _shaderSource.toStdString(); + _transparentFragmentSource.replacements = _opaqueFragmentSource.replacements; // Set any userdata specified uniforms int customSlot = procedural::slot::uniform::Custom; for (const auto& key : _data.uniforms.keys()) { std::string uniformName = key.toLocal8Bit().data(); - opaqueUniforms[uniformName] = customSlot; - transparentUniforms[uniformName] = customSlot; + _opaqueFragmentSource.reflection.uniforms[uniformName] = customSlot; + _transparentFragmentSource.reflection.uniforms[uniformName] = customSlot; ++customSlot; } @@ -276,18 +259,18 @@ void Procedural::prepare(gpu::Batch& batch, // qCDebug(procedural) << "FragmentShader:\n" << fragmentShaderSource.c_str(); // TODO: THis is a simple fix, we need a cleaner way to provide the "hosting" program for procedural custom shaders to be defined together with the required bindings. - _opaqueFragmentShader = gpu::Shader::createPixel({ opaqueShaderSource, opaqueReflection }); + _opaqueFragmentShader = gpu::Shader::createPixel(_opaqueFragmentSource); _opaqueShader = gpu::Shader::createProgram(_vertexShader, _opaqueFragmentShader); - if (!transparentShaderSource.empty() && transparentShaderSource != opaqueShaderSource) { - _transparentFragmentShader = gpu::Shader::createPixel({ transparentShaderSource, transparentReflection }); + _opaquePipeline = gpu::Pipeline::create(_opaqueShader, _opaqueState); + if (_transparentFragmentSource.valid()) { + _transparentFragmentShader = gpu::Shader::createPixel(_transparentFragmentSource); _transparentShader = gpu::Shader::createProgram(_vertexShader, _transparentFragmentShader); + _transparentPipeline = gpu::Pipeline::create(_transparentShader, _transparentState); } else { _transparentFragmentShader = _opaqueFragmentShader; _transparentShader = _opaqueShader; + _transparentPipeline = _opaquePipeline; } - - _opaquePipeline = gpu::Pipeline::create(_opaqueShader, _opaqueState); - _transparentPipeline = gpu::Pipeline::create(_transparentShader, _transparentState); _start = usecTimestampNow(); _frameCount = 0; } @@ -299,12 +282,8 @@ void Procedural::prepare(gpu::Batch& batch, setupUniforms(transparent); } - if (_shaderDirty || _uniformsDirty || _channelsDirty || _prevTransparent != transparent) { - setupChannels(_shaderDirty || _uniformsDirty, transparent); - } - _prevTransparent = transparent; - _shaderDirty = _uniformsDirty = _channelsDirty = false; + _shaderDirty = _uniformsDirty = false; for (auto lambda : _uniforms) { lambda(batch); @@ -329,18 +308,13 @@ void Procedural::prepare(gpu::Batch& batch, } } + void Procedural::setupUniforms(bool transparent) { _uniforms.clear(); - auto& pipeline = transparent ? _transparentShader : _opaqueShader; - const auto& uniformSlots = pipeline->getUniforms(); auto customUniformCount = _data.uniforms.keys().size(); - // Set any userdata specified uniforms for (int i = 0; i < customUniformCount; ++i) { int slot = procedural::slot::uniform::Custom + i; - if (!uniformSlots.isValid(slot)) { - continue; - } QString key = _data.uniforms.keys().at(i); std::string uniformName = key.toLocal8Bit().data(); QJsonValue value = _data.uniforms[key]; @@ -350,113 +324,80 @@ void Procedural::setupUniforms(bool transparent) { } else if (value.isArray()) { auto valueArray = value.toArray(); switch (valueArray.size()) { - case 0: - break; + case 0: + break; - case 1: { - float v = valueArray[0].toDouble(); - _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform1f(slot, v); }); - break; - } + case 1: { + float v = valueArray[0].toDouble(); + _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform1f(slot, v); }); + break; + } - case 2: { - glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() }; - _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform2f(slot, v.x, v.y); }); - break; - } + case 2: { + glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() }; + _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform2f(slot, v.x, v.y); }); + break; + } - case 3: { - glm::vec3 v{ - valueArray[0].toDouble(), - valueArray[1].toDouble(), - valueArray[2].toDouble(), - }; - _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform3f(slot, v.x, v.y, v.z); }); - break; - } + case 3: { + glm::vec3 v{ + valueArray[0].toDouble(), + valueArray[1].toDouble(), + valueArray[2].toDouble(), + }; + _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform3f(slot, v.x, v.y, v.z); }); + break; + } - default: - case 4: { - glm::vec4 v{ - valueArray[0].toDouble(), - valueArray[1].toDouble(), - valueArray[2].toDouble(), - valueArray[3].toDouble(), - }; - _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform4f(slot, v.x, v.y, v.z, v.w); }); - break; - } + default: + case 4: { + glm::vec4 v{ + valueArray[0].toDouble(), + valueArray[1].toDouble(), + valueArray[2].toDouble(), + valueArray[3].toDouble(), + }; + _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform4f(slot, v.x, v.y, v.z, v.w); }); + break; + } } } } - if (uniformSlots.isValid(procedural::slot::uniform::Time)) { - _uniforms.push_back([=](gpu::Batch& batch) { - // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds - float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND; - batch._glUniform(procedural::slot::uniform::Time, time); - }); - } + _uniforms.push_back([=](gpu::Batch& batch) { + _standardInputs.position = vec4(_entityPosition, 1.0f); + // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds + _standardInputs.time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND; - if (uniformSlots.isValid(procedural::slot::uniform::Date)) { - _uniforms.push_back([=](gpu::Batch& batch) { + // Date + { QDateTime now = QDateTime::currentDateTimeUtc(); QDate date = now.date(); QTime time = now.time(); - vec4 v; - v.x = date.year(); + _standardInputs.date.x = date.year(); // Shadertoy month is 0 based - v.y = date.month() - 1; + _standardInputs.date.y = date.month() - 1; // But not the day... go figure - v.z = date.day(); + _standardInputs.date.z = date.day(); float fractSeconds = (time.msec() / 1000.0f); - v.w = (time.hour() * 3600) + (time.minute() * 60) + time.second() + fractSeconds; - batch._glUniform(procedural::slot::uniform::Date, v); - }); - } - - if (uniformSlots.isValid(procedural::slot::uniform::FrameCount)) { - _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform(procedural::slot::uniform::FrameCount, ++_frameCount); }); - } - - if (uniformSlots.isValid(procedural::slot::uniform::Scale)) { - // FIXME move into the 'set once' section, since this doesn't change over time - _uniforms.push_back([=](gpu::Batch& batch) { batch._glUniform(procedural::slot::uniform::Scale, _entityDimensions); }); - } - - if (uniformSlots.isValid(procedural::slot::uniform::Orientation)) { - // FIXME move into the 'set once' section, since this doesn't change over time - _uniforms.push_back( - [=](gpu::Batch& batch) { batch._glUniform(procedural::slot::uniform::Orientation, _entityOrientation); }); - } - - if (uniformSlots.isValid(procedural::slot::uniform::Position)) { - // FIXME move into the 'set once' section, since this doesn't change over time - _uniforms.push_back( - [=](gpu::Batch& batch) { batch._glUniform(procedural::slot::uniform::Orientation, _entityPosition); }); - } -} - -void Procedural::setupChannels(bool shouldCreate, bool transparent) { - auto& pipeline = transparent ? _transparentShader : _opaqueShader; - const auto& uniformSlots = pipeline->getUniforms(); - - if (uniformSlots.isValid(procedural::slot::uniform::ChannelResolution)) { - if (!shouldCreate) { - // Instead of modifying the last element, just remove and recreate it. - _uniforms.pop_back(); + _standardInputs.date.w = (time.hour() * 3600) + (time.minute() * 60) + time.second() + fractSeconds; } - _uniforms.push_back([=](gpu::Batch& batch) { - vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS]; - for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) { - if (_channels[i]) { - channelSizes[i] = vec3(_channels[i]->getWidth(), _channels[i]->getHeight(), 1.0); - } + + _standardInputs.scale = vec4(_entityDimensions, 1.0f); + _standardInputs.frameCount = ++_frameCount; + _standardInputs.orientation = mat4(_entityOrientation); + + for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) { + if (_channels[i]) { + _standardInputs.resolution[i] = vec4(_channels[i]->getWidth(), _channels[i]->getHeight(), 1.0f, 1.0f); + } else { + _standardInputs.resolution[i] = vec4(1.0f); } - batch._glUniform3fv(procedural::slot::uniform::ChannelResolution, MAX_PROCEDURAL_TEXTURE_CHANNELS, - &channelSizes[0].x); - }); - } + } + + _standardInputsBuffer->setSubData(0, _standardInputs); + batch.setUniformBuffer(0, _standardInputsBuffer, 0, sizeof(StandardInputs)); + }); } glm::vec4 Procedural::getColor(const glm::vec4& entityColor) { diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index 973b323f60..781ac25249 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -65,14 +65,37 @@ public: void setDoesFade(bool doesFade) { _doesFade = doesFade; } gpu::Shader::Source _vertexSource; - gpu::Shader::Source _opaquefragmentSource; - gpu::Shader::Source _transparentfragmentSource; + gpu::Shader::Source _opaqueFragmentSource; + gpu::Shader::Source _transparentFragmentSource; gpu::StatePointer _opaqueState { std::make_shared() }; gpu::StatePointer _transparentState { std::make_shared() }; protected: + // DO NOT TOUCH + // We have to pack these in a particular way to match the ProceduralCommon.slh + // layout. + struct StandardInputs { + vec4 date; + vec4 position; + vec4 scale; + float time; + int frameCount; + vec2 _spare1; + vec4 resolution[4]; + mat4 orientation; + }; + + static_assert(0 == offsetof(StandardInputs, date), "ProceduralOffsets"); + static_assert(16 == offsetof(StandardInputs, position), "ProceduralOffsets"); + static_assert(32 == offsetof(StandardInputs, scale), "ProceduralOffsets"); + static_assert(48 == offsetof(StandardInputs, time), "ProceduralOffsets"); + static_assert(52 == offsetof(StandardInputs, frameCount), "ProceduralOffsets"); + static_assert(56 == offsetof(StandardInputs, _spare1), "ProceduralOffsets"); + static_assert(64 == offsetof(StandardInputs, resolution), "ProceduralOffsets"); + static_assert(128 == offsetof(StandardInputs, orientation), "ProceduralOffsets"); + // Procedural metadata ProceduralData _data; @@ -88,13 +111,14 @@ protected: bool _dirty { false }; bool _shaderDirty { true }; bool _uniformsDirty { true }; - bool _channelsDirty { true }; // Rendering objects UniformLambdas _uniforms; NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; gpu::PipelinePointer _opaquePipeline; gpu::PipelinePointer _transparentPipeline; + StandardInputs _standardInputs; + gpu::BufferPointer _standardInputsBuffer; gpu::ShaderPointer _vertexShader; gpu::ShaderPointer _opaqueFragmentShader; gpu::ShaderPointer _transparentFragmentShader; @@ -109,9 +133,6 @@ protected: private: // This should only be called from the render thread, as it shares data with Procedural::prepare void setupUniforms(bool transparent); - void setupChannels(bool shouldCreate, bool transparent); - - std::string replaceProceduralBlock(const std::string& fragmentSource); mutable quint64 _fadeStartTime { 0 }; mutable bool _hasStartedFade { false }; diff --git a/libraries/procedural/src/procedural/ProceduralCommon.slh b/libraries/procedural/src/procedural/ProceduralCommon.slh index c36f2da1d3..64a4fdd1f9 100644 --- a/libraries/procedural/src/procedural/ProceduralCommon.slh +++ b/libraries/procedural/src/procedural/ProceduralCommon.slh @@ -14,18 +14,48 @@ <$declareStandardCameraTransform()$> -#define PROCEDURAL 1 +LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL0) uniform sampler2D iChannel0; +LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL1) uniform sampler2D iChannel1; +LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL2) uniform sampler2D iChannel2; +LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL3) uniform sampler2D iChannel3; -//PROCEDURAL_VERSION +// DO NOT TOUCH +// This block does not follow our normal rules of always using a struct and +// always using 16 byte aligned types like vec4 and mat4 +// +// This is because this block must be EXACTLY how it is in order to maintain +// comptability with existing procedural shaders that previously relied on these +// inputs as uniforms, not members of a UBO -#ifdef PROCEDURAL_V1 +LAYOUT_STD140(binding=0) uniform standardInputsBuffer { + // Offset 0 + vec4 date; + // Offset 16, acts as vec4 for alignment purposes + vec3 worldPosition; + // Offset 32, acts as vec4 for alignment purposes + vec3 worldScale; + // Offset 48 + float globalTime; + // Offset 52 + int frameCount; + // Offset 56 + vec2 _spare1; + // Offset 64, acts as vec4[4] for alignment purposes + vec3 channelResolution[4]; + // Offset 128, acts as vec4[3] for alignment purposes + // Also, each individual component is aligned as a vec4 + mat3 worldOrientation; + // Offset 176 + vec4 _spare2; +} standardInputs; -// shader playback time (in seconds) -layout(location=PROCEDURAL_UNIFORM_TIME) uniform float iGlobalTime; -// the dimensions of the object being rendered -layout(location=PROCEDURAL_UNIFORM_SCALE) uniform vec3 iWorldScale; - -#else +#define iDate standardInputs.date +#define iWorldPosition standardInputs.worldPosition +#define iWorldScale standardInputs.worldScale +#define iGlobalTime standardInputs.globalTime +#define iFrameCount standardInputs.frameCount +#define iChannelResolution standardInputs.channelResolution +#define iWorldOrientation standardInputs.worldOrientation // Unimplemented uniforms // Resolution doesn't make sense in the VR context @@ -37,20 +67,9 @@ const float iSampleRate = 1.0; // No support for video input const vec4 iChannelTime = vec4(0.0); +#define PROCEDURAL 1 -layout(location=PROCEDURAL_UNIFORM_TIME) uniform float iGlobalTime; // shader playback time (in seconds) -layout(location=PROCEDURAL_UNIFORM_DATE) uniform vec4 iDate; -layout(location=PROCEDURAL_UNIFORM_FRAME_COUNT) uniform int iFrameCount; -layout(location=PROCEDURAL_UNIFORM_POSITION) uniform vec3 iWorldPosition; // the position of the object being rendered -layout(location=PROCEDURAL_UNIFORM_SCALE) uniform vec3 iWorldScale; // the dimensions of the object being rendered -layout(location=PROCEDURAL_UNIFORM_ORIENTATION) uniform mat3 iWorldOrientation; // the orientation of the object being rendered -layout(location=PROCEDURAL_UNIFORM_CHANNEL_RESOLUTION) uniform vec3 iChannelResolution[4]; -layout(binding=PROCEDURAL_TEXTURE_CHANNEL0) uniform sampler2D iChannel0; -layout(binding=PROCEDURAL_TEXTURE_CHANNEL1) uniform sampler2D iChannel1; -layout(binding=PROCEDURAL_TEXTURE_CHANNEL2) uniform sampler2D iChannel2; -layout(binding=PROCEDURAL_TEXTURE_CHANNEL3) uniform sampler2D iChannel3; - -#endif +//PROCEDURAL_VERSION // hack comment for extra whitespace diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 0addb57fcf..ea5be23eb8 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -19,7 +19,7 @@ ProceduralSkybox::ProceduralSkybox() : graphics::Skybox() { _procedural._vertexSource = gpu::Shader::createVertex(shader::graphics::vertex::skybox)->getSource(); - _procedural._opaquefragmentSource = gpu::Shader::createPixel(shader::procedural::fragment::proceduralSkybox)->getSource(); + _procedural._opaqueFragmentSource = shader::Source::get(shader::procedural::fragment::proceduralSkybox); // Adjust the pipeline state for background using the stencil test _procedural.setDoesFade(false); // Must match PrepareStencil::STENCIL_BACKGROUND diff --git a/libraries/procedural/src/procedural/ShaderConstants.h b/libraries/procedural/src/procedural/ShaderConstants.h index bfbf2a2691..cd0d997050 100644 --- a/libraries/procedural/src/procedural/ShaderConstants.h +++ b/libraries/procedural/src/procedural/ShaderConstants.h @@ -14,14 +14,6 @@ #ifndef PROCEDURAL_SHADER_CONSTANTS_H #define PROCEDURAL_SHADER_CONSTANTS_H -#define PROCEDURAL_UNIFORM_TIME 200 -#define PROCEDURAL_UNIFORM_DATE 201 -#define PROCEDURAL_UNIFORM_FRAME_COUNT 202 -#define PROCEDURAL_UNIFORM_POSITION 203 -#define PROCEDURAL_UNIFORM_SCALE 204 -#define PROCEDURAL_UNIFORM_ORIENTATION 205 -// Additional space because orientation will take up 3-4 locations, being a matrix -#define PROCEDURAL_UNIFORM_CHANNEL_RESOLUTION 209 #define PROCEDURAL_UNIFORM_CUSTOM 220 #define PROCEDURAL_TEXTURE_CHANNEL0 0 @@ -33,15 +25,9 @@ namespace procedural { namespace slot { + namespace uniform { enum Uniform { - Time = PROCEDURAL_UNIFORM_TIME, - Date = PROCEDURAL_UNIFORM_DATE, - FrameCount = PROCEDURAL_UNIFORM_FRAME_COUNT, - Position = PROCEDURAL_UNIFORM_POSITION, - Scale = PROCEDURAL_UNIFORM_SCALE, - Orientation = PROCEDURAL_UNIFORM_ORIENTATION, - ChannelResolution = PROCEDURAL_UNIFORM_CHANNEL_RESOLUTION, Custom = PROCEDURAL_UNIFORM_CUSTOM, }; } diff --git a/libraries/procedural/src/procedural/proceduralSkybox.slf b/libraries/procedural/src/procedural/proceduralSkybox.slf index e18b7abef6..12e8de9dc3 100644 --- a/libraries/procedural/src/procedural/proceduralSkybox.slf +++ b/libraries/procedural/src/procedural/proceduralSkybox.slf @@ -12,13 +12,13 @@ // <@include graphics/ShaderConstants.h@> -layout(binding=GRAPHICS_TEXTURE_SKYBOX) uniform samplerCube cubeMap; +LAYOUT(binding=GRAPHICS_TEXTURE_SKYBOX) uniform samplerCube cubeMap; struct Skybox { vec4 color; }; -layout(binding=GRAPHICS_BUFFER_SKYBOX_PARAMS) uniform skyboxBuffer { +LAYOUT(binding=GRAPHICS_BUFFER_SKYBOX_PARAMS) uniform skyboxBuffer { Skybox skybox; }; @@ -28,9 +28,13 @@ layout(location=0) out vec4 _fragColor; <@include procedural/ProceduralCommon.slh@> #line 1001 -//PROCEDURAL_BLOCK +//PROCEDURAL_BLOCK_BEGIN +vec3 getSkyboxColor() { + return vec3(abs(sin(iGlobalTime / 5.0)), 1.0, 0.0); +} +//PROCEDURAL_BLOCK_END -#line 2033 +#line 2038 void main(void) { vec3 color = getSkyboxColor(); // Protect from NaNs and negative values diff --git a/libraries/procedural/src/procedural/proceduralSkybox.slp b/libraries/procedural/src/procedural/proceduralSkybox.slp new file mode 100644 index 0000000000..5247547850 --- /dev/null +++ b/libraries/procedural/src/procedural/proceduralSkybox.slp @@ -0,0 +1 @@ +VERTEX graphics::vertex::skybox \ No newline at end of file diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp index 39f3123d40..46fc3490a7 100644 --- a/libraries/qml/src/qml/impl/RenderEventHandler.cpp +++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -114,6 +115,7 @@ void RenderEventHandler::onRender() { PROFILE_RANGE(render_qml_gl, __FUNCTION__); + gl::globalLock(); if (!_shared->preRender()) { return; } @@ -139,11 +141,12 @@ void RenderEventHandler::onRender() { glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + // Fence will be used in another thread / context, so a flush is required glFlush(); _shared->updateTextureAndFence({ texture, fence }); - // Fence will be used in another thread / context, so a flush is required _shared->_quickWindow->resetOpenGLState(); } + gl::globalRelease(); } void RenderEventHandler::onQuit() { @@ -167,4 +170,5 @@ void RenderEventHandler::onQuit() { moveToThread(qApp->thread()); QThread::currentThread()->quit(); } -#endif \ No newline at end of file + +#endif diff --git a/libraries/qml/src/qml/impl/TextureCache.cpp b/libraries/qml/src/qml/impl/TextureCache.cpp index 7af8fa1ac9..7b4fb3adaf 100644 --- a/libraries/qml/src/qml/impl/TextureCache.cpp +++ b/libraries/qml/src/qml/impl/TextureCache.cpp @@ -51,8 +51,10 @@ uint32_t TextureCache::acquireTexture(const QSize& size) { if (!textureSet.returnedTextures.empty()) { auto textureAndFence = textureSet.returnedTextures.front(); textureSet.returnedTextures.pop_front(); - glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED); - glDeleteSync((GLsync)textureAndFence.second); + if (textureAndFence.second) { + glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED); + glDeleteSync((GLsync)textureAndFence.second); + } return textureAndFence.first; } return createTexture(size); @@ -101,9 +103,11 @@ void TextureCache::destroyTexture(uint32_t texture) { void TextureCache::destroy(const Value& textureAndFence) { const auto& fence = textureAndFence.second; - // FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context. - glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync((GLsync)fence); + if (fence) { + // FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context. + glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync((GLsync)fence); + } destroyTexture(textureAndFence.first); } diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 74afded28f..bf528ee5f0 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -149,11 +149,11 @@ void AnimDebugDraw::shutdown() { } void AnimDebugDraw::addAbsolutePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) { - _absolutePoses[key] = PosesInfo(skeleton, poses, rootPose, color); + _posesInfoMap[key] = PosesInfo(skeleton, poses, rootPose, color); } void AnimDebugDraw::removeAbsolutePoses(const std::string& key) { - _absolutePoses.erase(key); + _posesInfoMap.erase(key); } static const uint32_t red = toRGBA(255, 0, 0, 255); @@ -320,7 +320,12 @@ void AnimDebugDraw::update() { return; } render::Transaction transaction; - transaction.updateItem(_itemID, [&](AnimDebugDrawData& data) { + + // Make a copy of the _posesInfoMap member variable, and pass the copy into the lambda. + // This allows the body of the lambda, which executes on the render thread, to safely iterate over the map. + std::shared_ptr posesInfoMapCopy; + posesInfoMapCopy = std::make_shared(_posesInfoMap); + transaction.updateItem(_itemID, [posesInfoMapCopy](AnimDebugDrawData& data) { const size_t VERTICES_PER_BONE = (6 + (NUM_CIRCLE_SLICES * 2) * 3); const size_t VERTICES_PER_LINK = 8 * 2; @@ -332,7 +337,7 @@ void AnimDebugDraw::update() { // figure out how many verts we will need. int numVerts = 0; - for (auto& iter : _absolutePoses) { + for (auto& iter : *posesInfoMapCopy) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; for (auto i = 0; i < skeleton->getNumJoints(); i++) { @@ -362,7 +367,7 @@ void AnimDebugDraw::update() { } // draw absolute poses - for (auto& iter : _absolutePoses) { + for (auto& iter : *posesInfoMapCopy) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); AnimPoseVec& absPoses = std::get<1>(iter.second); AnimPose rootPose = std::get<2>(iter.second); @@ -421,7 +426,7 @@ void AnimDebugDraw::update() { data._indexBuffer->resize(sizeof(uint32_t) * numVerts); for (int i = 0; i < numVerts; i++) { - data._indexBuffer->setSubData(i, (uint32_t)i);; + data._indexBuffer->setSubData(i, (uint32_t)i); } }); scene->enqueueTransaction(transaction); diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index 03101c9f86..2333b749fe 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -44,7 +44,8 @@ protected: typedef std::tuple PosesInfo; - std::unordered_map _absolutePoses; + typedef std::unordered_map PosesInfoMap; + PosesInfoMap _posesInfoMap; // no copies AnimDebugDraw(const AnimDebugDraw&) = delete; diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index d9115c7943..ca643b9f14 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -55,85 +55,46 @@ BackgroundStage::BackgroundPointer BackgroundStage::removeBackground(Index index void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { - const auto& lightingModel = inputs; + const auto& lightingModel = inputs.get0(); if (!lightingModel->isBackgroundEnabled()) { return; } // Background rendering decision - auto backgroundStage = renderContext->_scene->getStage(); - assert(backgroundStage); - - graphics::SunSkyStagePointer background; + const auto& backgroundStage = renderContext->_scene->getStage(); + const auto& backgroundFrame = inputs.get1(); graphics::SkyboxPointer skybox; - if (backgroundStage->_currentFrame._backgrounds.size()) { - auto backgroundId = backgroundStage->_currentFrame._backgrounds.front(); - auto background = backgroundStage->getBackground(backgroundId); + if (backgroundStage && backgroundFrame->_backgrounds.size()) { + const auto& background = backgroundStage->getBackground(backgroundFrame->_backgrounds.front()); if (background) { skybox = background->getSkybox(); } } - /* auto backgroundMode = skyStage->getBackgroundMode(); - switch (backgroundMode) { - case graphics::SunSkyStage::SKY_DEFAULT: { - auto scene = DependencyManager::get()->getStage(); - auto sceneKeyLight = scene->getKeyLight(); - - scene->setSunModelEnable(false); - sceneKeyLight->setColor(ColorUtils::toVec3(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR)); - sceneKeyLight->setIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY); - sceneKeyLight->setAmbientIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY); - sceneKeyLight->setDirection(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION); - // fall through: render a skybox (if available), or the defaults (if requested) - } - - case graphics::SunSkyStage::SKY_BOX: {*/ if (skybox && !skybox->empty()) { - PerformanceTimer perfTimer("skybox"); - auto args = renderContext->args; + PerformanceTimer perfTimer("skybox"); + auto args = renderContext->args; - gpu::doInBatch("DrawBackgroundStage::run", args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; + gpu::doInBatch("DrawBackgroundStage::run", args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; - batch.enableSkybox(true); + batch.enableSkybox(true); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); - skybox->render(batch, args->getViewFrustum()); - }); - args->_batch = nullptr; - - // break; - } - // fall through: render defaults (if requested) -// } -/* - case graphics::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: { - if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { - auto scene = DependencyManager::get()->getStage(); - auto sceneKeyLight = scene->getKeyLight(); - auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); - if (defaultSkyboxAmbientTexture) { - sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); - sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); - } - // fall through: render defaults skybox - } else { - break; - } + skybox->render(batch, args->getViewFrustum()); + }); + args->_batch = nullptr; } - */ - } BackgroundStageSetup::BackgroundStageSetup() { diff --git a/libraries/render-utils/src/BackgroundStage.h b/libraries/render-utils/src/BackgroundStage.h index db876b1993..61ca576ca8 100644 --- a/libraries/render-utils/src/BackgroundStage.h +++ b/libraries/render-utils/src/BackgroundStage.h @@ -65,6 +65,7 @@ public: BackgroundStage::BackgroundIndices _backgrounds; }; + using FramePointer = std::shared_ptr; Frame _currentFrame; }; @@ -76,18 +77,16 @@ public: BackgroundStageSetup(); void run(const render::RenderContextPointer& renderContext); - -protected: }; class DrawBackgroundStage { public: - using Inputs = LightingModelPointer; + using Inputs = render::VaryingSet2; using JobModel = render::Job::ModelI; - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + DrawBackgroundStage() {} -protected: + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); }; #endif diff --git a/libraries/render-utils/src/Blendshape.slh b/libraries/render-utils/src/Blendshape.slh index df62af5a77..f2c6bfe794 100644 --- a/libraries/render-utils/src/Blendshape.slh +++ b/libraries/render-utils/src/Blendshape.slh @@ -10,13 +10,13 @@ <@func declareBlendshape(USE_NORMAL, USE_TANGENT)@> -#if defined(GPU_GL410) -layout(binding=0) uniform samplerBuffer blendshapeOffsetsBuffer; +#if !defined(GPU_SSBO_TRANSFORM_OBJECT) +LAYOUT(binding=GPU_RESOURCE_BUFFER_SLOT0_TEXTURE) uniform samplerBuffer blendshapeOffsetsBuffer; uvec4 getPackedBlendshapeOffset(int i) { return floatBitsToUint(texelFetch(blendshapeOffsetsBuffer, i)); } #else -layout(std140, binding=0) buffer blendshapeOffsetsBuffer { +LAYOUT_STD140(binding=GPU_RESOURCE_BUFFER_SLOT0_STORAGE) buffer blendshapeOffsetsBuffer { uvec4 _packedBlendshapeOffsets[]; }; uvec4 getPackedBlendshapeOffset(int i) { diff --git a/libraries/render-utils/src/BloomApply.slf b/libraries/render-utils/src/BloomApply.slf index a53894de60..dcdb989780 100644 --- a/libraries/render-utils/src/BloomApply.slf +++ b/libraries/render-utils/src/BloomApply.slf @@ -12,11 +12,11 @@ <@include BloomApply.shared.slh@> <@include render-utils/ShaderConstants.h@> -layout(binding=0) uniform sampler2D blurMap0; -layout(binding=1) uniform sampler2D blurMap1; -layout(binding=2) uniform sampler2D blurMap2; +LAYOUT(binding=0) uniform sampler2D blurMap0; +LAYOUT(binding=1) uniform sampler2D blurMap1; +LAYOUT(binding=2) uniform sampler2D blurMap2; -layout(std140, binding=RENDER_UTILS_BUFFER_BLOOM_PARAMS) uniform parametersBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_BLOOM_PARAMS) uniform parametersBuffer { Parameters parameters; }; diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index e6217eb825..db86480eea 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -35,7 +35,16 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons const auto frameTransform = inputs.get0(); const auto inputFrameBuffer = inputs.get1(); - const auto bloom = inputs.get2(); + const auto bloomFrame = inputs.get2(); + const auto& bloomStage = renderContext->_scene->getStage(); + graphics::BloomPointer bloom; + if (bloomStage && bloomFrame->_blooms.size()) { + bloom = bloomStage->getBloom(bloomFrame->_blooms.front()); + } + if (!bloom) { + renderContext->taskFlow.abortTask(); + return; + } assert(inputFrameBuffer->hasColor()); @@ -65,11 +74,6 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; - if (!bloom) { - renderContext->taskFlow.abortTask(); - return; - } - _parameters.edit()._threshold = bloom->getBloomThreshold(); gpu::doInBatch("BloomThreshold::run", args->_context, [&](gpu::Batch& batch) { @@ -89,6 +93,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons outputs.edit0() = _outputBuffer; outputs.edit1() = 0.5f + bloom->getBloomSize() * 3.5f; + outputs.edit2() = bloom; } BloomApply::BloomApply() { @@ -296,9 +301,9 @@ void BloomEffect::build(JobModel& task, const render::Varying& inputs, render::V const auto blurFB2 = task.addJob("BloomBlur2", blurInput2); const auto& frameBuffer = inputs.getN(1); - const auto& bloom = inputs.getN(2); // Mix all blur levels at quarter resolution + const auto bloom = bloomOutputs.getN(2); const auto applyInput = BloomApply::Inputs(blurInputBuffer, blurFB0, blurFB1, blurFB2, bloom).asVarying(); task.addJob("BloomApply", applyInput); // And then blend result in additive manner on top of final color buffer diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 12c94bb929..07ae2887c9 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -14,7 +14,7 @@ #include -#include "graphics/Bloom.h" +#include "BloomStage.h" #include "DeferredFrameTransform.h" @@ -28,8 +28,8 @@ class BloomThresholdConfig : public render::Job::Config { class BloomThreshold { public: - using Inputs = render::VaryingSet3; - using Outputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; + using Outputs = render::VaryingSet3; using Config = BloomThresholdConfig; using JobModel = render::Job::ModelIO; @@ -127,7 +127,7 @@ private: class BloomEffect { public: - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet3; using Config = BloomConfig; using JobModel = render::Task::ModelI; diff --git a/libraries/render-utils/src/BloomStage.cpp b/libraries/render-utils/src/BloomStage.cpp index 904e27f5da..b3ba5f9565 100644 --- a/libraries/render-utils/src/BloomStage.cpp +++ b/libraries/render-utils/src/BloomStage.cpp @@ -16,16 +16,6 @@ std::string BloomStage::_stageName { "BLOOM_STAGE"}; const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; -FetchBloomStage::FetchBloomStage() { - _bloom = std::make_shared(); -} - -void FetchBloomStage::configure(const Config& config) { - _bloom->setBloomIntensity(config.bloomIntensity); - _bloom->setBloomThreshold(config.bloomThreshold); - _bloom->setBloomSize(config.bloomSize); -} - BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const { auto found = _bloomMap.find(bloom); if (found != _bloomMap.end()) { @@ -66,14 +56,3 @@ void BloomStageSetup::run(const render::RenderContextPointer& renderContext) { renderContext->_scene->resetStage(BloomStage::getName(), std::make_shared()); } } - -void FetchBloomStage::run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom) { - auto bloomStage = renderContext->_scene->getStage(); - assert(bloomStage); - - bloom = nullptr; - if (bloomStage->_currentFrame._blooms.size() != 0) { - auto bloomId = bloomStage->_currentFrame._blooms.front(); - bloom = bloomStage->getBloom(bloomId); - } -} diff --git a/libraries/render-utils/src/BloomStage.h b/libraries/render-utils/src/BloomStage.h index 27bd97dcab..7ba7e56035 100644 --- a/libraries/render-utils/src/BloomStage.h +++ b/libraries/render-utils/src/BloomStage.h @@ -65,6 +65,7 @@ public: BloomStage::BloomIndices _blooms; }; + using FramePointer = std::shared_ptr; Frame _currentFrame; }; @@ -102,17 +103,4 @@ signals: void dirty(); }; -class FetchBloomStage { -public: - using Config = FetchBloomConfig; - using JobModel = render::Job::ModelO; - - FetchBloomStage(); - - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom); - -private: - graphics::BloomPointer _bloom; -}; #endif diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf index 47a1fb0d9f..bbf863994f 100644 --- a/libraries/render-utils/src/BloomThreshold.slf +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -12,8 +12,8 @@ <@include BloomThreshold.shared.slh@> <@include render-utils/ShaderConstants.h@> -layout(binding=RENDER_UTILS_TEXTURE_BLOOM_COLOR) uniform sampler2D colorMap; -layout(std140, binding=RENDER_UTILS_BUFFER_BLOOM_PARAMS) uniform parametersBuffer { +LAYOUT(binding=RENDER_UTILS_TEXTURE_BLOOM_COLOR) uniform sampler2D colorMap; +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_BLOOM_PARAMS) uniform parametersBuffer { Parameters parameters; }; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 8ea4cdcd60..c31345bc55 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -32,8 +32,8 @@ bool CauterizedModel::updateGeometry() { bool needsFullUpdate = Model::updateGeometry(); if (_isCauterized && needsFullUpdate) { assert(_cauterizeMeshStates.empty()); - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + const HFMModel& hfmModel = getHFMModel(); + foreach (const HFMMesh& mesh, hfmModel.meshes) { Model::MeshState state; if (_useDualQuaternionSkinning) { state.clusterDualQuaternions.resize(mesh.clusters.size()); @@ -76,7 +76,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - const FBXGeometry& fbxGeometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -86,7 +86,7 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(fbxGeometry.meshes[i], i); + initializeBlendshapes(hfmModel.meshes[i], i); auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); @@ -96,7 +96,7 @@ void CauterizedModel::createRenderItemSet() { shapeID++; } } - _blendshapeBuffersInitialized = true; + _blendshapeOffsetsInitialized = true; } else { Model::createRenderItemSet(); } @@ -109,13 +109,13 @@ void CauterizedModel::updateClusterMatrices() { return; } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); @@ -133,7 +133,7 @@ void CauterizedModel::updateClusterMatrices() { // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { - AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex); + AnimPose cauterizePose = _rig.getJointPose(hfmModel.neckJointIndex); cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); static const glm::mat4 zeroScale( @@ -141,14 +141,14 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; + auto cauterizeMatrix = _rig.getJointTransform(hfmModel.neckJointIndex) * zeroScale; for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { @@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 0e61b36280..9597ce1052 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -243,7 +243,7 @@ static const std::string DEFAULT_CUSTOM_SHADER{ " }" }; -static std::string getFileContent(std::string fileName, std::string defaultContent = std::string()) { +static std::string getFileContent(const std::string& fileName, const std::string& defaultContent = std::string()) { QFile customFile(QString::fromStdString(fileName)); if (customFile.open(QIODevice::ReadOnly)) { return customFile.readAll().toStdString(); @@ -270,7 +270,7 @@ DebugDeferredBuffer::~DebugDeferredBuffer() { } } -std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string customFile) { +std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, const std::string& customFile) { switch (mode) { case AlbedoMode: return DEFAULT_ALBEDO_SHADER; @@ -334,7 +334,7 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return std::string(); } -bool DebugDeferredBuffer::pipelineNeedsUpdate(Mode mode, std::string customFile) const { +bool DebugDeferredBuffer::pipelineNeedsUpdate(Mode mode, const std::string& customFile) const { if (mode != CustomMode) { return !_pipelines[mode]; } @@ -351,19 +351,17 @@ bool DebugDeferredBuffer::pipelineNeedsUpdate(Mode mode, std::string customFile) return true; } -const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::string customFile) { +const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, const std::string& customFile) { if (pipelineNeedsUpdate(mode, customFile)) { - static const auto FRAGMENT_SHADER_SOURCE = - gpu::Shader::createPixel(shader::render_utils::fragment::debug_deferred_buffer)->getSource(); - static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; - static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER_SOURCE.getCode().find(SOURCE_PLACEHOLDER); - Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, "Could not find source placeholder"); + static_assert(shader::render_utils::program::debug_deferred_buffer != 0, "Validate debug deferred program"); - auto bakedFragmentShader = FRAGMENT_SHADER_SOURCE.getCode(); - bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), getShaderSourceCode(mode, customFile)); + static const std::string REPLACEMENT_MARKER{ "//SOURCE_PLACEHOLDER" }; + shader::Source resolvedFragmentSource; + resolvedFragmentSource = shader::Source::get(shader::render_utils::fragment::debug_deferred_buffer); + resolvedFragmentSource.replacements[REPLACEMENT_MARKER] = getShaderSourceCode(mode, customFile); const auto vs = gpu::Shader::createVertex(shader::render_utils::vertex::debug_deferred_buffer); - const auto ps = gpu::Shader::createPixel({ bakedFragmentShader, FRAGMENT_SHADER_SOURCE.getReflection() }); + const auto ps = gpu::Shader::createPixel(resolvedFragmentSource); const auto program = gpu::Shader::createProgram(vs, ps); auto pipeline = gpu::Pipeline::create(program, std::make_shared()); @@ -405,6 +403,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto& ambientOcclusionFramebuffer = inputs.get3(); auto& velocityFramebuffer = inputs.get4(); auto& frameTransform = inputs.get5(); + auto& lightFrame = inputs.get6(); gpu::doInBatch("DebugDeferredBuffer::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -443,7 +442,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightStage = renderContext->_scene->getStage(); assert(lightStage); assert(lightStage->getNumLights() > 0); - auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); + auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(*lightFrame); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { batch.setResourceTexture(Textures::Shadow, globalShadow->map); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 9daa8fd530..cdaf5db83a 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -21,6 +21,8 @@ #include "AmbientOcclusionEffect.h" #include "VelocityBufferPass.h" +#include "LightStage.h" + class DebugDeferredBufferConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled) @@ -39,12 +41,13 @@ signals: class DebugDeferredBuffer { public: - using Inputs = render::VaryingSet6; + DeferredFrameTransformPointer, + LightStage::FramePointer>; using Config = DebugDeferredBufferConfig; using JobModel = render::Job::ModelI; @@ -109,9 +112,9 @@ private: using StandardPipelines = std::array; using CustomPipelines = std::unordered_map; - bool pipelineNeedsUpdate(Mode mode, std::string customFile = std::string()) const; - const gpu::PipelinePointer& getPipeline(Mode mode, std::string customFile = std::string()); - std::string getShaderSourceCode(Mode mode, std::string customFile = std::string()); + bool pipelineNeedsUpdate(Mode mode, const std::string& customFile = std::string()) const; + const gpu::PipelinePointer& getPipeline(Mode mode, const std::string& customFile = std::string()); + std::string getShaderSourceCode(Mode mode, const std::string& customFile = std::string()); ParametersBuffer _parameters; StandardPipelines _pipelines; diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index e5a7c39d54..f3b8c0404a 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -17,23 +17,23 @@ // See DeferredShader_MapSlot in DeferredLightingEffect.cpp for constants // the albedo texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRRED_COLOR) uniform sampler2D albedoMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRRED_COLOR) uniform sampler2D albedoMap; // the normal texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRRED_NORMAL) uniform sampler2D normalMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRRED_NORMAL) uniform sampler2D normalMap; // the specular texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRRED_SPECULAR) uniform sampler2D specularMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRRED_SPECULAR) uniform sampler2D specularMap; // the depth texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRRED_DEPTH) uniform sampler2D depthMap; -layout(binding=RENDER_UTILS_TEXTURE_DEFERRRED_LINEAR_Z_EYE) uniform sampler2D linearZeyeMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRRED_DEPTH) uniform sampler2D depthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRRED_LINEAR_Z_EYE) uniform sampler2D linearZeyeMap; // the obscurance texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRED_OBSCURANCE) uniform sampler2D obscuranceMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRED_OBSCURANCE) uniform sampler2D obscuranceMap; // the lighting texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRED_LIGHTING) uniform sampler2D lightingMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRED_LIGHTING) uniform sampler2D lightingMap; struct DeferredFragment { @@ -170,14 +170,14 @@ DeferredFragment unpackDeferredFragment(DeferredFrameTransform deferredTransform <@func declareDeferredCurvature()@> // the curvature texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRED_CURVATURE) uniform sampler2D curvatureMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRED_CURVATURE) uniform sampler2D curvatureMap; vec4 fetchCurvature(vec2 texcoord) { return texture(curvatureMap, texcoord); } // the curvature texture -layout(binding=RENDER_UTILS_TEXTURE_DEFERRED_DIFFUSED_CURVATURE) uniform sampler2D diffusedCurvatureMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_DEFERRED_DIFFUSED_CURVATURE) uniform sampler2D diffusedCurvatureMap; vec4 fetchDiffusedCurvature(vec2 texcoord) { return texture(diffusedCurvatureMap, texcoord); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 3b23711a64..8cf56ec7ad 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -44,7 +44,7 @@ using namespace render; struct LightLocations { bool shadowTransform{ false }; void initialize(const gpu::ShaderPointer& program) { - shadowTransform = program->getUniformBuffers().isValid(ru::Buffer::ShadowParams); + shadowTransform = program->getReflection().validUniformBuffer(ru::Buffer::ShadowParams); } }; @@ -70,17 +70,22 @@ void DeferredLightingEffect::init() { loadLightProgram(shader::render_utils::program::local_lights_drawOutline, true, _localLightOutline, _localLightOutlineLocations); } +// FIXME: figure out how to move lightFrame into a varying in GeometryCache and RenderPipelines void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch) { + setupKeyLightBatch(args, batch, args->_scene->getStage()->_currentFrame); +} + +void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch, const LightStage::Frame& lightFrame) { PerformanceTimer perfTimer("DLE->setupBatch()"); graphics::LightPointer keySunLight; auto lightStage = args->_scene->getStage(); if (lightStage) { - keySunLight = lightStage->getCurrentKeyLight(); + keySunLight = lightStage->getCurrentKeyLight(lightFrame); } graphics::LightPointer keyAmbiLight; if (lightStage) { - keyAmbiLight = lightStage->getCurrentAmbientLight(); + keyAmbiLight = lightStage->getCurrentAmbientLight(lightFrame); } if (keySunLight) { @@ -361,12 +366,6 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input // For the rest of the rendering, bind the lighting model batch.setUniformBuffer(ru::Buffer::LightModel, lightingModel->getParametersBuffer()); }); - - - // Prepare a fresh Light Frame - auto lightStage = renderContext->_scene->getStage(); - assert(lightStage); - lightStage->_currentFrame.clear(); } @@ -374,7 +373,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, const DeferredFrameTransformPointer& frameTransform, const DeferredFramebufferPointer& deferredFramebuffer, const LightingModelPointer& lightingModel, - const graphics::HazePointer& haze, + const LightStage::FramePointer& lightFrame, + const HazeStage::FramePointer& hazeFrame, const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer, const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource, @@ -434,7 +434,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, auto lightStage = renderContext->_scene->getStage(); assert(lightStage); assert(lightStage->getNumLights() > 0); - auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); + auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(*lightFrame); const auto& globalShadow = lightAndShadow.second; // Bind the shadow buffers @@ -448,8 +448,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, auto keyLight = lightAndShadow.first; graphics::LightPointer ambientLight; - if (lightStage && lightStage->_currentFrame._ambientLights.size()) { - ambientLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front()); + if (lightStage && lightFrame->_ambientLights.size()) { + ambientLight = lightStage->getLight(lightFrame->_ambientLights.front()); } bool hasAmbientMap = (ambientLight != nullptr); @@ -458,8 +458,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, // Check if keylight casts shadows bool keyLightCastShadows { false }; - if (renderShadows && lightStage && lightStage->_currentFrame._sunLights.size()) { - graphics::LightPointer keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front()); + if (renderShadows && lightStage && lightFrame->_sunLights.size()) { + graphics::LightPointer keyLight = lightStage->getLight(lightFrame->_sunLights.front()); if (keyLight) { keyLightCastShadows = keyLight->getCastShadows(); } @@ -496,13 +496,17 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, } // Setup the global lighting - deferredLightingEffect->setupKeyLightBatch(args, batch); + deferredLightingEffect->setupKeyLightBatch(args, batch, *lightFrame); // Haze - if (haze) { - batch.setUniformBuffer(ru::Buffer::HazeParams, haze->getHazeParametersBuffer()); + const auto& hazeStage = args->_scene->getStage(); + if (hazeStage && hazeFrame->_hazes.size() > 0) { + const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front()); + if (hazePointer) { + batch.setUniformBuffer(ru::Buffer::HazeParams, hazePointer->getHazeParametersBuffer()); + } } - + batch.draw(gpu::TRIANGLE_STRIP, 4); deferredLightingEffect->unsetKeyLightBatch(batch); @@ -617,7 +621,8 @@ void RenderDeferred::run(const RenderContextPointer& renderContext, const Inputs auto lightClusters = inputs.get6(); auto args = renderContext->args; - const auto haze = inputs.get7(); + const auto& lightFrame = inputs.get7(); + const auto& hazeFrame = inputs.get8(); if (!_gpuTimer) { _gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__); @@ -626,10 +631,10 @@ void RenderDeferred::run(const RenderContextPointer& renderContext, const Inputs auto previousBatch = args->_batch; gpu::doInBatch(nullptr, args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; - _gpuTimer->begin(batch); + _gpuTimer->begin(batch); + + setupJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, lightFrame, hazeFrame, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource, _renderShadows); - setupJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, haze, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource, _renderShadows); - lightsJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, lightClusters); cleanupJob.run(renderContext); @@ -647,19 +652,18 @@ void RenderDeferred::run(const RenderContextPointer& renderContext, const Inputs void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { if (!_defaultLight || !_defaultBackground) { - if (!_defaultSkyboxTexture) { - auto textureCache = DependencyManager::get(); - { - PROFILE_RANGE(render, "Process Default Skybox"); - QFileSelector fileSelector; - fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); - auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); + if (!_defaultSkyboxNetworkTexture) { + PROFILE_RANGE(render, "Process Default Skybox"); + _defaultSkyboxNetworkTexture = DependencyManager::get()->getTexture( + PathUtils::resourcesUrl() + "images/Default-Sky-9-cubemap/Default-Sky-9-cubemap.texmeta.json", image::TextureUsage::CUBE_TEXTURE); + } - _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString()); - _defaultSkyboxAmbientTexture = _defaultSkyboxTexture; - - _defaultSkybox->setCubemap(_defaultSkyboxTexture); - } + if (_defaultSkyboxNetworkTexture && _defaultSkyboxNetworkTexture->isLoaded() && _defaultSkyboxNetworkTexture->getGPUTexture()) { + _defaultSkyboxAmbientTexture = _defaultSkyboxNetworkTexture->getGPUTexture(); + _defaultSkybox->setCubemap(_defaultSkyboxAmbientTexture); + } else { + // Don't do anything until the skybox has loaded + return; } auto lightStage = renderContext->_scene->getStage(); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 70bfb65f38..a838d0c4d6 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -49,6 +49,7 @@ public: void init(); static void setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch); + static void setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch, const LightStage::Frame& lightFrame); static void unsetKeyLightBatch(gpu::Batch& batch); static void setupLocalLightsBatch(gpu::Batch& batch, const LightClustersPointer& lightClusters); @@ -139,13 +140,13 @@ public: class RenderDeferredSetup { public: - // using JobModel = render::Job::ModelI; - + void run(const render::RenderContextPointer& renderContext, const DeferredFrameTransformPointer& frameTransform, const DeferredFramebufferPointer& deferredFramebuffer, const LightingModelPointer& lightingModel, - const graphics::HazePointer& haze, + const LightStage::FramePointer& lightFrame, + const HazeStage::FramePointer& hazeFrame, const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer, const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource, @@ -181,9 +182,9 @@ using RenderDeferredConfig = render::GPUJobConfig; class RenderDeferred { public: - using Inputs = render::VaryingSet8 < + using Inputs = render::VaryingSet9< DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, - AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer, LightClustersPointer, graphics::HazePointer>; + AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer, LightClustersPointer, LightStage::FramePointer, HazeStage::FramePointer>; using Config = RenderDeferredConfig; using JobModel = render::Job::ModelI; @@ -220,7 +221,7 @@ protected: graphics::HazePointer _defaultHaze{ nullptr }; HazeStage::Index _defaultHazeID{ HazeStage::INVALID_INDEX }; graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() }; - gpu::TexturePointer _defaultSkyboxTexture; + NetworkTexturePointer _defaultSkyboxNetworkTexture; gpu::TexturePointer _defaultSkyboxAmbientTexture; }; diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index 19986805f6..8a8805e928 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -24,7 +24,7 @@ struct CameraCorrection { mat4 _prevViewInverse; }; -layout(binding=GPU_BUFFER_CAMERA_CORRECTION) uniform cameraCorrectionBuffer { +LAYOUT(binding=GPU_BUFFER_CAMERA_CORRECTION) uniform cameraCorrectionBuffer { CameraCorrection cameraCorrection; }; @@ -42,7 +42,7 @@ struct DeferredFrameTransform { mat4 _invProjectionUnJittered[2]; }; -layout(binding=RENDER_UTILS_BUFFER_DEFERRED_FRAME_TRANSFORM) uniform deferredFrameTransformBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_DEFERRED_FRAME_TRANSFORM) uniform deferredFrameTransformBuffer { DeferredFrameTransform frameTransform; }; diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 538a916a06..db80cbecae 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -19,7 +19,6 @@ #include "StencilMaskPass.h" #include "FramebufferCache.h" -#include "HazeStage.h" #include "LightStage.h" namespace ru { @@ -32,98 +31,15 @@ namespace gr { using graphics::slot::buffer::Buffer; } -void HazeConfig::setHazeColor(const glm::vec3 value) { - hazeColor = value; -} - -void HazeConfig::setHazeGlareAngle(const float value) { - hazeGlareAngle = value; -} - -void HazeConfig::setHazeGlareColor(const glm::vec3 value) { - hazeGlareColor = value; -} - -void HazeConfig::setHazeBaseReference(const float value) { - hazeBaseReference = value; -} - -void HazeConfig::setHazeActive(const bool active) { - isHazeActive = active; -} - -void HazeConfig::setAltitudeBased(const bool active) { - isAltitudeBased = active; -} - -void HazeConfig::setHazeAttenuateKeyLight(const bool active) { - isHazeAttenuateKeyLight = active; -} - -void HazeConfig::setModulateColorActive(const bool active) { - isModulateColorActive = active; -} - -void HazeConfig::setHazeEnableGlare(const bool active) { - isHazeEnableGlare = active; -} - -void HazeConfig::setHazeRange(const float value) { - hazeRange = value; -} - -void HazeConfig::setHazeAltitude(const float value) { - hazeHeight = value; -} - -void HazeConfig::setHazeKeyLightRange(const float value) { - hazeKeyLightRange = value; -} - -void HazeConfig::setHazeKeyLightAltitude(const float value) { - hazeKeyLightAltitude = value; -} - -void HazeConfig::setHazeBackgroundBlend(const float value) { - hazeBackgroundBlend = value; -} - -MakeHaze::MakeHaze() { - _haze = std::make_shared(); -} - -void MakeHaze::configure(const Config& config) { - _haze->setHazeColor(config.hazeColor); - _haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(config.hazeGlareAngle)); - - _haze->setHazeGlareColor(config.hazeGlareColor); - _haze->setHazeBaseReference(config.hazeBaseReference); - - _haze->setHazeActive(config.isHazeActive); - _haze->setAltitudeBased(config.isAltitudeBased); - _haze->setHazeAttenuateKeyLight(config.isHazeAttenuateKeyLight); - _haze->setModulateColorActive(config.isModulateColorActive); - _haze->setHazeEnableGlare(config.isHazeEnableGlare); - - _haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange)); - _haze->setHazeAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight)); - - _haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange)); - _haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude)); - - _haze->setHazeBackgroundBlend(config.hazeBackgroundBlend); -} - -void MakeHaze::run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze) { - haze = _haze; -} - -void DrawHaze::configure(const Config& config) { -} - void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { - const auto haze = inputs.get0(); - if (haze == nullptr) { + const auto hazeFrame = inputs.get0(); + const auto& hazeStage = renderContext->args->_scene->getStage(); + graphics::HazePointer haze; + if (hazeStage && hazeFrame->_hazes.size() > 0) { + haze = hazeStage->getHaze(hazeFrame->_hazes.front()); + } + + if (!haze) { return; } @@ -131,6 +47,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu const auto framebuffer = inputs.get2(); const auto transformBuffer = inputs.get3(); const auto lightingModel = inputs.get4(); + const auto lightFrame = inputs.get5(); auto depthBuffer = framebuffer->getLinearDepthTexture(); @@ -161,17 +78,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(outputFramebufferSize, args->_viewport)); batch.setPipeline(_hazePipeline); - - auto hazeStage = args->_scene->getStage(); - if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { - graphics::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); - if (hazePointer) { - batch.setUniformBuffer(ru::Buffer::HazeParams, hazePointer->getHazeParametersBuffer()); - } else { - // Something is wrong, so just quit Haze - return; - } - } + batch.setUniformBuffer(ru::Buffer::HazeParams, haze->getHazeParametersBuffer()); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, transformBuffer->getFrameTransformBuffer()); batch.setUniformBuffer(ru::Buffer::LightModel, lightingModel->getParametersBuffer()); @@ -179,13 +86,12 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu auto lightStage = args->_scene->getStage(); if (lightStage) { graphics::LightPointer keyLight; - keyLight = lightStage->getCurrentKeyLight(); + keyLight = lightStage->getCurrentKeyLight(*lightFrame); if (keyLight) { batch.setUniformBuffer(gr::Buffer::KeyLight, keyLight->getLightSchemaBuffer()); } } - batch.draw(gpu::TRIANGLE_STRIP, 4); }); } diff --git a/libraries/render-utils/src/DrawHaze.h b/libraries/render-utils/src/DrawHaze.h index e30ce26dd4..9236543068 100644 --- a/libraries/render-utils/src/DrawHaze.h +++ b/libraries/render-utils/src/DrawHaze.h @@ -19,152 +19,20 @@ #include #include #include -#include #include "SurfaceGeometryPass.h" #include "LightingModel.h" +#include "HazeStage.h" +#include "LightStage.h" + using LinearDepthFramebufferPointer = std::shared_ptr; -class MakeHazeConfig : public render::Job::Config { - Q_OBJECT - - Q_PROPERTY(glm::vec3 hazeColor MEMBER hazeColor WRITE setHazeColor NOTIFY dirty); - Q_PROPERTY(float hazeGlareAngle MEMBER hazeGlareAngle WRITE setHazeGlareAngle NOTIFY dirty); - - Q_PROPERTY(glm::vec3 hazeGlareColor MEMBER hazeGlareColor WRITE setHazeGlareColor NOTIFY dirty); - Q_PROPERTY(float hazeBaseReference MEMBER hazeBaseReference WRITE setHazeBaseReference NOTIFY dirty); - - Q_PROPERTY(bool isHazeActive MEMBER isHazeActive WRITE setHazeActive NOTIFY dirty); - Q_PROPERTY(bool isAltitudeBased MEMBER isAltitudeBased WRITE setAltitudeBased NOTIFY dirty); - Q_PROPERTY(bool isHazeAttenuateKeyLight MEMBER isHazeAttenuateKeyLight WRITE setHazeAttenuateKeyLight NOTIFY dirty); - Q_PROPERTY(bool isModulateColorActive MEMBER isModulateColorActive WRITE setModulateColorActive NOTIFY dirty); - Q_PROPERTY(bool isHazeEnableGlare MEMBER isHazeEnableGlare WRITE setHazeEnableGlare NOTIFY dirty); - - Q_PROPERTY(float hazeRange MEMBER hazeRange WRITE setHazeRange NOTIFY dirty); - Q_PROPERTY(float hazeHeight MEMBER hazeHeight WRITE setHazeAltitude NOTIFY dirty); - - Q_PROPERTY(float hazeKeyLightRange MEMBER hazeKeyLightRange WRITE setHazeKeyLightRange NOTIFY dirty); - Q_PROPERTY(float hazeKeyLightAltitude MEMBER hazeKeyLightAltitude WRITE setHazeKeyLightAltitude NOTIFY dirty); - - Q_PROPERTY(float hazeBackgroundBlend MEMBER hazeBackgroundBlend WRITE setHazeBackgroundBlend NOTIFY dirty); - -public: - MakeHazeConfig() : render::Job::Config() {} - - glm::vec3 hazeColor{ graphics::Haze::INITIAL_HAZE_COLOR }; - float hazeGlareAngle{ graphics::Haze::INITIAL_HAZE_GLARE_ANGLE }; - - glm::vec3 hazeGlareColor{ graphics::Haze::INITIAL_HAZE_GLARE_COLOR }; - float hazeBaseReference{ graphics::Haze::INITIAL_HAZE_BASE_REFERENCE }; - - bool isHazeActive{ false }; - bool isAltitudeBased{ false }; - bool isHazeAttenuateKeyLight{ false }; - bool isModulateColorActive{ false }; - bool isHazeEnableGlare{ false }; - - float hazeRange{ graphics::Haze::INITIAL_HAZE_RANGE }; - float hazeHeight{ graphics::Haze::INITIAL_HAZE_HEIGHT }; - - float hazeKeyLightRange{ graphics::Haze::INITIAL_KEY_LIGHT_RANGE }; - float hazeKeyLightAltitude{ graphics::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; - - float hazeBackgroundBlend{ graphics::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; - -public slots: - void setHazeColor(const glm::vec3 value) { hazeColor = value; emit dirty(); } - void setHazeGlareAngle(const float value) { hazeGlareAngle = value; emit dirty(); } - - void setHazeGlareColor(const glm::vec3 value) { hazeGlareColor = value; emit dirty(); } - void setHazeBaseReference(const float value) { hazeBaseReference = value; ; emit dirty(); } - - void setHazeActive(const bool active) { isHazeActive = active; emit dirty(); } - void setAltitudeBased(const bool active) { isAltitudeBased = active; emit dirty(); } - void setHazeAttenuateKeyLight(const bool active) { isHazeAttenuateKeyLight = active; emit dirty(); } - void setModulateColorActive(const bool active) { isModulateColorActive = active; emit dirty(); } - void setHazeEnableGlare(const bool active) { isHazeEnableGlare = active; emit dirty(); } - - void setHazeRange(const float value) { hazeRange = value; emit dirty(); } - void setHazeAltitude(const float value) { hazeHeight = value; emit dirty(); } - - void setHazeKeyLightRange(const float value) { hazeKeyLightRange = value; emit dirty(); } - void setHazeKeyLightAltitude(const float value) { hazeKeyLightAltitude = value; emit dirty(); } - - void setHazeBackgroundBlend(const float value) { hazeBackgroundBlend = value; ; emit dirty(); } - -signals: - void dirty(); -}; - -class MakeHaze { -public: - using Config = MakeHazeConfig; - using JobModel = render::Job::ModelO; - - MakeHaze(); - - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze); - -private: - graphics::HazePointer _haze; -}; - -class HazeConfig : public render::Job::Config { -public: - HazeConfig() : render::Job::Config(true) {} - - // attributes - glm::vec3 hazeColor{ graphics::Haze::INITIAL_HAZE_COLOR }; - float hazeGlareAngle{ graphics::Haze::INITIAL_HAZE_GLARE_ANGLE }; - - glm::vec3 hazeGlareColor{ graphics::Haze::INITIAL_HAZE_GLARE_COLOR }; - float hazeBaseReference{ graphics::Haze::INITIAL_HAZE_BASE_REFERENCE }; - - bool isHazeActive{ false }; // Setting this to true will set haze to on - bool isAltitudeBased{ false }; - bool isHazeAttenuateKeyLight{ false }; - bool isModulateColorActive{ false }; - bool isHazeEnableGlare{ false }; - - float hazeRange{ graphics::Haze::INITIAL_HAZE_RANGE }; - float hazeHeight{ graphics::Haze::INITIAL_HAZE_HEIGHT }; - - float hazeKeyLightRange{ graphics::Haze::INITIAL_KEY_LIGHT_RANGE }; - float hazeKeyLightAltitude{ graphics::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; - - float hazeBackgroundBlend{ graphics::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; - - // methods - void setHazeColor(const glm::vec3 value); - void setHazeGlareAngle(const float value); - - void setHazeGlareColor(const glm::vec3 value); - void setHazeBaseReference(const float value); - - void setHazeActive(const bool active); - void setAltitudeBased(const bool active); - void setHazeAttenuateKeyLight(const bool active); - void setModulateColorActive(const bool active); - void setHazeEnableGlare(const bool active); - - void setHazeRange(const float value); - void setHazeAltitude(const float value); - - void setHazeKeyLightRange(const float value); - void setHazeKeyLightAltitude(const float value); - - void setHazeBackgroundBlend(const float value); -}; - class DrawHaze { public: - using Inputs = render::VaryingSet5; - using Config = HazeConfig; - using JobModel = render::Job::ModelI; + using Inputs = render::VaryingSet6; + using JobModel = render::Job::ModelI; - void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: diff --git a/libraries/render-utils/src/Fade.slh b/libraries/render-utils/src/Fade.slh index 47347ba135..a7523f969b 100644 --- a/libraries/render-utils/src/Fade.slh +++ b/libraries/render-utils/src/Fade.slh @@ -19,12 +19,12 @@ <@include FadeObjectParams.shared.slh@> // See ShapePipeline::Slot::BUFFER in ShapePipeline.h -layout(std140, binding=RENDER_UTILS_BUFFER_FADE_PARAMS) uniform fadeParametersBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_FADE_PARAMS) uniform fadeParametersBuffer { FadeParameters fadeParameters[CATEGORY_COUNT]; }; // See ShapePipeline::Slot::MAP in ShapePipeline.h -layout(binding=RENDER_UTILS_TEXTURE_FADE_MASK) uniform sampler2D fadeMaskMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_FADE_MASK) uniform sampler2D fadeMaskMap; vec3 getNoiseInverseSize(int category) { return fadeParameters[category]._noiseInvSizeAndLevel.xyz; @@ -117,7 +117,7 @@ void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) { <@func declareFadeFragmentUniform()@> -layout(std140, binding=RENDER_UTILS_BUFFER_FADE_OBJECT_PARAMS) uniform fadeObjectParametersBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_FADE_OBJECT_PARAMS) uniform fadeObjectParametersBuffer { FadeObjectParams fadeObjectParams; }; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 2fe811c97b..1215c9abea 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2175,7 +2175,10 @@ public: bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); } Flags _flags = 0; - short _spare = 0; +#if defined(__clang__) + __attribute__((unused)) +#endif + short _spare = 0; // Padding int getRaw() const { return *reinterpret_cast(this); } diff --git a/libraries/render-utils/src/Haze.slf b/libraries/render-utils/src/Haze.slf index bb3c0bc769..8d90b4c816 100644 --- a/libraries/render-utils/src/Haze.slf +++ b/libraries/render-utils/src/Haze.slf @@ -21,7 +21,7 @@ <@include Haze.slh@> -layout(binding=RENDER_UTILS_TEXTURE_HAZE_LINEAR_DEPTH) uniform sampler2D linearDepthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_HAZE_LINEAR_DEPTH) uniform sampler2D linearDepthMap; vec4 unpackPositionFromZeye(vec2 texcoord) { float Zeye = -texture(linearDepthMap, texcoord).x; diff --git a/libraries/render-utils/src/Haze.slh b/libraries/render-utils/src/Haze.slh index b7bcfcefcd..a7654da8d2 100644 --- a/libraries/render-utils/src/Haze.slh +++ b/libraries/render-utils/src/Haze.slh @@ -39,7 +39,7 @@ struct HazeParams { }; // See ShapePipeline::Slot::BUFFER in ShapePipeline.h -layout(std140, binding=RENDER_UTILS_BUFFER_HAZE_PARAMS) uniform hazeBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_HAZE_PARAMS) uniform hazeBuffer { HazeParams hazeParams; }; diff --git a/libraries/render-utils/src/HazeStage.cpp b/libraries/render-utils/src/HazeStage.cpp index e56b715b8c..c850828be5 100644 --- a/libraries/render-utils/src/HazeStage.cpp +++ b/libraries/render-utils/src/HazeStage.cpp @@ -16,32 +16,6 @@ std::string HazeStage::_stageName { "HAZE_STAGE"}; const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; -FetchHazeStage::FetchHazeStage() { - _haze = std::make_shared(); -} - -void FetchHazeStage::configure(const Config& config) { - _haze->setHazeColor(config.hazeColor); - _haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(config.hazeGlareAngle)); - - _haze->setHazeGlareColor(config.hazeGlareColor); - _haze->setHazeBaseReference(config.hazeBaseReference); - - _haze->setHazeActive(config.isHazeActive); - _haze->setAltitudeBased(config.isAltitudeBased); - _haze->setHazeAttenuateKeyLight(config.isHazeAttenuateKeyLight); - _haze->setModulateColorActive(config.isModulateColorActive); - _haze->setHazeEnableGlare(config.isHazeEnableGlare); - - _haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange)); - _haze->setHazeAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight)); - - _haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange)); - _haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude)); - - _haze->setHazeBackgroundBlend(config.hazeBackgroundBlend); -} - HazeStage::Index HazeStage::findHaze(const HazePointer& haze) const { auto found = _hazeMap.find(haze); if (found != _hazeMap.end()) { @@ -84,15 +58,4 @@ void HazeStageSetup::run(const render::RenderContextPointer& renderContext) { if (!stage) { renderContext->_scene->resetStage(HazeStage::getName(), std::make_shared()); } -} - -void FetchHazeStage::run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze) { - auto hazeStage = renderContext->_scene->getStage(); - assert(hazeStage); - - haze = nullptr; - if (hazeStage->_currentFrame._hazes.size() != 0) { - auto hazeId = hazeStage->_currentFrame._hazes.front(); - haze = hazeStage->getHaze(hazeId); - } -} +} \ No newline at end of file diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index b48168e376..b1c7d0384c 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -65,6 +65,7 @@ public: HazeStage::HazeIndices _hazes; }; + using FramePointer = std::shared_ptr; Frame _currentFrame; }; @@ -150,18 +151,4 @@ public slots: signals: void dirty(); }; - -class FetchHazeStage { -public: - using Config = FetchHazeConfig; - using JobModel = render::Job::ModelO; - - FetchHazeStage(); - - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze); - -private: - graphics::HazePointer _haze; -}; #endif diff --git a/libraries/render-utils/src/Highlight.slh b/libraries/render-utils/src/Highlight.slh index 885df34d26..264b57acbb 100644 --- a/libraries/render-utils/src/Highlight.slh +++ b/libraries/render-utils/src/Highlight.slh @@ -15,12 +15,12 @@ <@include Highlight_shared.slh@> -layout(std140, binding=RENDER_UTILS_BUFFER_HIGHLIGHT_PARAMS) uniform highlightParamsBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_HIGHLIGHT_PARAMS) uniform highlightParamsBuffer { HighlightParameters params; }; -layout(binding=RENDER_UTILS_TEXTURE_HIGHLIGHT_SCENE_DEPTH) uniform sampler2D sceneDepthMap; -layout(binding=RENDER_UTILS_TEXTURE_HIGHLIGHT_DEPTH) uniform sampler2D highlightedDepthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_HIGHLIGHT_SCENE_DEPTH) uniform sampler2D sceneDepthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_HIGHLIGHT_DEPTH) uniform sampler2D highlightedDepthMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 2e51013bc5..0cb08971ff 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -39,10 +39,10 @@ namespace gr { extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); -HighlightRessources::HighlightRessources() { +HighlightResources::HighlightResources() { } -void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightResources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize()); // If the buffer size changed, we need to delete our FBOs and recreate them at the @@ -58,32 +58,37 @@ void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuff if (!_colorFrameBuffer) { allocateColorBuffer(primaryFrameBuffer); } + + // The primaryFrameBuffer render buffer can change + if (_colorFrameBuffer->getRenderBuffer(0) != primaryFrameBuffer->getRenderBuffer(0)) { + _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); + } } } -void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightResources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil")); _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); _colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat()); } -void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightResources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); _depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y)); _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth")); _depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat); } -gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() { +gpu::FramebufferPointer HighlightResources::getDepthFramebuffer() { assert(_depthFrameBuffer); return _depthFrameBuffer; } -gpu::TexturePointer HighlightRessources::getDepthTexture() { +gpu::TexturePointer HighlightResources::getDepthTexture() { return _depthStencilTexture; } -gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() { +gpu::FramebufferPointer HighlightResources::getColorFramebuffer() { assert(_colorFrameBuffer); return _colorFrameBuffer; } @@ -97,25 +102,21 @@ float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle& } PrepareDrawHighlight::PrepareDrawHighlight() { - _ressources = std::make_shared(); + _resources = std::make_shared(); } void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto destinationFrameBuffer = inputs; - _ressources->update(destinationFrameBuffer); - outputs = _ressources; + _resources->update(destinationFrameBuffer); + outputs = _resources; } gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline; gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline; -DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, - render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) : - _highlightPassIndex{ highlightIndex }, - _shapePlumber { shapePlumber }, - _sharedParameters{ parameters } { -} +DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, + HighlightSharedParametersPointer parameters) : _highlightPassIndex(highlightIndex), _shapePlumber(shapePlumber), _sharedParameters(parameters) {} void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); @@ -126,13 +127,13 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c const int PARAMETERS_SLOT = 0; if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) { - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + gpu::StatePointer state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE)); + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); state->setColorWriteMask(false, false, false, false); state->setCullMode(gpu::State::CULL_FRONT); - gpu::StatePointer fillState = gpu::StatePointer(new gpu::State()); + gpu::StatePointer fillState = std::make_shared(); fillState->setDepthTest(false, false, gpu::LESS_EQUAL); fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); fillState->setColorWriteMask(false, false, false, false); @@ -151,7 +152,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex]; if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) { - auto ressources = inputs.get1(); + auto resources = inputs.get1(); auto& highlight = highlightStage->getHighlight(highlightId); RenderArgs* args = renderContext->args; @@ -165,15 +166,11 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c // while stereo is enabled triggers a warning gpu::doInBatch("DrawHighlightMask::run::begin", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setFramebuffer(ressources->getDepthFramebuffer()); + batch.setFramebuffer(resources->getDepthFramebuffer()); batch.clearDepthStencilFramebuffer(1.0f, 0); }); - glm::mat4 projMat; - Transform viewMat; const auto jitter = inputs.get2(); - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); render::ItemBounds itemBounds; @@ -185,6 +182,10 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c auto maskDeformedDQPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed().withDualQuatSkinned()); // Setup camera, projection and viewport for all items + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); batch.setProjectionJitter(jitter.x, jitter.y); @@ -233,7 +234,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c const auto securityMargin = 2.0f; const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); - const auto framebufferSize = ressources->getSourceFrameSize(); + const auto framebufferSize = resources->getSourceFrameSize(); const glm::vec2 highlightWidth = { blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y }; if (highlightWidth != _outlineWidth.get()) { @@ -241,11 +242,6 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c } gpu::doInBatch("DrawHighlightMask::run::end", args->_context, [&](gpu::Batch& batch) { - // Setup camera, projection and viewport for all items - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - // Draw stencil mask with object bounding boxes auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; batch.setPipeline(stencilPipeline); @@ -264,15 +260,14 @@ gpu::PipelinePointer DrawHighlight::_pipeline; gpu::PipelinePointer DrawHighlight::_pipelineFilled; DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) : - _highlightPassIndex{ highlightIndex }, - _sharedParameters{ parameters } { + _highlightPassIndex(highlightIndex), _sharedParameters(parameters) { } void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { auto highlightFrameBuffer = inputs.get1(); auto highlightRect = inputs.get3(); - if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) { + if (highlightFrameBuffer && highlightRect.z > 0 && highlightRect.w > 0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture(); @@ -334,10 +329,11 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) { if (!_pipeline) { - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + gpu::StatePointer state = std::make_shared(); state->setDepthTest(gpu::State::DepthTest(false, false)); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); + state->setColorWriteMask(true, true, true, true); auto program = gpu::Shader::createProgram(shader::render_utils::program::highlight); _pipeline = gpu::Pipeline::create(program, state); @@ -364,10 +360,10 @@ void DebugHighlight::configure(const Config& config) { } void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) { - const auto highlightRessources = input.get0(); + const auto highlightResources = input.get0(); const auto highlightRect = input.get1(); - if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) { + if (_isDisplayEnabled && highlightResources && highlightRect.z > 0 && highlightRect.w > 0) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; @@ -376,7 +372,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons auto primaryFramebuffer = input.get3(); gpu::doInBatch("DebugHighlight::run", args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); - batch.setFramebuffer(highlightRessources->getColorFramebuffer()); + batch.setFramebuffer(highlightResources->getColorFramebuffer()); const auto geometryBuffer = DependencyManager::get(); @@ -386,13 +382,13 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setProjectionJitter(jitter.x, jitter.y); - batch.setViewTransform(viewMat, true); + batch.setViewTransform(viewMat); batch.setModelTransform(Transform()); const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); batch.setPipeline(getDepthPipeline()); - batch.setResourceTexture(0, highlightRessources->getDepthTexture()); + batch.setResourceTexture(0, highlightResources->getDepthTexture()); const glm::vec2 bottomLeft(-1.0f, -1.0f); const glm::vec2 topRight(1.0f, 1.0f); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); @@ -406,35 +402,31 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons } void DebugHighlight::initializePipelines() { - static const auto FRAGMENT_SHADER_SOURCE = gpu::Shader::createPixel(shader::render_utils::fragment::debug_deferred_buffer)->getSource(); - static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; - static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER_SOURCE.getCode().find(SOURCE_PLACEHOLDER); - Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, - "Could not find source placeholder"); - - auto state = std::make_shared(); - state->setDepthTest(gpu::State::DepthTest(false, false)); - state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); - - const auto vs = gpu::Shader::createVertex(shader::render_utils::vertex::debug_deferred_buffer); - + static const std::string REPLACEMENT_MARKER{ "//SOURCE_PLACEHOLDER" }; // Depth shader - { - static const std::string DEPTH_SHADER{ R"SHADER( + static const std::string DEPTH_SHADER{ R"SHADER( vec4 getFragmentColor() { float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x; Zdb = 1.0-(1.0-Zdb)*100; return vec4(Zdb, Zdb, Zdb, 1.0); } )SHADER" }; + static const auto& vs = gpu::Shader::createVertex(shader::render_utils::vertex::debug_deferred_buffer); - auto fragmentShader = FRAGMENT_SHADER_SOURCE.getCode(); - fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEPTH_SHADER); - const auto ps = gpu::Shader::createPixel({ fragmentShader, FRAGMENT_SHADER_SOURCE.getReflection() }); - const auto program = gpu::Shader::createProgram(vs, ps); - _depthPipeline = gpu::Pipeline::create(program, state); - } + gpu::Shader::Source fragmentSource; + fragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::debug_deferred_buffer); + fragmentSource.replacements[REPLACEMENT_MARKER] = DEPTH_SHADER; + + const auto ps = gpu::Shader::createPixel(fragmentSource); + const auto program = gpu::Shader::createProgram(vs, ps); + + auto state = std::make_shared(); + state->setDepthTest(gpu::State::DepthTest(false, false)); + state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); + state->setColorWriteMask(true, true, true, true); + + _depthPipeline = gpu::Pipeline::create(program, state); } const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() { @@ -512,7 +504,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters); // Prepare for highlight group rendering. - const auto highlightRessources = task.addJob("PrepareHighlight", primaryFramebuffer); + const auto highlightResources = task.addJob("PrepareHighlight", primaryFramebuffer); render::Varying highlight0Rect; for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) { @@ -532,7 +524,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren stream << "HighlightMask" << i; name = stream.str(); } - const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources, jitter).asVarying(); + const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightResources, jitter).asVarying(); const auto highlightedRect = task.addJob(name, drawMaskInputs, i, shapePlumber, sharedParameters); if (i == 0) { highlight0Rect = highlightedRect; @@ -544,12 +536,12 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren stream << "HighlightEffect" << i; name = stream.str(); } - const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying(); + const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightResources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying(); task.addJob(name, drawHighlightInputs, i, sharedParameters); } // Debug highlight - const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast(highlight0Rect), jitter, primaryFramebuffer).asVarying(); + const auto debugInputs = DebugHighlight::Inputs(highlightResources, const_cast(highlight0Rect), jitter, primaryFramebuffer).asVarying(); task.addJob("HighlightDebug", debugInputs); } diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h index 32668c1ab6..933503fdb5 100644 --- a/libraries/render-utils/src/HighlightEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -19,9 +19,9 @@ #include "DeferredFramebuffer.h" #include "DeferredFrameTransform.h" -class HighlightRessources { +class HighlightResources { public: - HighlightRessources(); + HighlightResources(); gpu::FramebufferPointer getDepthFramebuffer(); gpu::TexturePointer getDepthTexture(); @@ -44,7 +44,7 @@ protected: void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer); }; -using HighlightRessourcesPointer = std::shared_ptr; +using HighlightResourcesPointer = std::shared_ptr; class HighlightSharedParameters { public: @@ -65,7 +65,7 @@ using HighlightSharedParametersPointer = std::shared_ptr; PrepareDrawHighlight(); @@ -74,7 +74,7 @@ public: private: - HighlightRessourcesPointer _ressources; + HighlightResourcesPointer _resources; }; @@ -112,8 +112,7 @@ private: class DrawHighlightMask { public: - - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet3; using Outputs = glm::ivec4; using JobModel = render::Job::ModelIO; @@ -122,7 +121,6 @@ public: void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); protected: - unsigned int _highlightPassIndex; render::ShapePlumberPointer _shapePlumber; HighlightSharedParametersPointer _sharedParameters; @@ -136,7 +134,7 @@ protected: class DrawHighlight { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet5; using Config = render::Job::Config; using JobModel = render::Job::ModelI; @@ -163,11 +161,10 @@ private: class DebugHighlightConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty) + Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty) public: - - bool viewMask{ false }; + bool viewMask { false }; signals: void dirty(); @@ -175,7 +172,7 @@ signals: class DebugHighlight { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = DebugHighlightConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/Highlight_aabox.slv b/libraries/render-utils/src/Highlight_aabox.slv index 2ecebdea51..65b98355ae 100644 --- a/libraries/render-utils/src/Highlight_aabox.slv +++ b/libraries/render-utils/src/Highlight_aabox.slv @@ -22,8 +22,8 @@ struct ItemBound { vec4 boundDim_s; }; -#if defined(GPU_GL410) -layout(binding=0) uniform samplerBuffer ssbo0Buffer; +#if !defined(GPU_SSBO_TRANSFORM_OBJECT) +LAYOUT(binding=GPU_RESOURCE_BUFFER_SLOT0_TEXTURE) uniform samplerBuffer ssbo0Buffer; ItemBound getItemBound(int i) { int offset = 2 * i; ItemBound bound; @@ -32,7 +32,7 @@ ItemBound getItemBound(int i) { return bound; } #else -layout(std140, binding=0) buffer ssbo0Buffer { +LAYOUT_STD140(binding=GPU_RESOURCE_BUFFER_SLOT0_STORAGE) buffer ssbo0Buffer { ItemBound bounds[]; }; ItemBound getItemBound(int i) { @@ -45,7 +45,7 @@ struct HighlightParameters { vec2 outlineWidth; }; -layout(std140, binding=0) uniform parametersBuffer { +LAYOUT_STD140(binding=0) uniform parametersBuffer { HighlightParameters _parameters; }; diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 797595bf47..4ea9c0cd4c 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -9,7 +9,7 @@ <@include render-utils/ShaderConstants.h@> <@func declareSkyboxMap()@> // declareSkyboxMap -layout(binding=RENDER_UTILS_TEXTURE_SKYBOX) uniform samplerCube skyboxMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SKYBOX) uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod diff --git a/libraries/render-utils/src/LightClusterGrid.slh b/libraries/render-utils/src/LightClusterGrid.slh index 8f57169ace..62af92e6ce 100644 --- a/libraries/render-utils/src/LightClusterGrid.slh +++ b/libraries/render-utils/src/LightClusterGrid.slh @@ -24,7 +24,7 @@ struct FrustumGrid { mat4 eyeToWorldMat; }; -layout(std140, binding=RENDER_UTILS_BUFFER_LIGHT_CLUSTER_FRUSTUM_GRID) uniform frustumGridBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_LIGHT_CLUSTER_FRUSTUM_GRID) uniform frustumGridBuffer { FrustumGrid frustumGrid; }; @@ -60,11 +60,11 @@ float projection_getFar(mat4 projection) { #define GRID_FETCH_BUFFER(i) i!> <@endif@> -layout(std140, binding=RENDER_UTILS_BUFFER_LIGHT_CLUSTER_GRID) uniform clusterGridBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_LIGHT_CLUSTER_GRID) uniform clusterGridBuffer { GRID_INDEX_TYPE _clusterGridTable[GRID_NUM_ELEMENTS]; }; -layout(std140, binding=RENDER_UTILS_BUFFER_LIGHT_CLUSTER_CONTENT) uniform clusterContentBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_LIGHT_CLUSTER_CONTENT) uniform clusterContentBuffer { GRID_INDEX_TYPE _clusterGridContent[GRID_NUM_ELEMENTS]; }; diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index ae484f868f..252416ea9d 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -175,15 +175,15 @@ void LightClusters::updateLightStage(const LightStagePointer& lightStage) { } -void LightClusters::updateLightFrame(const LightStage::Frame& lightFrame, bool points, bool spots) { +void LightClusters::updateLightFrame(const LightStage::FramePointer& lightFrame, bool points, bool spots) { // start fresh _visibleLightIndices.clear(); // Now gather the lights // gather lights - auto& srcPointLights = lightFrame._pointLights; - auto& srcSpotLights = lightFrame._spotLights; + auto& srcPointLights = lightFrame->_pointLights; + auto& srcSpotLights = lightFrame->_spotLights; int numPointLights = (int)srcPointLights.size(); int numSpotLights = (int)srcSpotLights.size(); @@ -548,7 +548,8 @@ void LightClusteringPass::run(const render::RenderContextPointer& renderContext, auto deferredTransform = inputs.get0(); auto lightingModel = inputs.get1(); - auto surfaceGeometryFramebuffer = inputs.get2(); + auto lightFrame = inputs.get2(); + auto surfaceGeometryFramebuffer = inputs.get3(); // first update the Grid with the new frustum if (!_freeze) { @@ -559,7 +560,7 @@ void LightClusteringPass::run(const render::RenderContextPointer& renderContext, auto lightStage = renderContext->_scene->getStage(); assert(lightStage); _lightClusters->updateLightStage(lightStage); - _lightClusters->updateLightFrame(lightStage->_currentFrame, lightingModel->isPointLightEnabled(), lightingModel->isSpotLightEnabled()); + _lightClusters->updateLightFrame(lightFrame, lightingModel->isPointLightEnabled(), lightingModel->isSpotLightEnabled()); auto clusteringStats = _lightClusters->updateClusters(); diff --git a/libraries/render-utils/src/LightClusters.h b/libraries/render-utils/src/LightClusters.h index fa054c304a..e109e43aaa 100644 --- a/libraries/render-utils/src/LightClusters.h +++ b/libraries/render-utils/src/LightClusters.h @@ -79,7 +79,7 @@ public: void updateLightStage(const LightStagePointer& lightStage); - void updateLightFrame(const LightStage::Frame& lightFrame, bool points = true, bool spots = true); + void updateLightFrame(const LightStage::FramePointer& lightFrame, bool points = true, bool spots = true); glm::ivec3 updateClusters(); @@ -167,7 +167,7 @@ protected: class LightClusteringPass { public: - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet4; using Outputs = LightClustersPointer; using Config = LightClusteringPassConfig; using JobModel = render::Job::ModelIO; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 369c62c197..8fe3b0fef5 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -60,7 +60,8 @@ LightStage::LightStage() { } LightStage::Shadow::Schema::Schema() { - ShadowTransform defaultTransform; + ShadowTransform defaultTransform = {}; + defaultTransform.reprojection = mat4(); defaultTransform.fixedBias = 0.005f; std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; @@ -372,36 +373,36 @@ LightStage::LightPointer LightStage::removeLight(Index index) { return removedLight; } -LightStage::LightPointer LightStage::getCurrentKeyLight() const { - Index keyLightId{ _defaultLightId }; - if (!_currentFrame._sunLights.empty()) { - keyLightId = _currentFrame._sunLights.front(); +LightStage::LightPointer LightStage::getCurrentKeyLight(const LightStage::Frame& frame) const { + Index keyLightId { _defaultLightId }; + if (!frame._sunLights.empty()) { + keyLightId = frame._sunLights.front(); } return _lights.get(keyLightId); } -LightStage::LightPointer LightStage::getCurrentAmbientLight() const { +LightStage::LightPointer LightStage::getCurrentAmbientLight(const LightStage::Frame& frame) const { Index keyLightId { _defaultLightId }; - if (!_currentFrame._ambientLights.empty()) { - keyLightId = _currentFrame._ambientLights.front(); + if (!frame._ambientLights.empty()) { + keyLightId = frame._ambientLights.front(); } return _lights.get(keyLightId); } -LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const { +LightStage::ShadowPointer LightStage::getCurrentKeyShadow(const LightStage::Frame& frame) const { Index keyLightId { _defaultLightId }; - if (!_currentFrame._sunLights.empty()) { - keyLightId = _currentFrame._sunLights.front(); + if (!frame._sunLights.empty()) { + keyLightId = frame._sunLights.front(); } auto shadow = getShadow(keyLightId); assert(shadow == nullptr || shadow->getLight() == getLight(keyLightId)); return shadow; } -LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const { +LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow(const LightStage::Frame& frame) const { Index keyLightId { _defaultLightId }; - if (!_currentFrame._sunLights.empty()) { - keyLightId = _currentFrame._sunLights.front(); + if (!frame._sunLights.empty()) { + keyLightId = frame._sunLights.front(); } auto shadow = getShadow(keyLightId); auto light = getLight(keyLightId); diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index b8a49d81bb..5e5b6cf4fa 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -151,11 +151,6 @@ public: return LightAndShadow(light, shadow); } - LightPointer getCurrentKeyLight() const; - LightPointer getCurrentAmbientLight() const; - ShadowPointer getCurrentKeyShadow() const; - LightAndShadow getCurrentKeyLightAndShadow() const; - LightStage(); gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; } @@ -185,6 +180,7 @@ public: LightStage::LightIndices _sunLights; LightStage::LightIndices _ambientLights; }; + using FramePointer = std::shared_ptr; Frame _currentFrame; @@ -193,6 +189,11 @@ public: Index getSpotOffLight() { return _spotOffLightId; } Index getSunOffLight() { return _sunOffLightId; } + LightPointer getCurrentKeyLight(const LightStage::Frame& frame) const; + LightPointer getCurrentAmbientLight(const LightStage::Frame& frame) const; + ShadowPointer getCurrentKeyShadow(const LightStage::Frame& frame) const; + LightAndShadow getCurrentKeyLightAndShadow(const LightStage::Frame& frame) const; + protected: struct Desc { diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index d10a52be60..61c74c7e50 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -23,7 +23,7 @@ struct LightingModel { }; // See DeferredShader_BufferSlot in DeferredLightingEffect.cpp -layout(binding=RENDER_UTILS_BUFFER_LIGHT_MODEL) uniform lightingModelBuffer{ +LAYOUT(binding=RENDER_UTILS_BUFFER_LIGHT_MODEL) uniform lightingModelBuffer{ LightingModel lightingModel; }; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c5df2c3e01..ca2e56862d 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -209,6 +209,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); + _meshNumVertices = (int)modelMesh->getNumVertices(); const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); @@ -238,19 +239,16 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in initCache(model); - if (_isBlendShaped) { - auto buffer = model->_blendshapeBuffers.find(meshIndex); - if (buffer != model->_blendshapeBuffers.end()) { - _blendshapeBuffer = buffer->second; - } - } - -#ifdef Q_OS_MAC - // On mac AMD, we specifically need to have a _blendshapeBuffer bound when using a deformed mesh pipeline +#if defined(Q_OS_MAC) || defined(Q_OS_ANDROID) + // On mac AMD, we specifically need to have a _meshBlendshapeBuffer bound when using a deformed mesh pipeline // it cannot be null otherwise we crash in the drawcall using a deformed pipeline with a skinned only (not blendshaped) mesh - if ((_isBlendShaped || _isSkinned) && !_blendshapeBuffer) { - glm::vec4 data; - _blendshapeBuffer = std::make_shared(sizeof(glm::vec4), reinterpret_cast(&data)); + if (_isBlendShaped) { + std::vector data(_meshNumVertices); + const auto blendShapeBufferSize = _meshNumVertices * sizeof(BlendshapeOffset); + _meshBlendshapeBuffer = std::make_shared(blendShapeBufferSize, reinterpret_cast(data.data()), blendShapeBufferSize); + } else if (_isSkinned) { + BlendshapeOffset data; + _meshBlendshapeBuffer = std::make_shared(sizeof(BlendshapeOffset), reinterpret_cast(&data), sizeof(BlendshapeOffset)); } #endif @@ -262,8 +260,8 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) { _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - const FBXGeometry& geometry = model->getFBXGeometry(); - const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + const HFMModel& hfmModel = model->getHFMModel(); + const HFMMesh& mesh = hfmModel.meshes.at(_meshIndex); _isBlendShaped = !mesh.blendshapes.isEmpty(); _hasTangents = !mesh.tangents.isEmpty(); @@ -292,8 +290,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clu if (!_clusterBuffer) { _clusterBuffer = std::make_shared(clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) clusterMatrices.data()); - } - else { + } else { _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) clusterMatrices.data()); } @@ -313,8 +310,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), (const gpu::Byte*) clusterDualQuaternions.data()); - } - else { + } else { _clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), (const gpu::Byte*) clusterDualQuaternions.data()); } @@ -403,8 +399,8 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setInputFormat((_drawMesh->getVertexFormat())); - if (_blendshapeBuffer) { - batch.setResourceBuffer(0, _blendshapeBuffer); + if (_meshBlendshapeBuffer) { + batch.setResourceBuffer(0, _meshBlendshapeBuffer); } batch.setInputStream(0, _drawMesh->getVertexStream()); } @@ -431,7 +427,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // IF deformed pass the mesh key - auto drawcallInfo = (uint16_t) (((_isBlendShaped && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1)); + auto drawcallInfo = (uint16_t) (((_isBlendShaped && _meshBlendshapeBuffer && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1)); if (drawcallInfo) { batch.setDrawcallUniform(drawcallInfo); } @@ -483,3 +479,12 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& blendshapeBuffers, const QVector& blendedMeshSizes) { + if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { + auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); + if (blendshapeBuffer != blendshapeBuffers.end()) { + _meshBlendshapeBuffer = blendshapeBuffer->second; + } + } +} diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ceed4b330b..29c0091f11 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -26,10 +26,9 @@ class Model; class MeshPartPayload { public: - - MeshPartPayload() {} + MeshPartPayload() = default; MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material); - virtual ~MeshPartPayload() {} + virtual ~MeshPartPayload() = default; typedef render::Payload Payload; typedef Payload::DataPointer Pointer; @@ -134,10 +133,13 @@ public: bool _isBlendShaped { false }; bool _hasTangents { false }; + void setBlendshapeBuffer(const std::unordered_map& blendshapeBuffers, const QVector& blendedMeshSizes); + private: void initCache(const ModelPointer& model); - gpu::BufferPointer _blendshapeBuffer; + gpu::BufferPointer _meshBlendshapeBuffer; + int _meshNumVertices; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; }; diff --git a/libraries/render-utils/src/MetaModelPayload.cpp b/libraries/render-utils/src/MetaModelPayload.cpp new file mode 100644 index 0000000000..510972d86a --- /dev/null +++ b/libraries/render-utils/src/MetaModelPayload.cpp @@ -0,0 +1,55 @@ +// +// MetaModelPayload.cpp +// +// Created by Sam Gondelman on 10/9/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "MetaModelPayload.h" + +#include "AbstractViewStateInterface.h" +#include "MeshPartPayload.h" + +void MetaModelPayload::setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, const render::ItemIDs& subRenderItems) { + PROFILE_RANGE(render, __FUNCTION__); + if (blendNumber < _appliedBlendNumber) { + return; + } + _appliedBlendNumber = blendNumber; + + // We have fewer meshes than before. Invalidate everything + if (blendedMeshSizes.length() < (int)_blendshapeBuffers.size()) { + _blendshapeBuffers.clear(); + } + + int index = 0; + for (int i = 0; i < blendedMeshSizes.size(); i++) { + int numVertices = blendedMeshSizes.at(i); + + // This mesh isn't blendshaped + if (numVertices == 0) { + _blendshapeBuffers.erase(i); + continue; + } + + const auto& buffer = _blendshapeBuffers.find(i); + const auto blendShapeBufferSize = numVertices * sizeof(BlendshapeOffset); + if (buffer == _blendshapeBuffers.end()) { + _blendshapeBuffers[i] = std::make_shared(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset), blendShapeBufferSize); + } else { + buffer->second->setData(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset)); + } + + index += numVertices; + } + + render::Transaction transaction; + for (auto& id : subRenderItems) { + transaction.updateItem(id, [this, blendedMeshSizes](ModelMeshPartPayload& data) { + data.setBlendshapeBuffer(_blendshapeBuffers, blendedMeshSizes); + }); + } + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} diff --git a/libraries/render-utils/src/MetaModelPayload.h b/libraries/render-utils/src/MetaModelPayload.h new file mode 100644 index 0000000000..83731f3039 --- /dev/null +++ b/libraries/render-utils/src/MetaModelPayload.h @@ -0,0 +1,30 @@ +// +// MetaModelPayload.h +// +// Created by Sam Gondelman on 10/9/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MetaModelPayload_h +#define hifi_MetaModelPayload_h + +#include + +#include "Model.h" + +#include "gpu/Buffer.h" + +class MetaModelPayload { +public: + void setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, const render::ItemIDs& subRenderItems); + +private: + std::unordered_map _blendshapeBuffers; + int _appliedBlendNumber { 0 }; + +}; + +#endif \ No newline at end of file diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a700c200f8..7da7a45e83 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -62,8 +62,6 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _url(HTTP_INVALID_COM), - _blendNumber(0), - _appliedBlendNumber(0), _isWireframe(false), _renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build()) { @@ -185,11 +183,11 @@ bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { return true; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); 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)geometry.meshes.size() || meshIndex >= (int)_meshStates.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; @@ -280,8 +278,8 @@ void Model::setRenderItemsNeedUpdate() { void Model::reset() { if (isLoaded()) { - const FBXGeometry& geometry = getFBXGeometry(); - _rig.reset(geometry); + const HFMModel& hfmModel = getHFMModel(); + _rig.reset(hfmModel); emit rigReset(); emit rigReady(); } @@ -297,13 +295,13 @@ bool Model::updateGeometry() { _needsReload = false; // TODO: should all Models have a valid _rig? - if (_rig.jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + if (_rig.jointStatesEmpty() && getHFMModel().joints.size() > 0) { initJointStates(); assert(_meshStates.empty()); - const FBXGeometry& fbxGeometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); int i = 0; - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); @@ -311,7 +309,7 @@ bool Model::updateGeometry() { initializeBlendshapes(mesh, i); i++; } - _blendshapeBuffersInitialized = true; + _blendshapeOffsetsInitialized = true; needFullUpdate = true; emit rigReady(); } @@ -321,10 +319,10 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates() { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.initJointStates(geometry, modelOffset); + _rig.initJointStates(hfmModel, modelOffset); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -365,9 +363,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g int bestShapeID = 0; int bestSubMeshIndex = 0; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); if (!_triangleSetsValid) { - calculateTriangleSets(geometry); + calculateTriangleSets(hfmModel); } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); @@ -450,7 +448,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; - extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); + extraInfo["subMeshName"] = hfmModel.getModelNameOfMesh(bestSubMeshIndex); extraInfo["subMeshTriangleWorld"] = QVariantMap{ { "v0", vec3toVariant(bestWorldTriangle.v0) }, { "v1", vec3toVariant(bestWorldTriangle.v1) }, @@ -508,9 +506,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co int bestShapeID = 0; int bestSubMeshIndex = 0; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); if (!_triangleSetsValid) { - calculateTriangleSets(geometry); + calculateTriangleSets(hfmModel); } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); @@ -597,7 +595,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; - extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); + extraInfo["subMeshName"] = hfmModel.getModelNameOfMesh(bestSubMeshIndex); extraInfo["subMeshTriangleWorld"] = QVariantMap{ { "v0", vec3toVariant(bestWorldTriangle.v0) }, { "v1", vec3toVariant(bestWorldTriangle.v1) }, @@ -643,7 +641,7 @@ bool Model::convexHullContains(glm::vec3 point) { QMutexLocker locker(&_mutex); if (!_triangleSetsValid) { - calculateTriangleSets(getFBXGeometry()); + calculateTriangleSets(getHFMModel()); } // If we are inside the models box, then consider the submeshes... @@ -755,29 +753,29 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe } // update triangles for picking { - FBXGeometry geometry; + HFMModel hfmModel; for (const auto& newMesh : meshes) { - FBXMesh mesh; + HFMMesh mesh; mesh._mesh = newMesh.getMeshPointer(); mesh.vertices = buffer_helpers::mesh::attributeToVector(mesh._mesh, gpu::Stream::POSITION); int numParts = (int)newMesh.getMeshPointer()->getNumParts(); for (int partID = 0; partID < numParts; partID++) { - FBXMeshPart part; + HFMMeshPart part; part.triangleIndices = buffer_helpers::bufferToVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); mesh.parts << part; } { foreach (const glm::vec3& vertex, mesh.vertices) { glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)); - geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); - geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + hfmModel.meshExtents.minimum = glm::min(hfmModel.meshExtents.minimum, transformedVertex); + hfmModel.meshExtents.maximum = glm::max(hfmModel.meshExtents.maximum, transformedVertex); mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex); mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); } } - geometry.meshes << mesh; + hfmModel.meshes << mesh; } - calculateTriangleSets(geometry); + calculateTriangleSets(hfmModel); } return true; } @@ -791,12 +789,12 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } - const FBXGeometry& geometry = getFBXGeometry(); - int numberOfMeshes = geometry.meshes.size(); + const HFMModel& hfmModel = getHFMModel(); + int numberOfMeshes = hfmModel.meshes.size(); int shapeID = 0; for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& fbxMesh = geometry.meshes.at(i); - if (auto mesh = fbxMesh._mesh) { + const HFMMesh& hfmMesh = hfmModel.meshes.at(i); + if (auto mesh = hfmMesh._mesh) { result.append(mesh); int numParts = (int)mesh->getNumParts(); @@ -810,24 +808,24 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } -void Model::calculateTriangleSets(const FBXGeometry& geometry) { +void Model::calculateTriangleSets(const HFMModel& hfmModel) { PROFILE_RANGE(render, __FUNCTION__); - int numberOfMeshes = geometry.meshes.size(); + int numberOfMeshes = hfmModel.meshes.size(); _triangleSetsValid = true; _modelSpaceMeshTriangleSets.clear(); _modelSpaceMeshTriangleSets.resize(numberOfMeshes); for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); const int numberOfParts = mesh.parts.size(); auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; meshTriangleSets.resize(numberOfParts); for (int j = 0; j < numberOfParts; j++) { - const FBXMeshPart& part = mesh.parts.at(j); + const HFMMeshPart& part = mesh.parts.at(j); auto& partTriangleSet = meshTriangleSets[j]; @@ -841,7 +839,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; partTriangleSet.reserve(totalTriangles); - auto meshTransform = geometry.offset * mesh.modelTransform; + auto meshTransform = hfmModel.offset * mesh.modelTransform; if (part.quadIndices.size() > 0) { int vIndex = 0; @@ -979,7 +977,8 @@ const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { bool Model::addToScene(const render::ScenePointer& scene, render::Transaction& transaction, - render::Item::Status::Getters& statusGetters) { + render::Item::Status::Getters& statusGetters, + BlendShapeOperator modelBlendshapeOperator) { if (!_addedToScene && isLoaded()) { updateClusterMatrices(); if (_modelMeshRenderItems.empty()) { @@ -987,10 +986,11 @@ bool Model::addToScene(const render::ScenePointer& scene, } } + _modelBlendshapeOperator = modelBlendshapeOperator; + bool somethingAdded = false; if (_modelMeshRenderItemsMap.empty()) { - bool hasTransparent = false; size_t verticesCount = 0; foreach(auto renderItem, _modelMeshRenderItems) { @@ -1032,9 +1032,8 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti _modelMeshMaterialNames.clear(); _modelMeshRenderItemShapes.clear(); - _blendshapeBuffers.clear(); _blendshapeOffsets.clear(); - _blendshapeBuffersInitialized = false; + _blendshapeOffsetsInitialized = false; _addedToScene = false; @@ -1115,7 +1114,7 @@ Extents Model::getBindExtents() const { if (!isActive()) { return Extents(); } - const Extents& bindExtents = getFBXGeometry().bindExtents; + const Extents& bindExtents = getHFMModel().bindExtents; Extents scaledExtents = { bindExtents.minimum * _scale, bindExtents.maximum * _scale }; return scaledExtents; } @@ -1129,12 +1128,12 @@ Extents Model::getMeshExtents() const { if (!isActive()) { return Extents(); } - const Extents& extents = getFBXGeometry().meshExtents; + const Extents& extents = getHFMModel().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMModel().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMModel().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum * _scale, maximum * _scale }; return scaledExtents; } @@ -1144,12 +1143,12 @@ Extents Model::getUnscaledMeshExtents() const { return Extents(); } - const Extents& extents = getFBXGeometry().meshExtents; + const Extents& extents = getHFMModel().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMModel().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMModel().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; return scaledExtents; @@ -1172,18 +1171,18 @@ void Model::setJointTranslation(int index, bool valid, const glm::vec3& translat } int Model::getParentJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).parentIndex : -1; + return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).parentIndex : -1; } int Model::getLastFreeJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; + return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).freeLineage.last() : -1; } void Model::setTextures(const QVariantMap& textures) { if (isLoaded()) { - _pendingTextures.clear(); _needsFixupInScene = true; _renderGeometry->setTextures(textures); + _pendingTextures.clear(); } else { _pendingTextures = textures; } @@ -1276,7 +1275,7 @@ QStringList Model::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return isActive() ? getFBXGeometry().getJointNames() : QStringList(); + return isActive() ? getHFMModel().getJointNames() : QStringList(); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1416,12 +1415,12 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); @@ -1437,7 +1436,7 @@ void Model::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } @@ -1445,9 +1444,8 @@ void Model::updateClusterMatrices() { void Model::deleteGeometry() { _deleteGeometryCounter++; - _blendshapeBuffers.clear(); _blendshapeOffsets.clear(); - _blendshapeBuffersInitialized = false; + _blendshapeOffsetsInitialized = false; _meshStates.clear(); _rig.destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); @@ -1507,7 +1505,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - auto& fbxGeometry = getFBXGeometry(); + auto& hfmModel = getHFMModel(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1517,7 +1515,7 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(fbxGeometry.meshes[i], i); + initializeBlendshapes(hfmModel.meshes[i], i); _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1525,7 +1523,7 @@ void Model::createRenderItemSet() { shapeID++; } } - _blendshapeBuffersInitialized = true; + _blendshapeOffsetsInitialized = true; } bool Model::isRenderable() const { @@ -1602,7 +1600,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string class CollisionRenderGeometry : public Geometry { public: CollisionRenderGeometry(graphics::MeshPointer mesh) { - _fbxGeometry = std::make_shared(); + _hfmModel = std::make_shared(); std::shared_ptr meshes = std::make_shared(); meshes->push_back(mesh); _meshes = meshes; @@ -1654,20 +1652,31 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe void Blender::run() { QVector blendshapeOffsets; + QVector blendedMeshSizes; if (_model && _model->isLoaded()) { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); int offset = 0; - auto meshes = _model->getFBXGeometry().meshes; + auto meshes = _model->getHFMModel().meshes; int meshIndex = 0; - foreach(const FBXMesh& mesh, meshes) { + foreach(const HFMMesh& mesh, meshes) { auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++); if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) { + // Not blendshaped or not initialized + blendedMeshSizes.push_back(0); + continue; + } + + if (mesh.vertices.size() != modelMeshBlendshapeOffsets->second.size()) { + // Mesh sizes don't match. Something has gone wrong + blendedMeshSizes.push_back(0); continue; } blendshapeOffsets += modelMeshBlendshapeOffsets->second; BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset; - offset += modelMeshBlendshapeOffsets->second.size(); + int numVertices = modelMeshBlendshapeOffsets->second.size(); + blendedMeshSizes.push_back(numVertices); + offset += numVertices; std::vector unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size()); const float NORMAL_COEFFICIENT_SCALE = 0.01f; @@ -1679,7 +1688,7 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; - const FBXBlendshape& blendshape = mesh.blendshapes.at(i); + const HFMBlendshape& blendshape = mesh.blendshapes.at(i); tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { for (auto j = range.begin(); j < range.end(); j++) { @@ -1711,7 +1720,7 @@ void Blender::run() { } // post the result to the ModelBlender, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", - Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector, blendshapeOffsets)); + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector, blendshapeOffsets), Q_ARG(QVector, blendedMeshSizes)); } bool Model::maybeStartBlender() { @@ -1722,43 +1731,18 @@ bool Model::maybeStartBlender() { return false; } -void Model::setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets) { - PROFILE_RANGE(render, __FUNCTION__); - if (!isLoaded() || blendNumber < _appliedBlendNumber || !_blendshapeBuffersInitialized) { - return; - } - _appliedBlendNumber = blendNumber; - const FBXGeometry& fbxGeometry = getFBXGeometry(); - int index = 0; - for (int i = 0; i < fbxGeometry.meshes.size(); i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); - auto meshBlendshapeOffsets = _blendshapeOffsets.find(i); - const auto& buffer = _blendshapeBuffers.find(i); - if (mesh.blendshapes.isEmpty() || meshBlendshapeOffsets == _blendshapeOffsets.end() || buffer == _blendshapeBuffers.end()) { - continue; - } - - const auto blendshapeOffsetSize = meshBlendshapeOffsets->second.size() * sizeof(BlendshapeOffset); - buffer->second->setSubData(0, blendshapeOffsetSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset)); - - index += meshBlendshapeOffsets->second.size(); - } -} - -void Model::initializeBlendshapes(const FBXMesh& mesh, int index) { +void Model::initializeBlendshapes(const HFMMesh& mesh, int index) { if (mesh.blendshapes.empty()) { // mesh doesn't have blendshape, did we allocate one though ? - if (_blendshapeBuffers.find(index) != _blendshapeBuffers.end()) { - qWarning() << "Mesh does not have Blendshape yet a blendshapeOffset buffer is allocated ?"; + if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) { + qWarning() << "Mesh does not have Blendshape yet the blendshapeOffsets are allocated ?"; } return; } // Mesh has blendshape, let s allocate the local buffer if not done yet - if (_blendshapeBuffers.find(index) == _blendshapeBuffers.end()) { + if (_blendshapeOffsets.find(index) == _blendshapeOffsets.end()) { QVector blendshapeOffset; blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size()); - const auto blendshapeOffsetsSize = blendshapeOffset.size() * sizeof(BlendshapeOffset); - _blendshapeBuffers[index] = std::make_shared(blendshapeOffsetsSize, (const gpu::Byte*) blendshapeOffset.constData(), blendshapeOffsetsSize); _blendshapeOffsets[index] = blendshapeOffset; } } @@ -1791,10 +1775,14 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets) { +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets, QVector blendedMeshSizes) { if (model) { - model->setBlendedVertices(blendNumber, blendshapeOffsets); + auto blendshapeOperator = model->getModelBlendshapeOperator(); + if (blendshapeOperator) { + blendshapeOperator(blendNumber, blendshapeOffsets, blendedMeshSizes, model->fetchRenderItemIDs()); + } } + { Lock lock(_mutex); _pendingBlenders--; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e42da4ecb1..93a0626d28 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,6 +86,7 @@ struct BlendshapeOffsetUnpacked { }; using BlendshapeOffset = BlendshapeOffsetPacked; +using BlendShapeOperator = std::function&, const QVector&, const render::ItemIDs&)>; /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public std::enable_shared_from_this, public scriptable::ModelProvider { @@ -141,7 +142,14 @@ public: } bool addToScene(const render::ScenePointer& scene, render::Transaction& transaction, - render::Item::Status::Getters& statusGetters); + BlendShapeOperator modelBlendshapeOperator) { + auto getters = render::Item::Status::Getters(0); + return addToScene(scene, transaction, getters, modelBlendshapeOperator); + } + bool addToScene(const render::ScenePointer& scene, + render::Transaction& transaction, + render::Item::Status::Getters& statusGetters, + BlendShapeOperator modelBlendshapeOperator = nullptr); void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction); bool isRenderable() const; @@ -155,10 +163,7 @@ public: bool maybeStartBlender(); - /// Sets blended vertices computed in a separate thread. - void setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets); - - bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } + bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isHFMModelLoaded(); } bool isAddedToScene() const { return _addedToScene; } void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } @@ -179,8 +184,8 @@ public: Q_INVOKABLE virtual void setTextures(const QVariantMap& textures); /// Provided as a convenience, will crash if !isLoaded() - // And so that getGeometry() isn't chained everywhere - const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return _renderGeometry->getFBXGeometry(); } + // And so that getHFMModel() isn't chained everywhere + const HFMModel& getHFMModel() const { assert(isLoaded()); return _renderGeometry->getHFMModel(); } bool isActive() const { return isLoaded(); } @@ -339,6 +344,7 @@ public: uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } const QMap& getRenderItems() const { return _modelMeshRenderItemsMap; } + BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; } void renderDebugMeshBoxes(gpu::Batch& batch); @@ -432,24 +438,19 @@ protected: virtual void deleteGeometry(); - QVector _blendshapeCoefficients; - QUrl _url; - std::unordered_map _blendshapeBuffers; - bool _blendshapeBuffersInitialized{ false }; - - QVector>> _dilatedTextures; - + BlendShapeOperator _modelBlendshapeOperator { nullptr }; + QVector _blendshapeCoefficients; QVector _blendedBlendshapeCoefficients; - int _blendNumber; - int _appliedBlendNumber; + int _blendNumber { 0 }; + bool _blendshapeOffsetsInitialized { false }; mutable QMutex _mutex{ QMutex::Recursive }; bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; - void calculateTriangleSets(const FBXGeometry& geometry); + void calculateTriangleSets(const HFMModel& hfmModel); std::vector> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes virtual void createRenderItemSet(); @@ -460,7 +461,6 @@ protected: // debug rendering support int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; - static AbstractViewStateInterface* _viewState; QVector> _modelMeshRenderItems; @@ -506,7 +506,7 @@ protected: bool shouldInvalidatePayloadShapeKey(int meshIndex); - void initializeBlendshapes(const FBXMesh& mesh, int index); + void initializeBlendshapes(const HFMMesh& mesh, int index); private: float _loadingPriority { 0.0f }; @@ -533,7 +533,7 @@ public: bool shouldComputeBlendshapes() { return _computeBlendshapes; } public slots: - void setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets); + void setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets, QVector blendedMeshSizes); void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; } private: diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 9aee0e57a4..4422d15d5c 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -197,12 +197,14 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer }); } -void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) { +void ExtractFrustums::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { assert(renderContext->args); assert(renderContext->args->_context); RenderArgs* args = renderContext->args; + const auto& lightFrame = inputs; + // Return view frustum auto& viewFrustum = output[VIEW_FRUSTUM].edit(); if (!viewFrustum) { @@ -216,7 +218,7 @@ void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Out for (auto i = 0; i < SHADOW_CASCADE_FRUSTUM_COUNT; i++) { auto& shadowFrustum = output[SHADOW_CASCADE0_FRUSTUM+i].edit(); if (lightStage) { - auto globalShadow = lightStage->getCurrentKeyShadow(); + auto globalShadow = lightStage->getCurrentKeyShadow(*lightFrame); if (globalShadow && i<(int)globalShadow->getCascadeCount()) { auto& cascade = globalShadow->getCascade(i); @@ -229,3 +231,21 @@ void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Out } } } + +void FetchCurrentFrames::run(const render::RenderContextPointer& renderContext, Outputs& outputs) { + auto lightStage = renderContext->_scene->getStage(); + assert(lightStage); + outputs.edit0() = std::make_shared(lightStage->_currentFrame); + + auto backgroundStage = renderContext->_scene->getStage(); + assert(backgroundStage); + outputs.edit1() = std::make_shared(backgroundStage->_currentFrame); + + auto hazeStage = renderContext->_scene->getStage(); + assert(hazeStage); + outputs.edit2() = std::make_shared(hazeStage->_currentFrame); + + auto bloomStage = renderContext->_scene->getStage(); + assert(bloomStage); + outputs.edit3() = std::make_shared(bloomStage->_currentFrame); +} diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h index 65f8cdfbfc..9b611bc38d 100644 --- a/libraries/render-utils/src/RenderCommonTask.h +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -13,6 +13,11 @@ #include #include "LightingModel.h" +#include "LightStage.h" +#include "BackgroundStage.h" +#include "HazeStage.h" +#include "BloomStage.h" + class BeginGPURangeTimer { public: using JobModel = render::Job::ModelO; @@ -106,10 +111,22 @@ public: FRUSTUM_COUNT }; - using Output = render::VaryingArray; - using JobModel = render::Job::ModelO; + using Inputs = LightStage::FramePointer; + using Outputs = render::VaryingArray; + using JobModel = render::Job::ModelIO; - void run(const render::RenderContextPointer& renderContext, Output& output); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); +}; + + +class FetchCurrentFrames { +public: + using Outputs = render::VaryingSet4; + using JobModel = render::Job::ModelO; + + FetchCurrentFrames() {} + + void run(const render::RenderContextPointer& renderContext, Outputs& outputs); }; #endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 57f5c3ec34..4052b6bd5a 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -180,29 +180,32 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. task.addJob("DrawLight", lights); + // Fetch the current frame stacks from all the stages + const auto currentFrames = task.addJob("FetchCurrentFrames"); + const auto lightFrame = currentFrames.getN(0); + const auto backgroundFrame = currentFrames.getN(1); + const auto hazeFrame = currentFrames.getN(2); + const auto bloomFrame = currentFrames.getN(3); + // Light Clustering // Create the cluster grid of lights, cpu job for now - const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, linearDepthTarget).asVarying(); + const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, lightFrame, linearDepthTarget).asVarying(); const auto lightClusters = task.addJob("LightClustering", lightClusteringPassInputs); - - // Add haze model - const auto hazeModel = task.addJob("HazeModel"); // DeferredBuffer is complete, now let's shade it into the LightingBuffer const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, - surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource, lightClusters, hazeModel).asVarying(); - + surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource, lightClusters, lightFrame, hazeFrame).asVarying(); task.addJob("RenderDeferred", deferredLightingInputs, renderShadows); - // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job - task.addJob("DrawBackgroundDeferred", lightingModel); + const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame).asVarying(); + task.addJob("DrawBackgroundDeferred", backgroundInputs); - const auto drawHazeInputs = render::Varying(DrawHaze::Inputs(hazeModel, lightingFramebuffer, linearDepthTarget, deferredFrameTransform, lightingModel)); + const auto drawHazeInputs = render::Varying(DrawHaze::Inputs(hazeFrame, lightingFramebuffer, linearDepthTarget, deferredFrameTransform, lightingModel, lightFrame)); task.addJob("DrawHazeDeferred", drawHazeInputs); // Render transparent objects forward in LightingBuffer - const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel, lightClusters, jitter).asVarying(); + const auto transparentsInputs = DrawDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, jitter).asVarying(); task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); // Light Cluster Grid Debuging job @@ -246,8 +249,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Antialiasing", antialiasingInputs); // Add bloom - const auto bloomModel = task.addJob("BloomModel"); - const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomModel).asVarying(); + const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomFrame).asVarying(); task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping @@ -261,11 +263,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawLightBounds", lights); task.addJob("DrawZones", zones); - const auto frustums = task.addJob("ExtractFrustums"); - const auto viewFrustum = frustums.getN(ExtractFrustums::VIEW_FRUSTUM); + const auto frustums = task.addJob("ExtractFrustums", lightFrame); + const auto viewFrustum = frustums.getN(ExtractFrustums::VIEW_FRUSTUM); task.addJob("DrawViewFrustum", viewFrustum, glm::vec3(0.0f, 1.0f, 0.0f)); for (auto i = 0; i < ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT; i++) { - const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_CASCADE0_FRUSTUM + i); + const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_CASCADE0_FRUSTUM + i); float tint = 1.0f - i / float(ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT - 1); char jobName[64]; sprintf(jobName, "DrawShadowFrustum%d", i); @@ -290,7 +292,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Debugging stages { // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, velocityBuffer, deferredFrameTransform)); + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, velocityBuffer, deferredFrameTransform, lightFrame)); task.addJob("DebugDeferredBuffer", debugFramebuffers); const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, @@ -315,7 +317,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawStatus", drawStatusInputs, DrawStatus(statusIconMap)); } - task.addJob("DrawZoneStack", deferredFrameTransform); + const auto debugZoneInputs = DebugZoneLighting::Inputs(deferredFrameTransform, lightFrame, backgroundFrame).asVarying(); + task.addJob("DrawZoneStack", debugZoneInputs); } // Upscale to finale resolution @@ -351,9 +354,11 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& auto config = std::static_pointer_cast(renderContext->jobConfig); const auto& inItems = inputs.get0(); - const auto& lightingModel = inputs.get1(); - const auto& lightClusters = inputs.get2(); - const auto jitter = inputs.get3(); + const auto& hazeFrame = inputs.get1(); + const auto& lightFrame = inputs.get2(); + const auto& lightingModel = inputs.get3(); + const auto& lightClusters = inputs.get4(); + const auto jitter = inputs.get5(); auto deferredLightingEffect = DependencyManager::get(); RenderArgs* args = renderContext->args; @@ -378,13 +383,13 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& batch.setUniformBuffer(ru::Buffer::LightModel, lightingModel->getParametersBuffer()); // Set the light - deferredLightingEffect->setupKeyLightBatch(args, batch); + deferredLightingEffect->setupKeyLightBatch(args, batch, *lightFrame); deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); // Setup haze if current zone has haze - auto hazeStage = args->_scene->getStage(); - if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { - graphics::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); + const auto& hazeStage = args->_scene->getStage(); + if (hazeStage && hazeFrame->_hazes.size() > 0) { + const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front()); if (hazePointer) { batch.setUniformBuffer(ru::Buffer::HazeParams, hazePointer->getHazeParametersBuffer()); } diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 161a14c943..c18daa6d3d 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -17,6 +17,7 @@ #include "LightingModel.h" #include "LightClusters.h" #include "RenderShadowTask.h" +#include "HazeStage.h" class DrawDeferredConfig : public render::Job::Config { Q_OBJECT @@ -42,7 +43,7 @@ protected: class DrawDeferred { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet6; using Config = DrawDeferredConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 9ab60786b5..53f89aaec3 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -32,7 +32,6 @@ #include "FramebufferCache.h" #include "TextureCache.h" #include "RenderCommonTask.h" -#include "LightStage.h" namespace ru { using render_utils::slot::texture::Texture; @@ -59,13 +58,13 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Extract opaques / transparents / lights / metas / overlays / background const auto& opaques = items.get0()[RenderFetchCullSortTask::OPAQUE_SHAPE]; const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; - // const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT]; + //const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT]; const auto& metas = items.get0()[RenderFetchCullSortTask::META]; const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; //const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND]; - // const auto& spatialSelection = items[1]; + //const auto& spatialSelection = items[1]; fadeEffect->build(task, opaques); @@ -76,10 +75,17 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Filter zones from the general metas bucket const auto zones = task.addJob("ZoneRenderer", metas); + // Fetch the current frame stacks from all the stages + const auto currentFrames = task.addJob("FetchCurrentFrames"); + const auto lightFrame = currentFrames.getN(0); + const auto backgroundFrame = currentFrames.getN(1); + //const auto hazeFrame = currentFrames.getN(2); + //const auto bloomFrame = currentFrames.getN(3); + // GPU jobs: Start preparing the main framebuffer const auto framebuffer = task.addJob("PrepareFramebuffer"); - task.addJob("PrepareForward", lightingModel); + task.addJob("PrepareForward", lightFrame); // draw a stencil mask in hidden regions of the framebuffer. task.addJob("PrepareStencil", framebuffer); @@ -101,7 +107,8 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("DrawOpaques", opaqueInputs, shapePlumber); // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job - task.addJob("DrawBackgroundDeferred", lightingModel); + const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame).asVarying(); + task.addJob("DrawBackgroundForward", backgroundInputs); // Draw transparent objects forward const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying(); @@ -114,8 +121,8 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("DrawTransparentBounds", transparents); task.addJob("DrawZones", zones); - task.addJob("DrawZoneStack", deferredFrameTransform); - + const auto debugZoneInputs = DebugZoneLighting::Inputs(deferredFrameTransform, lightFrame, backgroundFrame).asVarying(); + task.addJob("DrawZoneStack", debugZoneInputs); } // Lighting Buffer ready for tone mapping @@ -180,12 +187,12 @@ void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs graphics::LightPointer keySunLight; auto lightStage = args->_scene->getStage(); if (lightStage) { - keySunLight = lightStage->getCurrentKeyLight(); + keySunLight = lightStage->getCurrentKeyLight(*inputs); } graphics::LightPointer keyAmbiLight; if (lightStage) { - keyAmbiLight = lightStage->getCurrentAmbientLight(); + keyAmbiLight = lightStage->getCurrentAmbientLight(*inputs); } if (keySunLight) { diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 22e75d4bdc..54341d1ded 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -15,6 +15,7 @@ #include #include #include "LightingModel.h" +#include "LightStage.h" class RenderForwardTask { public: @@ -40,7 +41,7 @@ private: class PrepareForward { public: - using Inputs = LightingModelPointer; + using Inputs = LightStage::FramePointer; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 5551bbdfa8..a3f06c8942 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -229,7 +229,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip } void initForwardPipelines(ShapePlumber& plumber) { - using namespace shader::render_utils::program; + using namespace shader::render_utils; using Key = render::ShapeKey; auto addPipelineBind = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4); @@ -244,33 +244,33 @@ void initForwardPipelines(ShapePlumber& plumber) { forceLightBatchSetter = true; // Simple Opaques - addPipeline(Key::Builder(), simple); - addPipeline(Key::Builder().withUnlit(), simpleUnlit); + addPipeline(Key::Builder(), program::forward_simple_textured); + addPipeline(Key::Builder().withUnlit(), program::forward_simple_textured_unlit); // Simple Translucents - addPipeline(Key::Builder().withTranslucent(), simpleTranslucent); - addPipeline(Key::Builder().withTranslucent().withUnlit(), simpleTranslucentUnlit); + addPipeline(Key::Builder().withTranslucent(), program::forward_simple_textured_transparent); + addPipeline(Key::Builder().withTranslucent().withUnlit(), program::simple_transparent_textured_unlit); // Opaques - addPipeline(Key::Builder().withMaterial(), forward_model); - addPipeline(Key::Builder().withMaterial().withUnlit(), forward_model_unlit); - addPipeline(Key::Builder().withMaterial().withTangents(), forward_model_translucent); + addPipeline(Key::Builder().withMaterial(), program::forward_model); + addPipeline(Key::Builder().withMaterial().withUnlit(), program::forward_model_unlit); + addPipeline(Key::Builder().withMaterial().withTangents(), program::forward_model_translucent); // Deformed Opaques - addPipeline(Key::Builder().withMaterial().withDeformed(), forward_deformed_model); - addPipeline(Key::Builder().withMaterial().withDeformed().withTangents(), forward_deformed_model_normal_map); - addPipeline(Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), forward_deformed_model_dq); - addPipeline(Key::Builder().withMaterial().withDeformed().withTangents().withDualQuatSkinned(), forward_deformed_model_normal_map_dq); + addPipeline(Key::Builder().withMaterial().withDeformed(), program::forward_deformed_model); + addPipeline(Key::Builder().withMaterial().withDeformed().withTangents(), program::forward_deformed_model_normal_map); + addPipeline(Key::Builder().withMaterial().withDeformed().withDualQuatSkinned(), program::forward_deformed_model_dq); + addPipeline(Key::Builder().withMaterial().withDeformed().withTangents().withDualQuatSkinned(), program::forward_deformed_model_normal_map_dq); // Translucents - addPipeline(Key::Builder().withMaterial().withTranslucent(), forward_model_translucent); - addPipeline(Key::Builder().withMaterial().withTranslucent().withTangents(), forward_model_normal_map_translucent); + addPipeline(Key::Builder().withMaterial().withTranslucent(), program::forward_model_translucent); + addPipeline(Key::Builder().withMaterial().withTranslucent().withTangents(), program::forward_model_normal_map_translucent); // Deformed Translucents - addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent(), forward_deformed_translucent); - addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent().withTangents(), forward_deformed_translucent_normal_map); - addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent().withDualQuatSkinned(), forward_deformed_translucent_dq); - addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent().withTangents().withDualQuatSkinned(), forward_deformed_translucent_normal_map_dq); + addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent(), program::forward_deformed_translucent); + addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent().withTangents(), program::forward_deformed_translucent_normal_map); + addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent().withDualQuatSkinned(), program::forward_deformed_translucent_dq); + addPipeline(Key::Builder().withMaterial().withDeformed().withTranslucent().withTangents().withDualQuatSkinned(), program::forward_deformed_translucent_normal_map_dq); forceLightBatchSetter = false; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7c24c08c27..c4fa297965 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -24,6 +24,8 @@ #include "RenderUtilsLogging.h" +#include "RenderCommonTask.h" + // These values are used for culling the objects rendered in the shadow map // but are readjusted afterwards #define SHADOW_FRUSTUM_NEAR 1.0f @@ -40,10 +42,6 @@ void RenderShadowTask::configure(const Config& configuration) { } void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cameraCullFunctor, uint8_t tagBits, uint8_t tagMask) { - ::CullFunctor shadowCullFunctor = [this](const RenderArgs* args, const AABox& bounds) { - return _cullFunctor(args, bounds); - }; - // Prepare the ShapePipeline ShapePlumberPointer shapePlumber = std::make_shared(); { @@ -54,7 +52,12 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto setupOutput = task.addJob("ShadowSetup"); + // FIXME: calling this here before the zones/lights are drawn during the deferred/forward passes means we're actually using the frames from the previous draw + // Fetch the current frame stacks from all the stages + const auto currentFrames = task.addJob("FetchCurrentFrames"); + const auto lightFrame = currentFrames.getN(0); + + const auto setupOutput = task.addJob("ShadowSetup", lightFrame); const auto queryResolution = setupOutput.getN(1); // Fetch and cull the items from the scene @@ -89,7 +92,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); - const auto cascadeSetupOutput = task.addJob(jobName, i, _cullFunctor, tagBits, tagMask); + const auto cascadeSetupOutput = task.addJob(jobName, lightFrame, i, tagBits, tagMask); const auto shadowFilter = cascadeSetupOutput.getN(0); auto antiFrustum = render::Varying(ViewFrustumPointer()); cascadeFrustums[i] = cascadeSetupOutput.getN(1); @@ -97,14 +100,15 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende antiFrustum = cascadeFrustums[i - 2]; } - // CPU jobs: finer grained culling - const auto cullInputs = CullShadowBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying(); + const auto cullInputs = CullShadowBounds::Inputs(sortedShapes, shadowFilter, antiFrustum, lightFrame, cascadeSetupOutput.getN(2)).asVarying(); sprintf(jobName, "CullShadowCascade%d", i); - const auto culledShadowItemsAndBounds = task.addJob(jobName, cullInputs, shadowCullFunctor); + const auto culledShadowItemsAndBounds = task.addJob(jobName, cullInputs); // GPU jobs: Render to shadow map sprintf(jobName, "RenderShadowMap%d", i); - task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i); + const auto shadowInputs = RenderShadowMap::Inputs(culledShadowItemsAndBounds.getN(0), + culledShadowItemsAndBounds.getN(1), lightFrame).asVarying(); + task.addJob(jobName, shadowInputs, shapePlumber, i); sprintf(jobName, "ShadowCascadeTeardown%d", i); task.addJob(jobName, shadowFilter); @@ -204,11 +208,12 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con const auto& inShapes = inputs.get0(); const auto& inShapeBounds = inputs.get1(); + const auto& lightFrame = inputs.get2(); auto lightStage = renderContext->_scene->getStage(); assert(lightStage); - auto shadow = lightStage->getCurrentKeyShadow(); + auto shadow = lightStage->getCurrentKeyShadow(*lightFrame); if (!shadow || _cascadeIndex >= shadow->getCascadeCount()) { return; } @@ -328,11 +333,12 @@ void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) { _bias[cascadeIndex]._slope = value * value * value * 0.01f; } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, const Inputs& input, Outputs& output) { // Abort all jobs if not casting shadows auto lightStage = renderContext->_scene->getStage(); + auto lightFrame = *input; assert(lightStage); - if (!lightStage->getCurrentKeyLight() || !lightStage->getCurrentKeyLight()->getCastShadows()) { + if (!lightStage->getCurrentKeyLight(lightFrame) || !lightStage->getCurrentKeyLight(lightFrame)->getCastShadows()) { renderContext->taskFlow.abortTask(); return; } @@ -346,7 +352,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O *_cameraFrustum = args->getViewFrustum(); output.edit2() = _cameraFrustum; - const auto globalShadow = lightStage->getCurrentKeyShadow(); + const auto globalShadow = lightStage->getCurrentKeyShadow(lightFrame); if (globalShadow) { globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); @@ -413,15 +419,18 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O } } -void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { +void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, const Inputs& input, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); + const auto& lightFrame = *input; assert(lightStage); // Cache old render args RenderArgs* args = renderContext->args; - const auto globalShadow = lightStage->getCurrentKeyShadow(); - if (globalShadow && _cascadeIndexgetCascadeCount()) { + RenderShadowTask::CullFunctor cullFunctor; + + const auto globalShadow = lightStage->getCurrentKeyShadow(lightFrame); + if (globalShadow && _cascadeIndex < globalShadow->getCascadeCount()) { // Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers) output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask); @@ -434,13 +443,14 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto minTexelCount = 24.0f; // TODO : maybe adapt that with LOD management system? texelSize *= minTexelCount; - _cullFunctor._minSquareSize = texelSize * texelSize; + cullFunctor._minSquareSize = texelSize * texelSize; output.edit1() = cascadeFrustum; } else { output.edit0() = ItemFilter::Builder::nothing(); output.edit1() = ViewFrustumPointer(); } + output.edit2() = cullFunctor; } void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { @@ -498,13 +508,20 @@ void CullShadowBounds::run(const render::RenderContextPointer& renderContext, co outShapes.clear(); outBounds = AABox(); + const auto& lightFrame = *inputs.get3(); + auto cullFunctor = inputs.get4(); + + render::CullFunctor shadowCullFunctor = [cullFunctor](const RenderArgs* args, const AABox& bounds) { + return cullFunctor(args, bounds); + }; + if (!filter.selectsNothing()) { auto& details = args->_details.edit(RenderDetails::SHADOW); - render::CullTest test(_cullFunctor, args, details, antiFrustum); + render::CullTest test(shadowCullFunctor, args, details, antiFrustum); auto scene = args->_scene; auto lightStage = renderContext->_scene->getStage(); assert(lightStage); - const auto globalLightDir = lightStage->getCurrentKeyLight()->getDirection(); + const auto globalLightDir = lightStage->getCurrentKeyLight(lightFrame)->getDirection(); auto castersFilter = render::ItemFilter::Builder(filter).withShadowCaster().build(); const auto& receiversFilter = filter; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 77892305fb..8aaf554514 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -19,11 +19,13 @@ #include "Shadows_shared.slh" +#include "LightStage.h" + class ViewFrustum; class RenderShadowMap { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using JobModel = render::Job::ModelI; RenderShadowMap(render::ShapePlumberPointer shapePlumber, unsigned int cascadeIndex) : _shapePlumber{ shapePlumber }, _cascadeIndex{ cascadeIndex } {} @@ -98,13 +100,14 @@ signals: class RenderShadowSetup { public: + using Inputs = LightStage::FramePointer; using Outputs = render::VaryingSet3; using Config = RenderShadowSetupConfig; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::ModelIO; RenderShadowSetup(); void configure(const Config& configuration); - void run(const render::RenderContextPointer& renderContext, Outputs& output); + void run(const render::RenderContextPointer& renderContext, const Inputs& input, Outputs& output); private: @@ -121,19 +124,19 @@ private: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet2; - using JobModel = render::Job::ModelO; + using Inputs = LightStage::FramePointer; + using Outputs = render::VaryingSet3; + using JobModel = render::Job::ModelIO; - RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : - _cascadeIndex{ cascadeIndex }, _cullFunctor{ cullFunctor }, _tagBits(tagBits), _tagMask(tagMask) {} - void run(const render::RenderContextPointer& renderContext, Outputs& output); + RenderShadowCascadeSetup(unsigned int cascadeIndex, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : + _cascadeIndex(cascadeIndex), _tagBits(tagBits), _tagMask(tagMask) {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& input, Outputs& output); private: - unsigned int _cascadeIndex; - RenderShadowTask::CullFunctor& _cullFunctor; - uint8_t _tagBits{ 0x00 }; - uint8_t _tagMask{ 0x00 }; + uint8_t _tagBits { 0x00 }; + uint8_t _tagMask { 0x00 }; }; class RenderShadowCascadeTeardown { @@ -152,20 +155,11 @@ public: class CullShadowBounds { public: - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet5; using Outputs = render::VaryingSet2; using JobModel = render::Job::ModelIO; - CullShadowBounds(render::CullFunctor cullFunctor) : - _cullFunctor{ cullFunctor } { - } - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); - -private: - - render::CullFunctor _cullFunctor; - }; #endif // hifi_RenderShadowTask_h diff --git a/libraries/render-utils/src/ShadingModel.slh b/libraries/render-utils/src/ShadingModel.slh index 6b0b7bca18..99aa01cc5e 100644 --- a/libraries/render-utils/src/ShadingModel.slh +++ b/libraries/render-utils/src/ShadingModel.slh @@ -15,7 +15,7 @@ <@func declareBeckmannSpecular()@> -layout(binding=RENDER_UTILS_TEXTURE_SSSC_SPECULAR_BECKMANN) uniform sampler2D scatteringSpecularBeckmann; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SSSC_SPECULAR_BECKMANN) uniform sampler2D scatteringSpecularBeckmann; float fetchSpecularBeckmann(float ndoth, float roughness) { return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 5115a876fe..9506c9805d 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -19,7 +19,7 @@ #define SHADOW_SCREEN_SPACE_DITHER 1 // the shadow texture -layout(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowMaps; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowMaps; // Sample the shadowMap with PCF (built-in) float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) { diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 99c4b923f4..9819dac38c 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -13,7 +13,7 @@ <@include Shadows_shared.slh@> -layout(std140, binding=RENDER_UTILS_BUFFER_SHADOW_PARAMS) uniform shadowTransformBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_SHADOW_PARAMS) uniform shadowTransformBuffer { ShadowParameters shadow; }; diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 622ca946c2..63246e85a1 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -18,7 +18,7 @@ const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; -layout(std140, binding=GRAPHICS_BUFFER_SKINNING) uniform skinClusterBuffer { +LAYOUT_STD140(binding=GRAPHICS_BUFFER_SKINNING) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 114ccab712..f26bad86b0 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -41,14 +41,14 @@ void SoftAttachmentModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); @@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 84b51d626a..e004e66501 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -421,6 +421,10 @@ void DebugSubsurfaceScattering::configure(const Config& config) { _showSpecularTable = config.showSpecularTable; _showCursorPixel = config.showCursorPixel; _debugCursorTexcoord = config.debugCursorTexcoord; + if (!_debugParams) { + _debugParams = std::make_shared(sizeof(glm::vec4), nullptr); + } + _debugParams->setSubData(0, _debugCursorTexcoord); } @@ -479,6 +483,10 @@ void DebugSubsurfaceScattering::run(const render::RenderContextPointer& renderCo assert(lightStage); // const auto light = DependencyManager::get()->getLightStage()->getLight(0); const auto light = lightStage->getLight(0); + if (!_debugParams) { + _debugParams = std::make_shared(sizeof(glm::vec4), nullptr); + _debugParams->setSubData(0, _debugCursorTexcoord); + } gpu::doInBatch("DebugSubsurfaceScattering::run", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); @@ -521,9 +529,7 @@ void DebugSubsurfaceScattering::run(const render::RenderContextPointer& renderCo batch.setResourceTexture(ru::Texture::DeferredNormal, deferredFramebuffer->getDeferredNormalTexture()); batch.setResourceTexture(ru::Texture::DeferredColor, deferredFramebuffer->getDeferredColorTexture()); batch.setResourceTexture(ru::Texture::DeferredDepth, linearDepthTexture); - - - batch._glUniform2f(gpu::slot::uniform::Extra0, _debugCursorTexcoord.x, _debugCursorTexcoord.y); + batch.setUniformBuffer(1, _debugParams); batch.draw(gpu::TRIANGLE_STRIP, 4); } } diff --git a/libraries/render-utils/src/SubsurfaceScattering.h b/libraries/render-utils/src/SubsurfaceScattering.h index 780ce34d7f..e0073d23e8 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.h +++ b/libraries/render-utils/src/SubsurfaceScattering.h @@ -179,6 +179,7 @@ private: gpu::PipelinePointer _showLUTPipeline; gpu::PipelinePointer getShowLUTPipeline(); + gpu::BufferPointer _debugParams; bool _showProfile{ false }; bool _showLUT{ false }; bool _showSpecularTable{ false }; diff --git a/libraries/render-utils/src/SubsurfaceScattering.slh b/libraries/render-utils/src/SubsurfaceScattering.slh index 3d37f52e4d..66b3ab1ea0 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.slh +++ b/libraries/render-utils/src/SubsurfaceScattering.slh @@ -56,7 +56,7 @@ vec3 generateProfile(vec2 uv) { <@func declareSubsurfaceScatteringProfileMap()@> -layout(binding=RENDER_UTILS_TEXTURE_SSSC_PROFILE) uniform sampler2D scatteringProfile; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SSSC_PROFILE) uniform sampler2D scatteringProfile; vec3 scatter(float r) { return texture(scatteringProfile, vec2(r * 0.5, 0.5)).rgb; @@ -104,7 +104,7 @@ vec3 integrate(float cosTheta, float skinRadius) { <@func declareSubsurfaceScatteringResource()@> -layout(binding=RENDER_UTILS_TEXTURE_SSSC_LUT) uniform sampler2D scatteringLUT; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SSSC_LUT) uniform sampler2D scatteringLUT; vec3 fetchBRDF(float LdotN, float curvature) { return texture(scatteringLUT, vec2( clamp(LdotN * 0.5 + 0.5, 0.0, 1.0), clamp(2.0 * curvature, 0.0, 1.0))).xyz; @@ -124,7 +124,7 @@ struct ScatteringParameters { vec4 debugFlags; }; -layout(binding=RENDER_UTILS_BUFFER_SSSC_PARAMS) uniform subsurfaceScatteringParametersBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_SSSC_PARAMS) uniform subsurfaceScatteringParametersBuffer { ScatteringParameters parameters; }; diff --git a/libraries/render-utils/src/WorkloadResource.slh b/libraries/render-utils/src/WorkloadResource.slh index 81b6ed78ce..14fa33a12e 100644 --- a/libraries/render-utils/src/WorkloadResource.slh +++ b/libraries/render-utils/src/WorkloadResource.slh @@ -25,8 +25,8 @@ struct WorkloadProxy { vec4 region; }; -#if defined(GPU_GL410) -layout(binding=0) uniform samplerBuffer workloadProxiesBuffer; +#if !defined(GPU_SSBO_TRANSFORM_OBJECT) +LAYOUT(binding=GPU_RESOURCE_BUFFER_SLOT0_TEXTURE) uniform samplerBuffer workloadProxiesBuffer; WorkloadProxy getWorkloadProxy(int i) { int offset = 2 * i; WorkloadProxy proxy; @@ -35,7 +35,7 @@ WorkloadProxy getWorkloadProxy(int i) { return proxy; } #else -layout(std140, binding=0) buffer workloadProxiesBuffer { +LAYOUT_STD140(binding=GPU_RESOURCE_BUFFER_SLOT0_STORAGE) buffer workloadProxiesBuffer { WorkloadProxy _proxies[]; }; WorkloadProxy getWorkloadProxy(int i) { @@ -57,17 +57,23 @@ struct WorkloadView { vec4 regions[3]; }; -#if defined(GPU_GL410) -layout(binding=1) uniform samplerBuffer workloadViewsBuffer; +#if !defined(GPU_SSBO_TRANSFORM_OBJECT) +LAYOUT(binding=GPU_RESOURCE_BUFFER_SLOT1_TEXTURE) uniform samplerBuffer workloadViewsBuffer; WorkloadView getWorkloadView(int i) { - int offset = 2 * i; + int offset = 8 * i; WorkloadView view; - view.origin = texelFetch(workloadViewsBuffer, offset); - view.radiuses = texelFetch(workloadViewsBuffer, offset + 1); + view.direction_far = texelFetch(workloadViewsBuffer, offset + 0); + view.fov = texelFetch(workloadViewsBuffer, offset + 1); + view.origin = texelFetch(workloadViewsBuffer, offset + 2); + view.backFront[0] = texelFetch(workloadViewsBuffer, offset + 3); + view.backFront[1] = texelFetch(workloadViewsBuffer, offset + 4); + view.regions[0] = texelFetch(workloadViewsBuffer, offset + 5); + view.regions[1] = texelFetch(workloadViewsBuffer, offset + 6); + view.regions[2] = texelFetch(workloadViewsBuffer, offset + 7); return view; } #else -layout(std140, binding=1) buffer workloadViewsBuffer { +LAYOUT_STD140(binding=GPU_RESOURCE_BUFFER_SLOT1_STORAGE) buffer workloadViewsBuffer { WorkloadView _views[]; }; WorkloadView getWorkloadView(int i) { diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 1a1b3706f9..4ffc8730a7 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -39,28 +39,16 @@ namespace gr { using namespace render; -class SetupZones { -public: - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; - - SetupZones() {} - - void run(const RenderContextPointer& context, const Inputs& inputs); - -protected: -}; - const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" }; -void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& ouput) { +void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& output) { // Filter out the sorted list of zones const auto zoneItems = task.addJob("FilterZones", input, ZONES_SELECTION.c_str()); // just setup the current zone env task.addJob("SetupZones", zoneItems); - ouput = zoneItems; + output = zoneItems; } void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) { @@ -130,27 +118,29 @@ const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() { void DebugZoneLighting::run(const render::RenderContextPointer& context, const Inputs& inputs) { RenderArgs* args = context->args; - auto deferredTransform = inputs; + auto deferredTransform = inputs.get0(); + auto lightFrame = inputs.get1(); + auto backgroundFrame = inputs.get2(); auto lightStage = context->_scene->getStage(LightStage::getName()); std::vector keyLightStack; - if (lightStage && lightStage->_currentFrame._sunLights.size()) { - for (auto index : lightStage->_currentFrame._sunLights) { + if (lightStage && lightFrame->_sunLights.size()) { + for (auto index : lightFrame->_sunLights) { keyLightStack.push_back(lightStage->getLight(index)); } } std::vector ambientLightStack; - if (lightStage && lightStage->_currentFrame._ambientLights.size()) { - for (auto index : lightStage->_currentFrame._ambientLights) { + if (lightStage && lightFrame->_ambientLights.size()) { + for (auto index : lightFrame->_ambientLights) { ambientLightStack.push_back(lightStage->getLight(index)); } } auto backgroundStage = context->_scene->getStage(BackgroundStage::getName()); std::vector skyboxStack; - if (backgroundStage && backgroundStage->_currentFrame._backgrounds.size()) { - for (auto index : backgroundStage->_currentFrame._backgrounds) { + if (backgroundStage && backgroundFrame->_backgrounds.size()) { + for (auto index : backgroundFrame->_backgrounds) { auto background = backgroundStage->getBackground(index); if (background) { skyboxStack.push_back(background->getSkybox()); @@ -158,7 +148,6 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I } } - gpu::doInBatch("DebugZoneLighting::run", args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); diff --git a/libraries/render-utils/src/ZoneRenderer.h b/libraries/render-utils/src/ZoneRenderer.h index 6e85243d1a..1646c5977d 100644 --- a/libraries/render-utils/src/ZoneRenderer.h +++ b/libraries/render-utils/src/ZoneRenderer.h @@ -16,6 +16,19 @@ #include "DeferredFrameTransform.h" +#include "LightStage.h" +#include "BackgroundStage.h" + +class SetupZones { +public: + using Inputs = render::ItemBounds; + using JobModel = render::Job::ModelI; + + SetupZones() {} + + void run(const render::RenderContextPointer& context, const Inputs& inputs); +}; + class ZoneRendererConfig : public render::Task::Config { Q_OBJECT Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) @@ -44,7 +57,7 @@ public: ZoneRendererTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + void build(JobModel& task, const render::Varying& inputs, render::Varying& output); void configure(const Config& config) { _maxDrawn = config.maxDrawn; } @@ -59,7 +72,7 @@ public: Config(bool enabled = false) : JobConfig(enabled) {} }; - using Inputs = DeferredFrameTransformPointer; + using Inputs = render::VaryingSet3; using JobModel = render::Job::ModelI; DebugZoneLighting() {} diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf index 013640d910..ccbe5c491f 100644 --- a/libraries/render-utils/src/debug_deferred_buffer.slf +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -16,8 +16,8 @@ <@include gpu/Color.slh@> <$declareColorWheel()$> -layout(binding=RENDER_UTILS_DEBUG_TEXTURE0) uniform sampler2D debugTexture0; -layout(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowMaps; +LAYOUT(binding=RENDER_UTILS_DEBUG_TEXTURE0) uniform sampler2D debugTexture0; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowMaps; <@include ShadowCore.slh@> @@ -25,7 +25,7 @@ layout(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowM <@include debug_deferred_buffer_shared.slh@> -layout(std140, binding=RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS) uniform parametersBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS) uniform parametersBuffer { DebugParameters parameters; }; @@ -36,7 +36,13 @@ float curvatureAO(float k) { layout(location=0) in vec2 uv; layout(location=0) out vec4 outFragColor; -//SOURCE_PLACEHOLDER +//SOURCE_PLACEHOLDER_BEGIN +vec4 getFragmentColor() { + DeferredFragment frag = unpackDeferredFragmentNoPosition(uv); + return vec4(pow(frag.albedo, vec3(1.0 / 2.2)), 1.0); +} +//SOURCE_PLACEHOLDER_END + void main(void) { outFragColor = getFragmentColor(); diff --git a/libraries/render-utils/src/deferred_light_point.slv b/libraries/render-utils/src/deferred_light_point.slv index 1f4c66b6e5..3e6329be83 100644 --- a/libraries/render-utils/src/deferred_light_point.slv +++ b/libraries/render-utils/src/deferred_light_point.slv @@ -22,7 +22,7 @@ <$declareLightBuffer(256)$> -layout(binding=RENDER_UTILS_BUFFER_LIGHT_INDEX) uniform lightIndexBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_LIGHT_INDEX) uniform lightIndexBuffer { int lightIndex[256]; }; diff --git a/libraries/render-utils/src/deferred_light_spot.slv b/libraries/render-utils/src/deferred_light_spot.slv index c86551936b..0370acc6bc 100644 --- a/libraries/render-utils/src/deferred_light_spot.slv +++ b/libraries/render-utils/src/deferred_light_spot.slv @@ -21,7 +21,7 @@ <$declareLightBuffer(256)$> -layout(binding=RENDER_UTILS_BUFFER_LIGHT_INDEX) uniform lightIndexBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_LIGHT_INDEX) uniform lightIndexBuffer { int lightIndex[256]; }; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord0; diff --git a/libraries/render-utils/src/drawWorkloadView.slv b/libraries/render-utils/src/drawWorkloadView.slv index db4a33c450..2fdf3d773e 100644 --- a/libraries/render-utils/src/drawWorkloadView.slv +++ b/libraries/render-utils/src/drawWorkloadView.slv @@ -32,7 +32,7 @@ struct DrawMesh { vec4 verts[NUM_SEGMENT_PER_VIEW_REGION]; }; -layout(std140, binding=0) uniform DrawMeshBuffer { +LAYOUT_STD140(binding=0) uniform DrawMeshBuffer { DrawMesh _drawMeshBuffer; }; diff --git a/libraries/render-utils/src/forward_simple.slf b/libraries/render-utils/src/forward_simple.slf index ca3a13c024..9c86f9dff1 100644 --- a/libraries/render-utils/src/forward_simple.slf +++ b/libraries/render-utils/src/forward_simple.slf @@ -14,8 +14,10 @@ <@include DefaultMaterials.slh@> <@include ForwardGlobalLight.slh@> -<$declareEvalSkyboxGlobalColor()$> +<@include gpu/Transform.slh@> +<$declareEvalSkyboxGlobalColor()$> +<$declareStandardCameraTransform()$> // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; @@ -35,12 +37,6 @@ layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; layout(location=0) out vec4 _fragColor0; -<@include procedural/ProceduralCommon.slh@> - -#line 1001 -//PROCEDURAL_BLOCK - -#line 2030 void main(void) { vec3 normal = normalize(_normalWS.xyz); vec3 diffuse = _color.rgb; @@ -48,45 +44,18 @@ void main(void) { float shininess = DEFAULT_SHININESS; float emissiveAmount = 0.0; -#ifdef PROCEDURAL - -#ifdef PROCEDURAL_V1 - diffuse = getProceduralColor().rgb; - // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline - //diffuse = pow(diffuse, vec3(2.2)); - emissiveAmount = 1.0; -#else - emissiveAmount = getProceduralColors(diffuse, specular, shininess); -#endif - -#endif - TransformCamera cam = getTransformCamera(); vec3 fragPosition = _positionES.xyz; - if (emissiveAmount > 0.0) { - _fragColor0 = vec4(evalSkyboxGlobalColor( - cam._viewInverse, - 1.0, - DEFAULT_OCCLUSION, - fragPosition, - normal, - diffuse, - specular, - DEFAULT_METALLIC, - max(0.0, 1.0 - shininess / 128.0)), - 1.0); - } else { - _fragColor0 = vec4(evalSkyboxGlobalColor( - cam._viewInverse, - 1.0, - DEFAULT_OCCLUSION, - fragPosition, - normal, - diffuse, - DEFAULT_FRESNEL, - length(specular), - max(0.0, 1.0 - shininess / 128.0)), - 1.0); - } + _fragColor0 = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + DEFAULT_OCCLUSION, + fragPosition, + normal, + diffuse, + DEFAULT_FRESNEL, + length(specular), + max(0.0, 1.0 - shininess / 128.0)), + 1.0); } diff --git a/libraries/render-utils/src/forward_simple_textured.slf b/libraries/render-utils/src/forward_simple_textured.slf index 8570ae6183..ca31550b40 100644 --- a/libraries/render-utils/src/forward_simple_textured.slf +++ b/libraries/render-utils/src/forward_simple_textured.slf @@ -22,7 +22,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/forward_simple_textured_transparent.slf b/libraries/render-utils/src/forward_simple_textured_transparent.slf index 11c44c18a2..11d51bbd78 100644 --- a/libraries/render-utils/src/forward_simple_textured_transparent.slf +++ b/libraries/render-utils/src/forward_simple_textured_transparent.slf @@ -22,7 +22,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/forward_simple_textured_unlit.slf b/libraries/render-utils/src/forward_simple_textured_unlit.slf index 8ca46da499..ddbc5ae4d7 100644 --- a/libraries/render-utils/src/forward_simple_textured_unlit.slf +++ b/libraries/render-utils/src/forward_simple_textured_unlit.slf @@ -20,7 +20,7 @@ layout(location=0) out vec4 _fragColor0; // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; diff --git a/libraries/render-utils/src/fxaa.slf b/libraries/render-utils/src/fxaa.slf index f1096a3054..1539a87550 100644 --- a/libraries/render-utils/src/fxaa.slf +++ b/libraries/render-utils/src/fxaa.slf @@ -22,7 +22,7 @@ precision mediump float; precision mediump int; #endif -layout(binding=0) uniform sampler2D colorTexture; +LAYOUT(binding=0) uniform sampler2D colorTexture; //uniform sampler2D historyTexture; // FIXME make into a uniform buffer or push constant if this shader ever comes into use diff --git a/libraries/render-utils/src/fxaa_blend.slf b/libraries/render-utils/src/fxaa_blend.slf index c051801659..c22982bc3f 100644 --- a/libraries/render-utils/src/fxaa_blend.slf +++ b/libraries/render-utils/src/fxaa_blend.slf @@ -17,13 +17,13 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; -layout(binding=0) uniform sampler2D colorTexture; +LAYOUT(binding=0) uniform sampler2D colorTexture; struct FxaaBlendParams { vec4 sharpenIntensity; }; -layout(binding=0) uniform fxaaBlendParamsBuffer { +LAYOUT(binding=0) uniform fxaaBlendParamsBuffer { FxaaBlendParams params; }; diff --git a/libraries/render-utils/src/glowLine.slv b/libraries/render-utils/src/glowLine.slv index 075b291589..167aeb8c9e 100644 --- a/libraries/render-utils/src/glowLine.slv +++ b/libraries/render-utils/src/glowLine.slv @@ -21,7 +21,7 @@ struct LineData { float width; }; -layout(std140, binding=0) uniform LineDataBuffer { +LAYOUT_STD140(binding=0) uniform LineDataBuffer { LineData _lineData; }; diff --git a/libraries/render-utils/src/grid.slf b/libraries/render-utils/src/grid.slf index c2380c980d..8e9b35dace 100644 --- a/libraries/render-utils/src/grid.slf +++ b/libraries/render-utils/src/grid.slf @@ -20,7 +20,7 @@ struct Grid { vec4 edge; }; -layout(binding=0) uniform gridBuffer { +LAYOUT(binding=0) uniform gridBuffer { Grid grid; }; diff --git a/libraries/render-utils/src/hmd_ui.slf b/libraries/render-utils/src/hmd_ui.slf index eebeb2e060..6895a90f9e 100644 --- a/libraries/render-utils/src/hmd_ui.slf +++ b/libraries/render-utils/src/hmd_ui.slf @@ -13,13 +13,13 @@ // <@include render-utils/ShaderConstants.h@> -layout(binding=0) uniform sampler2D hudTexture; +LAYOUT(binding=0) uniform sampler2D hudTexture; struct HUDData { float alpha; }; -layout(std140, binding=0) uniform hudBuffer { +LAYOUT_STD140(binding=0) uniform hudBuffer { HUDData hud; }; diff --git a/libraries/render-utils/src/hmd_ui.slv b/libraries/render-utils/src/hmd_ui.slv index ab0d77c42a..6e782d1672 100644 --- a/libraries/render-utils/src/hmd_ui.slv +++ b/libraries/render-utils/src/hmd_ui.slv @@ -22,7 +22,7 @@ struct HUDData { float alpha; }; -layout(std140, binding=0) uniform hudBuffer { +LAYOUT_STD140(binding=0) uniform hudBuffer { HUDData hud; }; diff --git a/libraries/render-utils/src/parabola.slv b/libraries/render-utils/src/parabola.slv index 31b3ab8fae..53dfc75cfe 100644 --- a/libraries/render-utils/src/parabola.slv +++ b/libraries/render-utils/src/parabola.slv @@ -22,7 +22,7 @@ struct ParabolaData { ivec3 spare; }; -layout(std140, binding=0) uniform parabolaData { +LAYOUT_STD140(binding=0) uniform parabolaData { ParabolaData _parabolaData; }; diff --git a/libraries/render-utils/src/render-utils/ShaderConstants.h b/libraries/render-utils/src/render-utils/ShaderConstants.h index ccf6314a39..2d777d502f 100644 --- a/libraries/render-utils/src/render-utils/ShaderConstants.h +++ b/libraries/render-utils/src/render-utils/ShaderConstants.h @@ -131,15 +131,6 @@ namespace render_utils { namespace slot { -namespace uniform { -enum Uniform { - TextColor = RENDER_UTILS_UNIFORM_TEXT_COLOR, - TextOutline = RENDER_UTILS_UNIFORM_TEXT_OUTLINE, - TaaSharpenIntensity = GPU_UNIFORM_EXTRA0, - HighlightOutlineWidth = GPU_UNIFORM_EXTRA0, -}; -} - namespace buffer { enum Buffer { DeferredFrameTransform = RENDER_UTILS_BUFFER_DEFERRED_FRAME_TRANSFORM, diff --git a/libraries/render-utils/src/render-utils/debug_deferred_buffer.slp b/libraries/render-utils/src/render-utils/debug_deferred_buffer.slp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libraries/render-utils/src/render-utils/simple.slp b/libraries/render-utils/src/render-utils/simple.slp index 8a6e2e4f99..e69de29bb2 100644 --- a/libraries/render-utils/src/render-utils/simple.slp +++ b/libraries/render-utils/src/render-utils/simple.slp @@ -1 +0,0 @@ -FRAGMENT forward_simple_textured diff --git a/libraries/render-utils/src/render-utils/simpleTranslucent.slp b/libraries/render-utils/src/render-utils/simpleTranslucent.slp deleted file mode 100644 index 0163b09b84..0000000000 --- a/libraries/render-utils/src/render-utils/simpleTranslucent.slp +++ /dev/null @@ -1,2 +0,0 @@ -VERTEX simple -FRAGMENT forward_simple_textured_transparent diff --git a/libraries/render-utils/src/render-utils/simpleTranslucentUnlit.slp b/libraries/render-utils/src/render-utils/simpleTranslucentUnlit.slp deleted file mode 100644 index f1d1ec39be..0000000000 --- a/libraries/render-utils/src/render-utils/simpleTranslucentUnlit.slp +++ /dev/null @@ -1,2 +0,0 @@ -VERTEX simple -FRAGMENT simple_transparent_textured_unlit diff --git a/libraries/render-utils/src/render-utils/simpleUnlit.slp b/libraries/render-utils/src/render-utils/simpleUnlit.slp deleted file mode 100644 index ab491aa290..0000000000 --- a/libraries/render-utils/src/render-utils/simpleUnlit.slp +++ /dev/null @@ -1,2 +0,0 @@ -VERTEX simple -FRAGMENT forward_simple_textured_unlit diff --git a/libraries/render-utils/src/render-utils/simple_transparent.slp b/libraries/render-utils/src/render-utils/simple_transparent.slp new file mode 100644 index 0000000000..10e6b388c4 --- /dev/null +++ b/libraries/render-utils/src/render-utils/simple_transparent.slp @@ -0,0 +1 @@ +VERTEX simple diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index d35396e469..35e670eef8 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -13,14 +13,14 @@ <@include DeferredBufferWrite.slh@> <@include render-utils/ShaderConstants.h@> -layout(binding=0) uniform sampler2D Font; +LAYOUT(binding=0) uniform sampler2D Font; struct TextParams { vec4 color; vec4 outline; }; -layout(binding=0) uniform textParamsBuffer { +LAYOUT(binding=0) uniform textParamsBuffer { TextParams params; }; diff --git a/libraries/render-utils/src/sdf_text3D_transparent.slf b/libraries/render-utils/src/sdf_text3D_transparent.slf index 9dffca2038..6e271e1463 100644 --- a/libraries/render-utils/src/sdf_text3D_transparent.slf +++ b/libraries/render-utils/src/sdf_text3D_transparent.slf @@ -13,14 +13,14 @@ <@include DeferredBufferWrite.slh@> <@include render-utils/ShaderConstants.h@> -layout(binding=0) uniform sampler2D Font; +LAYOUT(binding=0) uniform sampler2D Font; struct TextParams { vec4 color; vec4 outline; }; -layout(binding=0) uniform textParamsBuffer { +LAYOUT(binding=0) uniform textParamsBuffer { TextParams params; }; diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index a7f5151880..039dbc4278 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -35,7 +35,17 @@ layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; <@include procedural/ProceduralCommon.slh@> #line 1001 -//PROCEDURAL_BLOCK +//PROCEDURAL_BLOCK_BEGIN + +vec3 getProceduralColor() { + return _color.rgb; +} + +float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { + return 1.0; +} + +//PROCEDURAL_BLOCK_END #line 2030 void main(void) { diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf deleted file mode 100644 index 97ed0c570c..0000000000 --- a/libraries/render-utils/src/simple_fade.slf +++ /dev/null @@ -1,110 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// simple_fade.frag -// fragment shader -// -// Created by Olivier Prat on 06/05/17. -// Copyright 2017 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 DeferredBufferWrite.slh@> - -<@include Fade.slh@> -<$declareFadeFragmentInstanced()$> - -<@include render-utils/ShaderConstants.h@> - -// the interpolated normal -layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; -layout(location=RENDER_UTILS_ATTR_NORMAL_MS) in vec3 _normalMS; -layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; -layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; -#define _texCoord0 _texCoord01.xy -#define _texCoord1 _texCoord01.zw -layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _positionMS; -layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; -layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; - -// For retro-compatibility -#define _normal _normalWS -#define _modelNormal _normalMS -#define _position _positionMS -#define _eyePosition _positionES - -<@include procedural/ProceduralCommon.slh@> - -#line 1001 -//PROCEDURAL_BLOCK - -#line 2030 -void main(void) { - vec3 fadeEmissive; - FadeObjectParams fadeParams; - - <$fetchFadeObjectParamsInstanced(fadeParams)$> - applyFade(fadeParams, _positionWS.xyz, fadeEmissive); - - vec3 normal = normalize(_normalWS.xyz); - vec3 diffuse = _color.rgb; - vec3 specular = DEFAULT_SPECULAR; - float shininess = DEFAULT_SHININESS; - float emissiveAmount = 0.0; - -#ifdef PROCEDURAL - -#ifdef PROCEDURAL_V1 - specular = getProceduralColor().rgb; - // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline - //specular = pow(specular, vec3(2.2)); - emissiveAmount = 1.0; -#else - emissiveAmount = getProceduralColors(diffuse, specular, shininess); -#endif - -#endif - - const float ALPHA_THRESHOLD = 0.999; - if (_color.a < ALPHA_THRESHOLD) { - if (emissiveAmount > 0.0) { - packDeferredFragmentTranslucent( - normal, - _color.a, - specular+fadeEmissive, - DEFAULT_FRESNEL, - DEFAULT_ROUGHNESS); - } else { - packDeferredFragmentTranslucent( - normal, - _color.a, - diffuse+fadeEmissive, - DEFAULT_FRESNEL, - DEFAULT_ROUGHNESS); - } - } else { - if (emissiveAmount > 0.0) { - packDeferredFragmentLightmap( - normal, - 1.0, - diffuse+fadeEmissive, - max(0.0, 1.0 - shininess / 128.0), - DEFAULT_METALLIC, - specular, - specular); - } else { - packDeferredFragment( - normal, - 1.0, - diffuse, - max(0.0, 1.0 - shininess / 128.0), - length(specular), - DEFAULT_EMISSIVE+fadeEmissive, - DEFAULT_OCCLUSION, - DEFAULT_SCATTERING); - } - } -} diff --git a/libraries/render-utils/src/simple_opaque_web_browser.slf b/libraries/render-utils/src/simple_opaque_web_browser.slf index cf4828d3b3..36b0c825ad 100644 --- a/libraries/render-utils/src/simple_opaque_web_browser.slf +++ b/libraries/render-utils/src/simple_opaque_web_browser.slf @@ -18,7 +18,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index 7676844084..b308b57345 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -17,7 +17,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_textured_fade.slf b/libraries/render-utils/src/simple_textured_fade.slf index 600f19be0f..ad2b636708 100644 --- a/libraries/render-utils/src/simple_textured_fade.slf +++ b/libraries/render-utils/src/simple_textured_fade.slf @@ -20,7 +20,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf index e3d9b9daf6..f33cb704dc 100644 --- a/libraries/render-utils/src/simple_textured_unlit.slf +++ b/libraries/render-utils/src/simple_textured_unlit.slf @@ -18,7 +18,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_textured_unlit_fade.slf b/libraries/render-utils/src/simple_textured_unlit_fade.slf index bffadbe819..494920b363 100644 --- a/libraries/render-utils/src/simple_textured_unlit_fade.slf +++ b/libraries/render-utils/src/simple_textured_unlit_fade.slf @@ -20,7 +20,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_transparent.slf b/libraries/render-utils/src/simple_transparent.slf index 5db54aa770..0e29ed7470 100644 --- a/libraries/render-utils/src/simple_transparent.slf +++ b/libraries/render-utils/src/simple_transparent.slf @@ -39,7 +39,18 @@ layout(location=0) out vec4 _fragColor0; <@include procedural/ProceduralCommon.slh@> #line 1001 -//PROCEDURAL_BLOCK + +//PROCEDURAL_BLOCK_BEGIN + +vec3 getProceduralColor() { + return _color.rgb; +} + +float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { + return 1.0; +} + +//PROCEDURAL_BLOCK_END #line 2030 void main(void) { diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index 5573a7aa22..ef83914096 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -17,7 +17,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_transparent_textured_fade.slf b/libraries/render-utils/src/simple_transparent_textured_fade.slf index 44a3fe2e01..5fac67e1d2 100644 --- a/libraries/render-utils/src/simple_transparent_textured_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_fade.slf @@ -26,7 +26,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/simple_transparent_textured_unlit.slf b/libraries/render-utils/src/simple_transparent_textured_unlit.slf index 9d43e41c2f..bf3dbbdf88 100644 --- a/libraries/render-utils/src/simple_transparent_textured_unlit.slf +++ b/libraries/render-utils/src/simple_transparent_textured_unlit.slf @@ -17,7 +17,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; diff --git a/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf b/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf index 43c28c41c3..943f361ead 100644 --- a/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf @@ -19,7 +19,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; diff --git a/libraries/render-utils/src/simple_transparent_web_browser.slf b/libraries/render-utils/src/simple_transparent_web_browser.slf index df92d238bf..2adc16e278 100644 --- a/libraries/render-utils/src/simple_transparent_web_browser.slf +++ b/libraries/render-utils/src/simple_transparent_web_browser.slf @@ -18,7 +18,7 @@ <@include render-utils/ShaderConstants.h@> // the albedo texture -layout(binding=0) uniform sampler2D originalTexture; +LAYOUT(binding=0) uniform sampler2D originalTexture; // the interpolated normal layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index b149d8f912..f0d522a41c 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -44,7 +44,7 @@ struct AmbientOcclusionParams { float _gaussianCoefs[8]; }; -layout(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBuffer { AmbientOcclusionParams params; }; @@ -232,7 +232,7 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, // the depth pyramid texture -layout(binding=RENDER_UTILS_TEXTURE_SSAO_PYRAMID) uniform sampler2D pyramidMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SSAO_PYRAMID) uniform sampler2D pyramidMap; float getZEye(ivec2 pixel, int level) { return -texelFetch(pyramidMap, pixel, level).x; @@ -313,7 +313,7 @@ float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { <$declareAmbientOcclusion()$> // the source occlusion texture -layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; vec2 fetchOcclusionDepthRaw(ivec2 coords, out vec3 raw) { raw = texelFetch(occlusionMap, coords, 0).xyz; diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index ab7989e35e..e15e52f448 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -26,7 +26,7 @@ struct DebugParams{ vec4 pixelInfo; }; -layout(binding=RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS) uniform debugAmbientOcclusionBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_SSAO_DEBUG_PARAMS) uniform debugAmbientOcclusionBuffer { DebugParams debugParams; }; diff --git a/libraries/render-utils/src/ssao_makePyramid.slf b/libraries/render-utils/src/ssao_makePyramid.slf index c87fe1e682..eae1b853f9 100644 --- a/libraries/render-utils/src/ssao_makePyramid.slf +++ b/libraries/render-utils/src/ssao_makePyramid.slf @@ -14,7 +14,7 @@ <@include ssao.slh@> <$declareAmbientOcclusion()$> -layout(binding=0) uniform sampler2D depthMap; +LAYOUT(binding=0) uniform sampler2D depthMap; layout(location=0) out vec4 outFragColor; diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf index 1a8af0f71c..620c811f75 100644 --- a/libraries/render-utils/src/standardDrawTexture.slf +++ b/libraries/render-utils/src/standardDrawTexture.slf @@ -14,7 +14,7 @@ <@include gpu/ShaderConstants.h@> // the texture -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=GPU_ATTR_POSITION) in vec3 varPosition; layout(location=GPU_ATTR_NORMAL) in vec3 varNormal; diff --git a/libraries/render-utils/src/standardDrawTextureNoBlend.slf b/libraries/render-utils/src/standardDrawTextureNoBlend.slf index 95138d123f..83915cd856 100644 --- a/libraries/render-utils/src/standardDrawTextureNoBlend.slf +++ b/libraries/render-utils/src/standardDrawTextureNoBlend.slf @@ -14,7 +14,7 @@ <@include gpu/ShaderConstants.h@> // the texture -layout(binding=0) uniform sampler2D colorMap; +LAYOUT(binding=0) uniform sampler2D colorMap; layout(location=GPU_ATTR_POSITION) in vec3 varPosition; layout(location=GPU_ATTR_NORMAL) in vec3 varNormal; diff --git a/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf b/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf index 8664fa16fd..877c31c23d 100644 --- a/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf +++ b/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf @@ -26,10 +26,14 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 _fragColor; -// FIXME make into a uniform buffer or push constant if this shader ever comes into use -vec2 uniformCursorTexcoord = vec2(0.5); +struct SSSDebugParams { + vec4 cursorTexCoordSpare2; +}; -//uniform vec3 uniformLightVector = vec3(1.0); +// Deferred frame transform uses slot 0 +LAYOUT_STD140(binding=1) uniform sssDebugParamsBuffer { + SSSDebugParams sssDebugParams; +}; vec3 evalScatteringBRDF(vec2 texcoord) { DeferredFragment fragment = unpackDeferredFragmentNoPosition(texcoord); @@ -76,8 +80,6 @@ vec3 drawScatteringTableUV(vec2 cursor, vec2 texcoord) { vec3 bentNdotL = evalScatteringBentNdotL(normal, midNormal, lowNormal, fragLightDir); - // return clamp(bentNdotL * 0.5 + 0.5, 0.0, 1.0); - vec3 distance = vec3(0.0); for (int c = 0; c < 3; c++) { vec2 BRDFuv = vec2(clamp(bentNdotL[c] * 0.5 + 0.5, 0.0, 1.0), clamp(2.0 * curvature, 0.0, 1.0)); @@ -104,10 +106,8 @@ vec3 drawScatteringTableUV(vec2 cursor, vec2 texcoord) { } void main(void) { - // _fragColor = vec4(evalScatteringBRDF(varTexCoord0), 1.0); - // _fragColor = vec4(uniformCursorTexcoord, 0.0, 1.0); - - _fragColor = vec4(drawScatteringTableUV(uniformCursorTexcoord, varTexCoord0), 1.0); + vec2 cursorTexcoord = sssDebugParams.cursorTexCoordSpare2.xy; + _fragColor = vec4(drawScatteringTableUV(cursorTexcoord, varTexCoord0), 1.0); } diff --git a/libraries/render-utils/src/surfaceGeometry_copyDepth.slf b/libraries/render-utils/src/surfaceGeometry_copyDepth.slf index f018ee1105..efff6e913c 100644 --- a/libraries/render-utils/src/surfaceGeometry_copyDepth.slf +++ b/libraries/render-utils/src/surfaceGeometry_copyDepth.slf @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -layout(binding=0) uniform sampler2D depthMap; +LAYOUT(binding=0) uniform sampler2D depthMap; layout(location=0) out vec4 outFragColor; diff --git a/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf b/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf index 34e78ea4ff..e4020dbdec 100644 --- a/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf +++ b/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf @@ -15,8 +15,8 @@ <@include gpu/PackedNormal.slh@> <@include render-utils/ShaderConstants.h@> -layout(binding=RENDER_UTILS_TEXTURE_SG_DEPTH) uniform sampler2D linearDepthMap; -layout(binding=RENDER_UTILS_TEXTURE_SG_NORMAL) uniform sampler2D normalMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SG_DEPTH) uniform sampler2D linearDepthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SG_NORMAL) uniform sampler2D normalMap; layout(location=0) in vec2 varTexCoord0; diff --git a/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf b/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf index b49bd618da..363fd0d4f8 100644 --- a/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf +++ b/libraries/render-utils/src/surfaceGeometry_makeCurvature.slf @@ -25,7 +25,7 @@ struct SurfaceGeometryParams { vec4 curvatureInfo; }; -layout(binding= RENDER_UTILS_BUFFER_SG_PARAMS) uniform surfaceGeometryParamsBuffer { +LAYOUT(binding= RENDER_UTILS_BUFFER_SG_PARAMS) uniform surfaceGeometryParamsBuffer { SurfaceGeometryParams params; }; @@ -46,7 +46,7 @@ bool isFullResolution() { } -layout(binding=RENDER_UTILS_TEXTURE_SG_DEPTH) uniform sampler2D linearDepthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SG_DEPTH) uniform sampler2D linearDepthMap; float getZEye(ivec2 pixel) { return -texelFetch(linearDepthMap, pixel, 0).x; @@ -59,7 +59,7 @@ vec2 sideToFrameTexcoord(vec2 side, vec2 texcoordPos) { return vec2((texcoordPos.x + side.x) * side.y, texcoordPos.y); } -layout(binding=RENDER_UTILS_TEXTURE_SG_NORMAL) uniform sampler2D normalMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SG_NORMAL) uniform sampler2D normalMap; vec3 getRawNormal(vec2 texcoord) { return texture(normalMap, texcoord).xyz; diff --git a/libraries/render-utils/src/surfaceGeometry_makeLinearDepth.slf b/libraries/render-utils/src/surfaceGeometry_makeLinearDepth.slf index 116f3b7686..fe0c320d1b 100644 --- a/libraries/render-utils/src/surfaceGeometry_makeLinearDepth.slf +++ b/libraries/render-utils/src/surfaceGeometry_makeLinearDepth.slf @@ -16,7 +16,7 @@ <@include DeferredTransform.slh@> <$declareDeferredFrameTransform()$> -layout(binding=RENDER_UTILS_TEXTURE_SG_DEPTH) uniform sampler2D depthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SG_DEPTH) uniform sampler2D depthMap; layout(location=0) out vec4 outFragColor; diff --git a/libraries/render-utils/src/taa.slh b/libraries/render-utils/src/taa.slh index 2161ad9524..784c0824d5 100644 --- a/libraries/render-utils/src/taa.slh +++ b/libraries/render-utils/src/taa.slh @@ -16,11 +16,11 @@ <@include render-utils/ShaderConstants.h@> <@include gpu/Color.slh@> -layout(binding=RENDER_UTILS_TEXTURE_TAA_HISTORY) uniform sampler2D historyMap; -layout(binding=RENDER_UTILS_TEXTURE_TAA_SOURCE) uniform sampler2D sourceMap; -layout(binding=RENDER_UTILS_TEXTURE_TAA_VELOCITY) uniform sampler2D velocityMap; -layout(binding=RENDER_UTILS_TEXTURE_TAA_DEPTH) uniform sampler2D depthMap; -layout(binding=RENDER_UTILS_TEXTURE_TAA_NEXT) uniform sampler2D nextMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TAA_HISTORY) uniform sampler2D historyMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TAA_SOURCE) uniform sampler2D sourceMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TAA_VELOCITY) uniform sampler2D velocityMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TAA_DEPTH) uniform sampler2D depthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TAA_NEXT) uniform sampler2D nextMap; struct TAAParams { @@ -33,7 +33,7 @@ struct TAAParams vec4 regionInfo; }; -layout(std140, binding=RENDER_UTILS_BUFFER_TAA_PARAMS) uniform taaParamsBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_TAA_PARAMS) uniform taaParamsBuffer { TAAParams params; }; diff --git a/libraries/render-utils/src/toneMapping.slf b/libraries/render-utils/src/toneMapping.slf index 8d89e54a1b..29f618c2f0 100644 --- a/libraries/render-utils/src/toneMapping.slf +++ b/libraries/render-utils/src/toneMapping.slf @@ -26,7 +26,7 @@ const int ToneCurveGamma22 = 1; const int ToneCurveReinhard = 2; const int ToneCurveFilmic = 3; -layout(binding=RENDER_UTILS_BUFFER_TM_PARAMS) uniform toneMappingParamsBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_TM_PARAMS) uniform toneMappingParamsBuffer { ToneMappingParams params; }; float getTwoPowExposure() { @@ -36,7 +36,7 @@ int getToneCurve() { return params._toneCurve_s0_s1_s2.x; } -layout(binding=RENDER_UTILS_TEXTURE_TM_COLOR) uniform sampler2D colorMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TM_COLOR) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; diff --git a/libraries/render-utils/src/velocityBuffer_cameraMotion.slf b/libraries/render-utils/src/velocityBuffer_cameraMotion.slf index 083440dbf8..0ec63a7b1d 100644 --- a/libraries/render-utils/src/velocityBuffer_cameraMotion.slf +++ b/libraries/render-utils/src/velocityBuffer_cameraMotion.slf @@ -17,7 +17,7 @@ layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; -layout(binding=RENDER_UTILS_TEXTURE_TAA_DEPTH) uniform sampler2D depthMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_TAA_DEPTH) uniform sampler2D depthMap; void main(void) { diff --git a/libraries/render-utils/src/zone_drawSkybox.slf b/libraries/render-utils/src/zone_drawSkybox.slf index 77de75a305..f8d1326b3a 100644 --- a/libraries/render-utils/src/zone_drawSkybox.slf +++ b/libraries/render-utils/src/zone_drawSkybox.slf @@ -12,13 +12,13 @@ <@include render-utils/ShaderConstants.h@> // FIXME use declareSkyboxMap from LightAmbient.slh? -layout(binding=RENDER_UTILS_TEXTURE_SKYBOX) uniform samplerCube skyboxMap; +LAYOUT(binding=RENDER_UTILS_TEXTURE_SKYBOX) uniform samplerCube skyboxMap; struct Skybox { vec4 color; }; -layout(binding=RENDER_UTILS_BUFFER_DEBUG_SKYBOX) uniform skyboxBuffer { +LAYOUT(binding=RENDER_UTILS_BUFFER_DEBUG_SKYBOX) uniform skyboxBuffer { Skybox skybox; }; diff --git a/libraries/render/src/render/BlurTask.slh b/libraries/render/src/render/BlurTask.slh index c07e71688a..db6b8e3bab 100644 --- a/libraries/render/src/render/BlurTask.slh +++ b/libraries/render/src/render/BlurTask.slh @@ -21,7 +21,7 @@ struct BlurParameters { vec2 taps[BLUR_MAX_NUM_TAPS]; }; -layout(binding=0) uniform blurParamsBuffer { +LAYOUT(binding=0) uniform blurParamsBuffer { BlurParameters parameters; }; @@ -76,7 +76,7 @@ float getPosLinearDepthFar() { <$declareBlurUniforms()$> -layout(binding=0) uniform sampler2D sourceMap; +LAYOUT(binding=0) uniform sampler2D sourceMap; vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) { texcoord = evalTexcoordTransformed(texcoord); @@ -112,8 +112,8 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) { <$declareBlurUniforms()$> -layout(binding=0) uniform sampler2D sourceMap; -layout(binding=1) uniform sampler2D depthMap; +LAYOUT(binding=0) uniform sampler2D sourceMap; +LAYOUT(binding=1) uniform sampler2D depthMap; vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep) { texcoord = evalTexcoordTransformed(texcoord); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index b2930032a3..5e5c6b4c6e 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -205,7 +205,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, if (!srcFilter.selectsNothing()) { auto filter = render::ItemFilter::Builder(srcFilter).withoutSubMetaCulled().build(); - // Now get the bound, and + // Now get the bound, and // filter individually against the _filter // visibility cull if partially selected ( octree cell contianing it was partial) // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 88e85c604a..a87e2031f9 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -531,7 +531,7 @@ public: typedef UpdateFunctor Updater; Payload(const DataPointer& data) : _data(data) {} - virtual ~Payload() {} + virtual ~Payload() = default; // Payload general interface virtual const ItemKey getKey() const override { return payloadGetKey(_data); } diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 2b2accde37..d742428897 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -86,29 +86,30 @@ void ShapePlumber::addPipeline(const Key& key, const gpu::ShaderPointer& program void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& program, const gpu::StatePointer& state, BatchSetter batchSetter, ItemSetter itemSetter) { ShapeKey key{ filter._flags }; + const auto& reflection = program->getReflection(); auto locations = std::make_shared(); - locations->albedoTextureUnit = program->getTextures().isValid(graphics::slot::texture::MaterialAlbedo); - locations->roughnessTextureUnit = program->getTextures().isValid(graphics::slot::texture::MaterialRoughness); - locations->normalTextureUnit = program->getTextures().isValid(graphics::slot::texture::MaterialNormal); - locations->metallicTextureUnit = program->getTextures().isValid(graphics::slot::texture::MaterialMetallic); - locations->emissiveTextureUnit = program->getTextures().isValid(graphics::slot::texture::MaterialEmissiveLightmap); - locations->occlusionTextureUnit = program->getTextures().isValid(graphics::slot::texture::MaterialOcclusion); - locations->lightingModelBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::LightModel); - locations->skinClusterBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::Skinning); - locations->materialBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::Material); - locations->texMapArrayBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::TexMapArray); - locations->keyLightBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::KeyLight); - locations->lightBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::Light); - locations->lightAmbientBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::AmbientLight); - locations->lightAmbientMapUnit = program->getTextures().isValid(graphics::slot::texture::Skybox); - locations->fadeMaskTextureUnit = program->getTextures().isValid(render_utils::slot::texture::FadeMask); - locations->fadeParameterBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::FadeParameters); - locations->fadeObjectParameterBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::FadeObjectParameters); - locations->hazeParameterBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::HazeParams); + locations->albedoTextureUnit = reflection.validTexture(graphics::slot::texture::MaterialAlbedo); + locations->roughnessTextureUnit = reflection.validTexture(graphics::slot::texture::MaterialRoughness); + locations->normalTextureUnit = reflection.validTexture(graphics::slot::texture::MaterialNormal); + locations->metallicTextureUnit = reflection.validTexture(graphics::slot::texture::MaterialMetallic); + locations->emissiveTextureUnit = reflection.validTexture(graphics::slot::texture::MaterialEmissiveLightmap); + locations->occlusionTextureUnit = reflection.validTexture(graphics::slot::texture::MaterialOcclusion); + locations->lightingModelBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::LightModel); + locations->skinClusterBufferUnit = reflection.validUniformBuffer(graphics::slot::buffer::Skinning); + locations->materialBufferUnit = reflection.validUniformBuffer(graphics::slot::buffer::Material); + locations->texMapArrayBufferUnit = reflection.validUniformBuffer(graphics::slot::buffer::TexMapArray); + locations->keyLightBufferUnit = reflection.validUniformBuffer(graphics::slot::buffer::KeyLight); + locations->lightBufferUnit = reflection.validUniformBuffer(graphics::slot::buffer::Light); + locations->lightAmbientBufferUnit = reflection.validUniformBuffer(graphics::slot::buffer::AmbientLight); + locations->lightAmbientMapUnit = reflection.validTexture(graphics::slot::texture::Skybox); + locations->fadeMaskTextureUnit = reflection.validTexture(render_utils::slot::texture::FadeMask); + locations->fadeParameterBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::FadeParameters); + locations->fadeObjectParameterBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::FadeObjectParameters); + locations->hazeParameterBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::HazeParams); if (key.isTranslucent()) { - locations->lightClusterGridBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::LightClusterGrid); - locations->lightClusterContentBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::LightClusterContent); - locations->lightClusterFrustumBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::LightClusterFrustumGrid); + locations->lightClusterGridBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::LightClusterGrid); + locations->lightClusterContentBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::LightClusterContent); + locations->lightClusterFrustumBufferUnit = reflection.validUniformBuffer(render_utils::slot::buffer::LightClusterFrustumGrid); } { diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index bd6ac6521a..24c17d43f1 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -258,7 +258,7 @@ public: using ItemSetter = std::function; - ShapePipeline(gpu::PipelinePointer pipeline, LocationsPointer locations, BatchSetter batchSetter = nullptr, ItemSetter itemSetter = nullptr) : + ShapePipeline(const gpu::PipelinePointer& pipeline, const LocationsPointer& locations, const BatchSetter& batchSetter = nullptr, const ItemSetter& itemSetter = nullptr) : pipeline(pipeline), locations(locations), _batchSetter(batchSetter), diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv index ea4d0f24e6..bb8e6a2886 100644 --- a/libraries/render/src/render/drawItemBounds.slv +++ b/libraries/render/src/render/drawItemBounds.slv @@ -24,7 +24,7 @@ struct DrawItemBoundsParams { vec4 color; }; -layout(binding=0) uniform drawItemBoundsParamsBuffer { +LAYOUT(binding=0) uniform drawItemBoundsParamsBuffer { DrawItemBoundsParams params; }; @@ -34,8 +34,8 @@ struct ItemBound { vec4 boundDim_s; }; -#if defined(GPU_GL410) -layout(binding=0) uniform samplerBuffer ssbo0Buffer; +#if !defined(GPU_SSBO_TRANSFORM_OBJECT) +LAYOUT(binding=GPU_RESOURCE_BUFFER_SLOT0_TEXTURE) uniform samplerBuffer ssbo0Buffer; ItemBound getItemBound(int i) { int offset = 2 * i; ItemBound bound; @@ -44,7 +44,7 @@ ItemBound getItemBound(int i) { return bound; } #else -layout(std140, binding=0) buffer ssbo0Buffer { +LAYOUT_STD140(binding=GPU_RESOURCE_BUFFER_SLOT0_STORAGE) buffer ssbo0Buffer { ItemBound bounds[]; }; ItemBound getItemBound(int i) { diff --git a/libraries/render/src/render/drawItemStatus.slf b/libraries/render/src/render/drawItemStatus.slf index 9409ee6171..e88cf4c920 100644 --- a/libraries/render/src/render/drawItemStatus.slf +++ b/libraries/render/src/render/drawItemStatus.slf @@ -15,7 +15,7 @@ layout(location=0) in vec4 varColor; layout(location=1) in vec3 varTexcoord; layout(location=0) out vec4 outFragColor; -layout(binding=0) uniform sampler2D _icons; +LAYOUT(binding=0) uniform sampler2D _icons; vec2 getIconTexcoord(float icon, vec2 uv) { const vec2 ICON_COORD_SIZE = vec2(0.0625, 1.0); return vec2((uv.x + icon) * ICON_COORD_SIZE.x, uv.y * ICON_COORD_SIZE.y); diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 31436bbf8b..588377c072 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -17,6 +17,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu procedural graphics model-networking ktx recording avatars fbx entities controllers animation audio physics image midi) +link_hifi_libraries(shared networking octree shaders gpu procedural graphics model-networking ktx recording avatars fbx entities controllers animation audio physics image midi) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 1220a9b769..2854445b4f 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -25,13 +25,49 @@ class AudioScriptingInterface : public QObject, public Dependency { // JSDoc for property is in Audio.h. Q_PROPERTY(bool isStereoInput READ isStereoInput WRITE setStereoInput NOTIFY isStereoInputChanged) + Q_PROPERTY(bool isSoloing READ isSoloing) + Q_PROPERTY(QVector soloList READ getSoloList) public: - virtual ~AudioScriptingInterface() {} + virtual ~AudioScriptingInterface() = default; void setLocalAudioInterface(AbstractAudioInterface* audioInterface); + bool isSoloing() const { + return _localAudioInterface->getAudioSolo().isSoloing(); + } + + QVector getSoloList() const { + return _localAudioInterface->getAudioSolo().getUUIDs(); + } + + /**jsdoc + * Add nodes to the audio solo list + * @function Audio.addToSoloList + * @param {Uuid[]} uuidList - List of node UUIDs to add to the solo list. + */ + Q_INVOKABLE void addToSoloList(QVector uuidList) { + _localAudioInterface->getAudioSolo().addUUIDs(uuidList); + } + + /**jsdoc + * Remove nodes from the audio solo list + * @function Audio.removeFromSoloList + * @param {Uuid[]} uuidList - List of node UUIDs to remove from the solo list. + */ + Q_INVOKABLE void removeFromSoloList(QVector uuidList) { + _localAudioInterface->getAudioSolo().removeUUIDs(uuidList); + } + + /**jsdoc + * Reset the list of soloed nodes. + * @function Audio.resetSoloList + */ + Q_INVOKABLE void resetSoloList() { + _localAudioInterface->getAudioSolo().reset(); + } + protected: - AudioScriptingInterface() {} + AudioScriptingInterface() = default; // these methods are protected to stop C++ callers from calling, but invokable from script diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 3bf044fd8b..4e07877d57 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -138,7 +138,8 @@ QString FileScriptingInterface::convertUrlToPath(QUrl url) { // this function is not in use void FileScriptingInterface::downloadZip(QString path, const QString link) { QUrl url = QUrl(link); - auto request = DependencyManager::get()->createResourceRequest(nullptr, url); + auto request = DependencyManager::get()->createResourceRequest( + nullptr, url, true, -1, "FileScriptingInterface::downloadZip"); connect(request, &ResourceRequest::finished, this, [this, path]{ unzipFile(path, ""); // so intellisense isn't mad }); diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index c693083ebf..1716ea72ff 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -192,7 +192,7 @@ QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertex } glm::vec3 pos = vertexBufferView.get(vertexIndex); - return vec3toScriptValue(_modelScriptEngine, pos); + return vec3ToScriptValue(_modelScriptEngine, pos); } diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index dba2db0458..8acf88a7ce 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -109,7 +109,8 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "about to call: ResourceManager::createResourceRequest(this, url); on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; #endif - auto request = DependencyManager::get()->createResourceRequest(nullptr, url); + auto request = DependencyManager::get()->createResourceRequest( + nullptr, url, true, -1, "ScriptCache::getScriptContents"); Q_ASSERT(request); request->setCacheEnabled(!forceDownload); connect(request, &ResourceRequest::finished, this, [=]{ scriptContentAvailable(maxRetries); }); @@ -166,7 +167,8 @@ void ScriptCache::scriptContentAvailable(int maxRetries) { qCDebug(scriptengine) << QString("Retrying script request [%1 / %2]: %3") .arg(attempt).arg(maxRetries).arg(url.toString()); - auto request = DependencyManager::get()->createResourceRequest(nullptr, url); + auto request = DependencyManager::get()->createResourceRequest( + nullptr, url, true, -1, "ScriptCache::scriptContentAvailable"); Q_ASSERT(request); // We've already made a request, so the cache must be disabled or it wasn't there, so enabling diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8cfd2b75cf..be1a9c20ef 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -679,9 +679,9 @@ void ScriptEngine::init() { qScriptRegisterSequenceMetaType>(this); qScriptRegisterSequenceMetaType>(this); - qScriptRegisterSequenceMetaType >(this); - qScriptRegisterSequenceMetaType >(this); - qScriptRegisterSequenceMetaType >(this); + qScriptRegisterSequenceMetaType>(this); + qScriptRegisterSequenceMetaType>(this); + qScriptRegisterSequenceMetaType>(this); QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); diff --git a/libraries/script-engine/src/SpatialEvent.cpp b/libraries/script-engine/src/SpatialEvent.cpp index d06cc556d3..8520c0c485 100644 --- a/libraries/script-engine/src/SpatialEvent.cpp +++ b/libraries/script-engine/src/SpatialEvent.cpp @@ -33,9 +33,9 @@ SpatialEvent::SpatialEvent(const SpatialEvent& event) { QScriptValue SpatialEvent::toScriptValue(QScriptEngine* engine, const SpatialEvent& event) { QScriptValue obj = engine->newObject(); - obj.setProperty("locTranslation", vec3toScriptValue(engine, event.locTranslation) ); + obj.setProperty("locTranslation", vec3ToScriptValue(engine, event.locTranslation) ); obj.setProperty("locRotation", quatToScriptValue(engine, event.locRotation) ); - obj.setProperty("absTranslation", vec3toScriptValue(engine, event.absTranslation) ); + obj.setProperty("absTranslation", vec3ToScriptValue(engine, event.absTranslation) ); obj.setProperty("absRotation", quatToScriptValue(engine, event.absRotation) ); return obj; diff --git a/libraries/script-engine/src/TouchEvent.cpp b/libraries/script-engine/src/TouchEvent.cpp index 6ff591decf..58ac9ec8c1 100644 --- a/libraries/script-engine/src/TouchEvent.cpp +++ b/libraries/script-engine/src/TouchEvent.cpp @@ -14,9 +14,10 @@ #include #include -#include #include +#include "RegisteredMetaTypes.h" + TouchEvent::TouchEvent() : x(0.0f), y(0.0f), @@ -220,7 +221,7 @@ QScriptValue TouchEvent::toScriptValue(QScriptEngine* engine, const TouchEvent& QScriptValue pointsObj = engine->newArray(); int index = 0; foreach (glm::vec2 point, event.points) { - QScriptValue thisPoint = vec2toScriptValue(engine, point); + QScriptValue thisPoint = vec2ToScriptValue(engine, point); pointsObj.setProperty(index, thisPoint); index++; } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 696981d1b4..fe903c07e2 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -21,24 +21,6 @@ #include "GLMHelpers.h" -/**jsdoc - * A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. - * - * @typedef {object} Vec3 - * @property {number} x - X-coordinate of the vector. - * @property {number} y - Y-coordinate of the vector. - * @property {number} z - Z-coordinate of the vector. - */ - -/**jsdoc - * A color vector. See also the {@link Vec3(0)|Vec3} object. - * - * @typedef {object} Vec3Color - * @property {number} x - Red component value. Integer in the range 0 - 255. - * @property {number} y - Green component value. Integer in the range 0 - 255. - * @property {number} z - Blue component value. Integer in the range 0 - 255. - */ - /**jsdoc * The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed * Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index ebc459b2d1..a74d185c6a 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -21,6 +21,7 @@ #include #include +#include "ResourceRequestObserver.h" #include "ScriptEngine.h" const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/"; @@ -189,7 +190,7 @@ void XMLHttpRequestClass::send(const QScriptValue& data) { } void XMLHttpRequestClass::doSend() { - + DependencyManager::get()->update(_url, -1, "XMLHttpRequestClass::doSend"); _reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData); connectToReply(_reply); diff --git a/libraries/shaders/CMakeLists.txt b/libraries/shaders/CMakeLists.txt index a065c635e7..1d9c4d59a4 100644 --- a/libraries/shaders/CMakeLists.txt +++ b/libraries/shaders/CMakeLists.txt @@ -1,16 +1,7 @@ set(TARGET_NAME shaders) autoscribe_shader_libs(gpu graphics display-plugins procedural render render-utils entities-renderer) setup_hifi_library(Gui) - -add_dependencies(${TARGET_NAME} compiled_shaders reflected_shaders) - -# Despite the dependency above, the autogen logic will attempt to compile the QRC before -# the compiled_shaders project is built causing an error on a clean workspace because the -# QRC references files generated by the compiled_shaders target -# To fix that we need to explicitly add every shader as a dependnecy of the autogen process -foreach(COMPILED_SHADER ${COMPILED_SHADERS}) - set_property(TARGET ${TARGET_NAME} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "${COMPILED_SHADER}") -endforeach() +add_dependencies(${TARGET_NAME} scribed_shaders spirv_shaders reflected_shaders) link_hifi_libraries(shared gl) - +target_json() diff --git a/libraries/shaders/ShaderEnums.cpp.in b/libraries/shaders/ShaderEnums.cpp.in index 7f4751f116..042362288d 100644 --- a/libraries/shaders/ShaderEnums.cpp.in +++ b/libraries/shaders/ShaderEnums.cpp.in @@ -1,11 +1,19 @@ #include "ShaderEnums.h" +#include namespace shader { - -uint32_t all_programs[] = { +const std::vector& allPrograms() { + static const std::vector ALL_PROGRAMS{{ @SHADER_PROGRAMS_ARRAY@ - (uint32_t)-1 -}; - + }}; + return ALL_PROGRAMS; } +const std::vector& allShaders() { + static const std::vector ALL_SHADERS{{ +@SHADER_SHADERS_ARRAY@ + }}; + return ALL_SHADERS; +} + +} \ No newline at end of file diff --git a/libraries/shaders/headers/310es/header.glsl b/libraries/shaders/headers/310es/header.glsl new file mode 100644 index 0000000000..ac48d5c94c --- /dev/null +++ b/libraries/shaders/headers/310es/header.glsl @@ -0,0 +1,15 @@ +#version 310 es +#define GPU_GLES +#define GPU_GLES_310 +#define BITFIELD highp int +#define LAYOUT(X) layout(X) +#define LAYOUT_STD140(X) layout(std140, X) +#ifdef VULKAN + #define gl_InstanceID gl_InstanceIndex + #define gl_VertexID gl_VertexIndex +#endif +#extension GL_EXT_texture_buffer : enable +precision highp float; +precision highp samplerBuffer; +precision highp sampler2DShadow; +precision highp sampler2DArrayShadow; diff --git a/libraries/shaders/headers/410/header.glsl b/libraries/shaders/headers/410/header.glsl new file mode 100644 index 0000000000..901ae6f9db --- /dev/null +++ b/libraries/shaders/headers/410/header.glsl @@ -0,0 +1,15 @@ +#version 410 core +#define GPU_GL410 +#define BITFIELD int +#if defined(VULKAN) + #extension GL_ARB_shading_language_420pack : require + #define LAYOUT(X) layout(X) + #define LAYOUT_STD140(X) layout(std140, X) +#else + #define LAYOUT(X) + #define LAYOUT_STD140(X) layout(std140) +#endif +#ifdef VULKAN +#define gl_InstanceID gl_InstanceIndex +#define gl_VertexID gl_VertexIndex +#endif diff --git a/libraries/shaders/headers/450/header.glsl b/libraries/shaders/headers/450/header.glsl new file mode 100644 index 0000000000..6ce61b4378 --- /dev/null +++ b/libraries/shaders/headers/450/header.glsl @@ -0,0 +1,10 @@ +#version 450 core +#define GPU_GL450 +#define GPU_SSBO_TRANSFORM_OBJECT +#define BITFIELD int +#define LAYOUT(X) layout(X) +#define LAYOUT_STD140(X) layout(std140, X) +#ifdef VULKAN +#define gl_InstanceID gl_InstanceIndex +#define gl_VertexID gl_VertexIndex +#endif diff --git a/libraries/shaders/headers/mono.glsl b/libraries/shaders/headers/mono.glsl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libraries/shaders/headers/stereo.glsl b/libraries/shaders/headers/stereo.glsl new file mode 100644 index 0000000000..090035b4a4 --- /dev/null +++ b/libraries/shaders/headers/stereo.glsl @@ -0,0 +1,4 @@ +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN diff --git a/libraries/shaders/src/shaders/Shaders.cpp b/libraries/shaders/src/shaders/Shaders.cpp index ac4810a896..c7e39b2940 100644 --- a/libraries/shaders/src/shaders/Shaders.cpp +++ b/libraries/shaders/src/shaders/Shaders.cpp @@ -8,101 +8,362 @@ #include "Shaders.h" -#include #include #include #include #include -#include -#include -#include -#include +#include +#include +#include + +#include #include -static bool cleanShaders() { -#if defined(Q_OS_MAC) - static const bool CLEAN_SHADERS = true; -#else - static const bool CLEAN_SHADERS = ::gl::disableGl45(); - -#endif - return CLEAN_SHADERS; -} - // Can't use the Q_INIT_RESOURCE macro inside a namespace on Mac, // so this is done out of line -void initShaders() { - static std::once_flag once; - std::call_once(once, [] { - Q_INIT_RESOURCE(shaders); - }); -} -static std::vector splitStringIntoLines(const std::string& s) { - std::stringstream ss(s); - std::vector result; - - std::string line; - while (std::getline(ss, line, '\n')) { - result.push_back(line); - } - return result; -} - -static std::string loadResource(const std::string& path) { - return FileUtils::readFile(path.c_str()).toStdString(); +static void initShadersResources() { + Q_INIT_RESOURCE(shaders); } namespace shader { -void cleanShaderSource(std::string& shaderSource) { - static const std::regex LAYOUT_REGEX{ R"REGEX(^layout\((\s*std140\s*,\s*)?(?:binding|location)\s*=\s*(?:\b\w+\b)\)\s*(?!(?:flat\s+)?(?:out|in|buffer))\b(.*)$)REGEX" }; - static const int GROUP_STD140 = 1; - static const int THE_REST_OF_THE_OWL = 2; - std::vector lines = splitStringIntoLines(shaderSource); - std::vector outLines; - std::unordered_map locationDefines; - for (const auto& line : lines) { - std::cmatch m; - if (std::regex_match(line.c_str(), m, LAYOUT_REGEX)) { - std::string outLine; - if (m[GROUP_STD140].matched) { - outLine = "layout(std140) "; - } - outLine += m[THE_REST_OF_THE_OWL].str(); - outLines.push_back(outLine); - continue; - // On mac we have to strip out all the explicit binding location layouts, - // because GL 4.1 doesn't support them - } - outLines.push_back(line); - } - std::ostringstream joined; - std::copy(outLines.begin(), outLines.end(), std::ostream_iterator(joined, "\n")); - shaderSource = joined.str(); +#if defined(USE_GLES) + +static const Dialect DEFAULT_DIALECT = Dialect::glsl310es; + +const std::vector& allDialects() { + static const std::vector ALL_DIALECTS{ { Dialect::glsl310es } }; + return ALL_DIALECTS; } -std::string loadShaderSource(uint32_t shaderId) { - initShaders(); - auto shaderStr = loadResource(std::string(":/shaders/") + std::to_string(shaderId)); - if (cleanShaders()) { - // OSX only supports OpenGL 4.1 without ARB_shading_language_420pack or - // ARB_explicit_uniform_location or basically anything useful that's - // been released in the last 8 fucking years, so in that case we need to - // strip out all explicit locations and do a bunch of background magic to - // make the system seem like it is using the explicit locations - cleanShaderSource(shaderStr); - } - return shaderStr; -} - -std::string loadShaderReflection(uint32_t shaderId) { - initShaders(); - auto path = std::string(":/shaders/") + std::to_string(shaderId) + std::string("_reflection"); - auto json = loadResource(path); - return json; +#elif defined(Q_OS_MAC) + +static const Dialect DEFAULT_DIALECT = Dialect::glsl410; + +const std::vector& allDialects() { + static const std::vector ALL_DIALECTS{ Dialect::glsl410 }; + return ALL_DIALECTS; } +#else + +static const Dialect DEFAULT_DIALECT = Dialect::glsl450; + +const std::vector & allDialects() { + static const std::vector ALL_DIALECTS{ { Dialect::glsl450, Dialect::glsl410 } }; + return ALL_DIALECTS; } +#endif + +const std::vector& allVariants() { + static const std::vector ALL_VARIANTS{ { Variant::Mono, Variant::Stereo } }; + return ALL_VARIANTS; +} + +const std::string& dialectPath(Dialect dialect) { + static const std::string e310esPath { "/310es/" }; + static const std::string e410Path { "/410/" }; + static const std::string e450Path { "/450/" }; + switch (dialect) { +#if defined(USE_GLES) + case Dialect::glsl310es: return e310esPath; +#else +#if !defined(Q_OS_MAC) + case Dialect::glsl450: return e450Path; +#endif + case Dialect::glsl410: return e410Path; +#endif + default: break; + } + throw std::runtime_error("Invalid dialect"); +} + +static std::string loadResource(const std::string& path) { + if (!QFileInfo(path.c_str()).exists()) { + return {}; + } + return FileUtils::readFile(path.c_str()).toStdString(); +} + +static Binary loadSpirvResource(const std::string& path) { + Binary result; + { + QFile file(path.c_str()); + + if (file.open(QFile::ReadOnly)) { + QByteArray bytes = file.readAll(); + result.resize(bytes.size()); + memcpy(bytes.data(), result.data(), bytes.size()); + } + } + return result; +} + +DialectVariantSource loadDialectVariantSource(const std::string& basePath) { + DialectVariantSource result; + result.scribe = loadResource(basePath + "scribe"); + result.spirv = loadSpirvResource(basePath + "spirv"); + result.glsl = loadResource(basePath + "glsl"); + String reflectionJson = loadResource(basePath + "json"); + result.reflection.parse(reflectionJson); + return result; +} + +DialectSource loadDialectSource(Dialect dialect, uint32_t shaderId) { + std::string basePath = std::string(":/shaders/") + std::to_string(shaderId) + dialectPath(dialect); + DialectSource result; + result.variantSources[Variant::Mono] = loadDialectVariantSource(basePath); + auto stereo = loadDialectVariantSource(basePath + "stereo/"); + if (stereo.valid()) { + result.variantSources[Variant::Stereo] = stereo; + } + return result; +} + +Source::Pointer Source::loadSource(uint32_t shaderId) { + auto result = std::make_shared(); + result->id = shaderId; + const auto& dialects = allDialects(); + result->name = loadResource(std::string(":/shaders/") + std::to_string(shaderId) + std::string("/name")); + for (const auto& dialect : dialects) { + result->dialectSources[dialect] = loadDialectSource(dialect, shaderId); + } + result->reflection = result->dialectSources[DEFAULT_DIALECT].variantSources[Variant::Mono].reflection; + return result; +} + +Source& Source::operator=(const Source& other) { + // DO NOT COPY the shader ID + name = other.name; + dialectSources = other.dialectSources; + replacements = other.replacements; + reflection = other.reflection; + return *this; +} + +const Source& Source::get(uint32_t shaderId) { + static std::once_flag once; + static const std::unordered_map shadersById; + std::call_once(once, [] { + initShadersResources(); + auto& map = const_cast&>(shadersById); + for (const auto& shaderId : allShaders()) { + map[shaderId] = loadSource(shaderId); + } + }); + const auto itr = shadersById.find(shaderId); + static const Source EMPTY_SHADER; + if (itr == shadersById.end()) { + return EMPTY_SHADER; + } + return *(itr->second); +} + +bool Source::doReplacement(String& source) const { + bool replaced = false; + for (const auto& entry : replacements) { + const auto& key = entry.first; + // First try search for a block to replace + // Blocks are required because oftentimes we need a stub function + // in the original source code to allow it to compile. As such we + // need to replace the stub with our own code rather than just inject + // some code. + const auto beginMarker = key + "_BEGIN"; + auto beginIndex = source.find(beginMarker); + if (beginIndex != std::string::npos) { + const auto endMarker = key + "_END"; + auto endIndex = source.find(endMarker, beginIndex); + if (endIndex != std::string::npos) { + auto size = endIndex - beginIndex; + source.replace(beginIndex, size, entry.second); + replaced = true; + continue; + } + } + + // If no block is found, try for a simple line replacement + beginIndex = source.find(key); + if (beginIndex != std::string::npos) { + source.replace(beginIndex, key.size(), entry.second); + replaced = true; + continue; + } + } + + return replaced; +} + +const DialectVariantSource& Source::getDialectVariantSource(Dialect dialect, Variant variant) const { + auto dialectEntry = dialectSources.find(dialect); + if (dialectEntry == dialectSources.end()) { + throw std::runtime_error("Dialect source not found"); + } + + const auto& dialectSource = dialectEntry->second; + auto variantEntry = dialectSource.variantSources.find(variant); + // FIXME revert to mono if stereo source is requested but not present + // (for when mono and stereo sources are the same) + if (variantEntry == dialectSource.variantSources.end()) { + throw std::runtime_error("Variant source not found"); + } + + return variantEntry->second; +} + + +String Source::getSource(Dialect dialect, Variant variant) const { + String result; + const auto& variantSource = getDialectVariantSource(dialect, variant); + if (!replacements.empty()) { + std::string result = variantSource.scribe; + if (doReplacement(result)) { + return result; + } + } + +#ifdef Q_OS_ANDROID + // SPIRV cross injects "#extension GL_OES_texture_buffer : require" into the GLSL shaders, + // which breaks android rendering + return variantSource.scribe; +#else + if (variantSource.glsl.empty()) { + return variantSource.scribe; + } + return variantSource.glsl; +#endif +} + +const Reflection& Source::getReflection(Dialect dialect, Variant variant) const { + const auto& variantSource = getDialectVariantSource(dialect, variant); + return variantSource.reflection; +} + +static const std::string NAME_KEY{ "name" }; +static const std::string INPUTS_KEY{ "inputs" }; +static const std::string OUTPUTS_KEY{ "outputs" }; +static const std::string UBOS_KEY{ "ubos" }; +static const std::string SSBOS_KEY{ "ssbos" }; + +static const std::string TEXTURES_KEY{ "textures" }; +static const std::string LOCATION_KEY{ "location" }; +static const std::string BINDING_KEY{ "binding" }; +static const std::string TYPE_KEY{ "type" }; + +std::unordered_set populateBufferTextureSet(const nlohmann::json& input) { + std::unordered_set result; + static const std::string SAMPLER_BUFFER{ "samplerBuffer" }; + auto arraySize = input.size(); + for (size_t i = 0; i < arraySize; ++i) { + auto entry = input[i]; + std::string name = entry[NAME_KEY]; + std::string type = entry[TYPE_KEY]; + if (type == SAMPLER_BUFFER) { + result.insert(name); + } + } + return result; +} + +Reflection::LocationMap populateLocationMap(const nlohmann::json& input, const std::string& locationKey) { + Reflection::LocationMap result; + auto arraySize = input.size(); + for (size_t i = 0; i < arraySize; ++i) { + auto entry = input[i]; + std::string name = entry[NAME_KEY]; + // Location or binding, depending on the locationKey parameter + int32_t location = entry[locationKey]; + result[name] = location; + } + return result; +} + +void Reflection::parse(const std::string& jsonString) { + if (jsonString.empty()) { + return; + } + using json = nlohmann::json; + auto root = json::parse(jsonString); + + if (root.count(INPUTS_KEY)) { + inputs = populateLocationMap(root[INPUTS_KEY], LOCATION_KEY); + } + if (root.count(OUTPUTS_KEY)) { + outputs = populateLocationMap(root[OUTPUTS_KEY], LOCATION_KEY); + } + if (root.count(SSBOS_KEY)) { + resourceBuffers = populateLocationMap(root[SSBOS_KEY], BINDING_KEY); + } + if (root.count(UBOS_KEY)) { + uniformBuffers = populateLocationMap(root[UBOS_KEY], BINDING_KEY); + } + if (root.count(TEXTURES_KEY)) { + textures = populateLocationMap(root[TEXTURES_KEY], BINDING_KEY); + auto bufferTextures = populateBufferTextureSet(root[TEXTURES_KEY]); + if (!bufferTextures.empty()) { + if (!resourceBuffers.empty()) { + throw std::runtime_error("Input shader has both SSBOs and texture buffers defined"); + } + for (const auto& bufferTexture : bufferTextures){ + resourceBuffers[bufferTexture] = textures[bufferTexture]; + textures.erase(bufferTexture); + } + } + } + updateValid(); + +} + + +static void mergeMap(Reflection::LocationMap& output, const Reflection::LocationMap& input) { + for (const auto& entry : input) { + if (0 != output.count(entry.first)) { + if (output[entry.first] != entry.second) { + throw std::runtime_error("Invalid reflection for merging"); + } + } else { + output[entry.first] = entry.second; + } + } +} + +static void updateValidSet(Reflection::ValidSet& output, const Reflection::LocationMap& input) { + output.clear(); + output.reserve(input.size()); + for (const auto& entry : input) { + output.insert(entry.second); + } +} + +void Reflection::merge(const Reflection& reflection) { + mergeMap(textures, reflection.textures); + mergeMap(uniforms, reflection.uniforms); + mergeMap(uniformBuffers, reflection.uniformBuffers); + mergeMap(resourceBuffers, reflection.resourceBuffers); + updateValid(); +} + +void Reflection::updateValid() { + updateValidSet(validInputs, inputs); + updateValidSet(validOutputs, outputs); + updateValidSet(validTextures, textures); + updateValidSet(validUniformBuffers, uniformBuffers); + updateValidSet(validResourceBuffers, resourceBuffers); + updateValidSet(validUniforms, uniforms); +} + + +std::vector Reflection::getNames(const LocationMap& locations) { + std::vector result; + result.reserve(locations.size()); + for (const auto& entry : locations) { + result.push_back(entry.first); + } + return result; +} + + +} // namespace shader + diff --git a/libraries/shaders/src/shaders/Shaders.h b/libraries/shaders/src/shaders/Shaders.h index 1335c1b49b..025abf7b0b 100644 --- a/libraries/shaders/src/shaders/Shaders.h +++ b/libraries/shaders/src/shaders/Shaders.h @@ -8,27 +8,169 @@ #pragma once #include +#include #include +#include #include -#include +#include + +#include + +#include namespace shader { static const uint32_t INVALID_SHADER = (uint32_t)-1; static const uint32_t INVALID_PROGRAM = (uint32_t)-1; -extern uint32_t all_programs[]; +const std::vector& allPrograms(); +const std::vector& allShaders(); -std::string loadShaderSource(uint32_t shaderId); -std::string loadShaderReflection(uint32_t shaderId); +enum class Dialect +{ +#if defined(USE_GLES) + // GLES only support 3.1 es + glsl310es, +#elif defined(Q_OS_MAC) + // Mac only supports 4.1 + glsl410, +#else + // Everything else supports 4.1 and 4.5 + glsl450, + glsl410, +#endif +}; + +const std::vector& allDialects(); +const std::string& dialectPath(Dialect dialect); + +enum class Variant { + Mono, + Stereo, +}; + +const std::vector& allVariants(); + +static const uint32_t NUM_VARIANTS = 2; + +using Binary = std::vector; +using String = std::string; + +struct EnumClassHash +{ + template + std::size_t operator()(T t) const + { + return static_cast(t); + } +}; + +struct Reflection { + using LocationMap = std::unordered_map; + using ValidSet = std::unordered_set; + + void parse(const std::string& json); + void merge(const Reflection& reflection); + + bool validInput(int32_t location) const { return validLocation(validInputs, location); } + bool validOutput(int32_t location) const { return validLocation(validOutputs, location); } + bool validTexture(int32_t location) const { return validLocation(validTextures, location); } + bool validUniform(int32_t location) const { return validLocation(validUniforms, location); } + bool validUniformBuffer(int32_t location) const { return validLocation(validUniformBuffers, location); } + bool validResourceBuffer(int32_t location) const { return validLocation(validResourceBuffers, location); } + + + LocationMap inputs; + + LocationMap outputs; + + LocationMap textures; + + LocationMap uniformBuffers; + + // Either SSBOs or Textures with the type samplerBuffer, depending on dialect + LocationMap resourceBuffers; + + // Needed for procedural code, will map to push constants for Vulkan + LocationMap uniforms; + + static std::vector getNames(const LocationMap& locations); + +private: + + bool validLocation(const ValidSet& locations, int32_t location) const { + return locations.count(location) != 0; + } + + void updateValid(); + + ValidSet validInputs; + ValidSet validOutputs; + ValidSet validTextures; + ValidSet validUniformBuffers; + ValidSet validResourceBuffers; + ValidSet validUniforms; +}; + +struct DialectVariantSource { + // The output of the scribe application with platforms specific headers + String scribe; + // Optimized SPIRV version of the shader + Binary spirv; + // Regenerated GLSL from the optimized SPIRV + String glsl; + // Shader reflection from the optimized SPIRV + Reflection reflection; + + bool valid() const { return !scribe.empty(); } +}; + +struct DialectSource { + std::unordered_map variantSources; +}; + +struct Source { + using Pointer = std::shared_ptr; + Source() = default; + Source& operator=(const Source& other); + + uint32_t id{ INVALID_SHADER }; + + // The name of the shader file, with extension, i.e. DrawColor.frag + std::string name; + + // Generic reflection, copied from the 450 dialect / mono variant + Reflection reflection; + + // Map of platforms to their specific shaders + std::unordered_map dialectSources; + + // Support for swapping out code blocks for procedural and debugging shaders + std::unordered_map replacements; + + String getSource(Dialect dialect, Variant variant) const; + const Reflection& getReflection(Dialect dialect, Variant variant) const; + bool valid() const { return !dialectSources.empty(); } + static Source generate(const std::string& glsl) { throw std::runtime_error("Implement me"); } + static const Source& get(uint32_t shaderId); + +private: + // Disallow copy construction and assignment + Source(const Source& other) = default; + + static Source::Pointer loadSource(uint32_t shaderId) ; + + bool doReplacement(String& source) const; + const DialectVariantSource& getDialectVariantSource(Dialect dialect, Variant variant) const; + +}; inline uint32_t getVertexId(uint32_t programId) { return (programId >> 16) & UINT16_MAX; } - + inline uint32_t getFragmentId(uint32_t programId) { return programId & UINT16_MAX; } -} - +} // namespace shader diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index 4c08b30dcd..4b4356d641 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -30,32 +30,37 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star _isAtRest = false; } -bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { - uint64_t now = usecTimestampNow(); - float dt = (float)(now - _lastUpdateTime) / (float)USECS_PER_SECOND; - _lastUpdateTime = now; - const float TAU = 1.0f; - float delta = glm::min(dt / TAU, 1.0f); +void AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { + _lastIsAtRest = _isAtRest; + if (_isValid) { + uint64_t now = usecTimestampNow(); + float dt = (float)(now - _lastUpdateTime) / (float)USECS_PER_SECOND; + _lastUpdateTime = now; + const float TAU = 1.0f; + float delta = glm::min(dt / TAU, 1.0f); - // keep a running average of position. - _positionAverage = position * delta + _positionAverage * (1 - delta); + // keep a running average of position. + _positionAverage = position * delta + _positionAverage * (1 - delta); - // keep a running average of position variances. - glm::vec3 dx = position - _positionAverage; - _positionVariance = glm::dot(dx, dx) * delta + _positionVariance * (1 - delta); + // keep a running average of position variances. + glm::vec3 dx = position - _positionAverage; + _positionVariance = glm::dot(dx, dx) * delta + _positionVariance * (1 - delta); - // keep a running average of quaternion logarithms. - glm::quat quatLogAsQuat = glm::log(rotation); - glm::vec3 quatLog(quatLogAsQuat.x, quatLogAsQuat.y, quatLogAsQuat.z); - _quatLogAverage = quatLog * delta + _quatLogAverage * (1 - delta); + // keep a running average of quaternion logarithms. + glm::quat quatLogAsQuat = glm::log(rotation); + glm::vec3 quatLog(quatLogAsQuat.x, quatLogAsQuat.y, quatLogAsQuat.z); + _quatLogAverage = quatLog * delta + _quatLogAverage * (1 - delta); - // keep a running average of quatLog variances. - glm::vec3 dql = quatLog - _quatLogAverage; - _quatLogVariance = glm::dot(dql, dql) * delta + _quatLogVariance * (1 - delta); + // keep a running average of quatLog variances. + glm::vec3 dql = quatLog - _quatLogAverage; + _quatLogVariance = glm::dot(dql, dql) * delta + _quatLogVariance * (1 - delta); - const float POSITION_VARIANCE_THRESHOLD = 0.001f; - const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; + const float POSITION_VARIANCE_THRESHOLD = 0.001f; + const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; - _isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; - return _isAtRest; + _isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; + } else { + reset(position, rotation); + _isValid = true; + } } diff --git a/libraries/shared/src/AtRestDetector.h b/libraries/shared/src/AtRestDetector.h index 525156de65..eb8e5f53f9 100644 --- a/libraries/shared/src/AtRestDetector.h +++ b/libraries/shared/src/AtRestDetector.h @@ -17,21 +17,27 @@ class AtRestDetector { public: + AtRestDetector() {}; AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation); void reset(const glm::vec3& startPosition, const glm::quat& startRotation); // returns true if object is at rest, dt in assumed to be seconds. - bool update(const glm::vec3& position, const glm::quat& startRotation); + void update(const glm::vec3& position, const glm::quat& startRotation); + void invalidate() { _isValid = false; } bool isAtRest() const { return _isAtRest; } + bool onRest() const { return !_lastIsAtRest && _isAtRest; } + bool onWake() const { return _lastIsAtRest && !_isAtRest; } protected: + bool _isValid { false }; glm::vec3 _positionAverage; glm::vec3 _quatLogAverage; uint64_t _lastUpdateTime { 0 }; float _positionVariance { 0.0f }; float _quatLogVariance { 0.0f }; bool _isAtRest { false }; + bool _lastIsAtRest { false }; }; #endif diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 6c38d08c96..87da47a27a 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -70,9 +70,10 @@ const float DEFAULT_AVATAR_MAX_WALKING_BACKWARD_SPEED = 2.2f; // meters / second const float DEFAULT_AVATAR_MAX_FLYING_SPEED = 30.0f; // meters / second const float DEFAULT_AVATAR_WALK_SPEED_THRESHOLD = 0.15f; -const float DEFAULT_AVATAR_GRAVITY = -5.0f; // meters / second^2 -const float DEFAULT_AVATAR_JUMP_SPEED = 3.5f; // meters / second -const float DEFAULT_AVATAR_JUMP_HEIGHT = (DEFAULT_AVATAR_JUMP_SPEED * DEFAULT_AVATAR_JUMP_SPEED) / (2.0f * DEFAULT_AVATAR_GRAVITY); // meters +const float DEFAULT_AVATAR_GRAVITY = -5.0f; // meters / second^2 (world) +const float DEFAULT_AVATAR_JUMP_SPEED = 3.5f; // meters / second (sensor) +const float DEFAULT_AVATAR_JUMP_HEIGHT = (DEFAULT_AVATAR_JUMP_SPEED * DEFAULT_AVATAR_JUMP_SPEED) / (2.0f * -DEFAULT_AVATAR_GRAVITY); // meters (sensor) +const float DEFAULT_AVATAR_MIN_JUMP_HEIGHT = 0.25f; // meters (world) // hack const float DEFAULT_AVATAR_FALL_HEIGHT = 20.0f; // meters const float DEFAULT_AVATAR_MIN_HOVER_HEIGHT = 2.5f; // meters diff --git a/libraries/shared/src/BufferParser.h b/libraries/shared/src/BufferParser.h index 74b47cb72f..c73356558e 100644 --- a/libraries/shared/src/BufferParser.h +++ b/libraries/shared/src/BufferParser.h @@ -94,13 +94,6 @@ inline void BufferParser::readValue(QUuid& result) { readUuid(result); } -template<> -inline void BufferParser::readValue(xColor& result) { - readValue(result.red); - readValue(result.blue); - readValue(result.green); -} - template<> inline void BufferParser::readValue(QVector& result) { uint16_t length; readValue(length); diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index e113449db3..dd9fd8dcd6 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -22,7 +22,7 @@ extern const float srgbToLinearLookupTable[256]; class ColorUtils { public: - inline static glm::vec3 toVec3(const xColor& color); + inline static glm::vec3 toVec3(const glm::u8vec3& color); // Convert to gamma 2.2 space from linear inline static glm::vec3 toGamma22Vec3(const glm::vec3& linear); @@ -40,9 +40,9 @@ public: inline static float tosRGBFloat(const float& linear); }; -inline glm::vec3 ColorUtils::toVec3(const xColor& color) { +inline glm::vec3 ColorUtils::toVec3(const glm::u8vec3& color) { const float ONE_OVER_255 = 1.0f / 255.0f; - return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); + return glm::vec3(color.x * ONE_OVER_255, color.y * ONE_OVER_255, color.z * ONE_OVER_255); } inline glm::vec3 ColorUtils::toGamma22Vec3(const glm::vec3& linear) { diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index d324e5af10..905bf3ccfd 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "GLMHelpers.h" #include #include "NumericalConstants.h" @@ -43,8 +44,8 @@ const mat4 Matrices::X_180 { createMatFromQuatAndPos(Quaternions::X_180, Vectors const mat4 Matrices::Y_180 { createMatFromQuatAndPos(Quaternions::Y_180, Vectors::ZERO) }; const mat4 Matrices::Z_180 { createMatFromQuatAndPos(Quaternions::Z_180, Vectors::ZERO) }; -// Safe version of glm::mix; based on the code in Nick Bobick's article, -// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, +// Safe version of glm::mix; based on the code in Nick Bobic's article, +// https://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php?page=1 (via Clyde, // https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) { float cosa = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; @@ -76,9 +77,11 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) { // Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix) { - int16_t twoByteFixed = (int16_t)(scalar * (float)(1 << radix)); - memcpy(buffer, &twoByteFixed, sizeof(int16_t)); - return sizeof(int16_t); + using FixedType = int16_t; + FixedType twoByteFixed = (FixedType) glm::clamp(scalar * (1 << radix), (float)std::numeric_limits::min(), + (float)std::numeric_limits::max()); + memcpy(buffer, &twoByteFixed, sizeof(FixedType)); + return sizeof(FixedType); } int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix) { @@ -448,17 +451,16 @@ glm::vec2 toGlm(const QPointF& pt) { return glm::vec2(pt.x(), pt.y()); } -glm::vec3 toGlm(const xColor& color) { +glm::vec3 toGlm(const glm::u8vec3& color) { static const float MAX_COLOR = 255.0f; - return glm::vec3(color.red, color.green, color.blue) / MAX_COLOR; + return glm::vec3(color) / MAX_COLOR; } -xColor xColorFromGlm(const glm::vec3 & color) { +vec4 toGlm(const glm::u8vec3& color, float alpha) { static const float MAX_COLOR = 255.0f; - return { (uint8_t)(color.x * MAX_COLOR), (uint8_t)(color.y * MAX_COLOR), (uint8_t)(color.z * MAX_COLOR) }; + return vec4(glm::vec3(color) / MAX_COLOR, alpha); } - glm::vec4 toGlm(const QColor& color) { return glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } @@ -475,10 +477,6 @@ QSize fromGlm(const glm::ivec2 & v) { return QSize(v.x, v.y); } -vec4 toGlm(const xColor& color, float alpha) { - return vec4((float)color.red / 255.0f, (float)color.green / 255.0f, (float)color.blue / 255.0f, alpha); -} - QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { QRectF result(pos.x, pos.y, size.x, size.y); return result; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 96219ea48c..e7aaace1ae 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -24,6 +24,7 @@ using glm::ivec2; using glm::ivec3; using glm::ivec4; using glm::uvec2; +using glm::u8vec3; using glm::uvec3; using glm::uvec4; using glm::mat3; @@ -174,12 +175,10 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f uvec2 toGlm(const QSize& size); ivec2 toGlm(const QPoint& pt); vec2 toGlm(const QPointF& pt); -vec3 toGlm(const xColor& color); +vec3 toGlm(const glm::u8vec3& color); vec4 toGlm(const QColor& color); ivec4 toGlm(const QRect& rect); -vec4 toGlm(const xColor& color, float alpha); - -xColor xColorFromGlm(const glm::vec3 & c); +vec4 toGlm(const glm::u8vec3& color, float alpha); QSize fromGlm(const glm::ivec2 & v); QMatrix4x4 fromGlm(const glm::mat4 & m); diff --git a/libraries/shared/src/NestableTransformNode.h b/libraries/shared/src/NestableTransformNode.h index 2f9bc2e985..be017a696d 100644 --- a/libraries/shared/src/NestableTransformNode.h +++ b/libraries/shared/src/NestableTransformNode.h @@ -56,4 +56,4 @@ public: NestableTransformNode(std::weak_ptr spatiallyNestable, int jointIndex) : BaseNestableTransformNode(spatiallyNestable, jointIndex) {}; }; -#endif // hifi_NestableTransformNode_h \ No newline at end of file +#endif // hifi_NestableTransformNode_h diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index e66121f159..60b426e46d 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -132,8 +132,6 @@ QUrl PathUtils::expandToLocalDataAbsolutePath(const QUrl& fileUrl) { return expandedURL; } - QUrl::fromLocalFile(resourcesPath()).toString(); - return fileUrl; } diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 4a74c5d212..d6a740231c 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -29,7 +29,8 @@ namespace PrioritySortUtil { class Sortable { public: - virtual ~Sortable() {} + virtual ~Sortable() = default; + virtual glm::vec3 getPosition() const = 0; virtual float getRadius() const = 0; virtual uint64_t getTimestamp() const = 0; @@ -67,8 +68,14 @@ namespace PrioritySortUtil { void reserve(size_t num) { _vector.reserve(num); } - const std::vector& getSortedVector() { - std::sort(_vector.begin(), _vector.end(), [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); }); + const std::vector& getSortedVector(int numToSort = 0) { + if (numToSort == 0 || numToSort >= (int)_vector.size()) { + std::sort(_vector.begin(), _vector.end(), + [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); }); + } else { + std::partial_sort(_vector.begin(), _vector.begin() + numToSort, _vector.end(), + [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); }); + } return _vector; } @@ -98,6 +105,9 @@ namespace PrioritySortUtil { float radius = glm::max(thing.getRadius(), MIN_RADIUS); // Other item's angle from view centre: float cosineAngle = glm::dot(offset, view.getDirection()) / distance; + if (cosineAngle > 0.0f) { + cosineAngle = std::sqrt(cosineAngle); + } float age = float((_usecCurrentTime - thing.getTimestamp()) / USECS_PER_SECOND); // the "age" term accumulates at the sum of all weights diff --git a/libraries/shared/src/QVariantGLM.cpp b/libraries/shared/src/QVariantGLM.cpp index f7fd7fbc8a..12c4628fbd 100644 --- a/libraries/shared/src/QVariantGLM.cpp +++ b/libraries/shared/src/QVariantGLM.cpp @@ -20,10 +20,6 @@ QVariantList quatToQList(const glm::quat& g) { return QVariantList() << g.x << g.y << g.z << g.w; } -QVariantList rgbColorToQList(const rgbColor& v) { - return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]); -} - QVariantMap vec3ToQMap(const glm::vec3& glmVector) { QVariantMap vectorAsVariantMap; vectorAsVariantMap["x"] = glmVector.x; @@ -56,14 +52,6 @@ glm::quat qListToQuat(const QVariant& q) { return glm::quat(w, x, y, z); } -void qListToRgbColor(const QVariant& q, rgbColor& returnValue) { - QVariantList qList = q.toList(); - returnValue[RED_INDEX] = qList[RED_INDEX].toInt(); - returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt(); - returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt(); -} - - glm::vec3 qMapToVec3(const QVariant& q) { QVariantMap qMap = q.toMap(); if (qMap.contains("x") && qMap.contains("y") && qMap.contains("z")) { diff --git a/libraries/shared/src/QVariantGLM.h b/libraries/shared/src/QVariantGLM.h index a8f8b531c0..d61f64312a 100644 --- a/libraries/shared/src/QVariantGLM.h +++ b/libraries/shared/src/QVariantGLM.h @@ -19,14 +19,12 @@ QVariantList vec3ToQList(const glm::vec3& g); QVariantList quatToQList(const glm::quat& g); -QVariantList rgbColorToQList(const rgbColor& v); QVariantMap vec3ToQMap(const glm::vec3& glmVector); QVariantMap quatToQMap(const glm::quat& glmQuat); glm::vec3 qListToVec3(const QVariant& q); glm::quat qListToQuat(const QVariant& q); -void qListToRgbColor(const QVariant& q, rgbColor& returnValue); glm::vec3 qMapToVec3(const QVariant& q); glm::quat qMapToQuat(const QVariant& q); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 0c8f4f0466..dc84afff93 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -25,14 +25,14 @@ #include #include -int vec4MetaTypeId = qRegisterMetaType(); +int vec2MetaTypeId = qRegisterMetaType(); +int u8vec3MetaTypeId = qRegisterMetaType(); int vec3MetaTypeId = qRegisterMetaType(); +int vec4MetaTypeId = qRegisterMetaType(); int qVectorVec3MetaTypeId = qRegisterMetaType>(); int qVectorQuatMetaTypeId = qRegisterMetaType>(); int qVectorBoolMetaTypeId = qRegisterMetaType>(); -int vec2MetaTypeId = qRegisterMetaType(); int quatMetaTypeId = qRegisterMetaType(); -int xColorMetaTypeId = qRegisterMetaType(); int pickRayMetaTypeId = qRegisterMetaType(); int collisionMetaTypeId = qRegisterMetaType(); int qMapURLStringMetaTypeId = qRegisterMetaType>(); @@ -41,27 +41,560 @@ int voidLambdaType = qRegisterMetaType>(); int variantLambdaType = qRegisterMetaType>(); void registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); + qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue); + qScriptRegisterMetaType(engine, vec3ToScriptValue, vec3FromScriptValue); + qScriptRegisterMetaType(engine, u8vec3ToScriptValue, u8vec3FromScriptValue); qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); - qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); + qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); + qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); + qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue); qScriptRegisterMetaType(engine, qVectorQuatToScriptValue, qVectorQuatFromScriptValue); qScriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue); qScriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue); qScriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue); - qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); - qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); + + qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); - qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); - qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue); + qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); + qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); - qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); } +QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) { + auto prototype = engine->globalObject().property("__hifi_vec2__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec2__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "u: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "v: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("x", vec2.x); + value.setProperty("y", vec2.y); + value.setPrototype(prototype); + return value; +} + +void vec2FromScriptValue(const QScriptValue& object, glm::vec2& vec2) { + if (object.isNumber()) { + vec2 = glm::vec2(object.toVariant().toFloat()); + } else if (object.isArray()) { + QVariantList list = object.toVariant().toList(); + if (list.length() == 2) { + vec2.x = list[0].toFloat(); + vec2.y = list[1].toFloat(); + } + } else { + QScriptValue x = object.property("x"); + if (!x.isValid()) { + x = object.property("u"); + } + + QScriptValue y = object.property("y"); + if (!y.isValid()) { + y = object.property("v"); + } + + vec2.x = x.toVariant().toFloat(); + vec2.y = y.toVariant().toFloat(); + } +} + +QVariant vec2ToVariant(const glm::vec2 &vec2) { + if (vec2.x != vec2.x || vec2.y != vec2.y) { + // if vec2 contains a NaN don't try to convert it + return QVariant(); + } + QVariantMap result; + result["x"] = vec2.x; + result["y"] = vec2.y; + return result; +} + +glm::vec2 vec2FromVariant(const QVariant &object, bool& isValid) { + isValid = false; + glm::vec2 result; + if (object.canConvert()) { + result = glm::vec2(object.toFloat()); + isValid = true; + } else if (object.canConvert()) { + auto qvec2 = qvariant_cast(object); + result.x = qvec2.x(); + result.y = qvec2.y(); + isValid = true; + } else { + auto map = object.toMap(); + auto x = map["x"]; + if (!x.isValid()) { + x = map["u"]; + } + + auto y = map["y"]; + if (!y.isValid()) { + y = map["v"]; + } + + if (x.isValid() && y.isValid()) { + result.x = x.toFloat(&isValid); + if (isValid) { + result.y = y.toFloat(&isValid); + } + } + } + return result; +} + +glm::vec2 vec2FromVariant(const QVariant &object) { + bool valid; + return vec2FromVariant(object, valid); +} + +QScriptValue vec3ToScriptValue(QScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_vec3__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("x", vec3.x); + value.setProperty("y", vec3.y); + value.setProperty("z", vec3.z); + value.setPrototype(prototype); + return value; +} + +QScriptValue vec3ColorToScriptValue(QScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_vec3_color__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec3_color__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("red", vec3.x); + value.setProperty("green", vec3.y); + value.setProperty("blue", vec3.z); + value.setPrototype(prototype); + return value; +} + +void vec3FromScriptValue(const QScriptValue& object, glm::vec3& vec3) { + if (object.isNumber()) { + vec3 = glm::vec3(object.toVariant().toFloat()); + } else if (object.isString()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + vec3.x = qColor.red(); + vec3.y = qColor.green(); + vec3.z = qColor.blue(); + } + } else if (object.isArray()) { + QVariantList list = object.toVariant().toList(); + if (list.length() == 3) { + vec3.x = list[0].toFloat(); + vec3.y = list[1].toFloat(); + vec3.z = list[2].toFloat(); + } + } else { + QScriptValue x = object.property("x"); + if (!x.isValid()) { + x = object.property("r"); + } + if (!x.isValid()) { + x = object.property("red"); + } + + QScriptValue y = object.property("y"); + if (!y.isValid()) { + y = object.property("g"); + } + if (!y.isValid()) { + y = object.property("green"); + } + + QScriptValue z = object.property("z"); + if (!z.isValid()) { + z = object.property("b"); + } + if (!z.isValid()) { + z = object.property("blue"); + } + + vec3.x = x.toVariant().toFloat(); + vec3.y = y.toVariant().toFloat(); + vec3.z = z.toVariant().toFloat(); + } +} + +QScriptValue u8vec3ToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_u8vec3__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_u8vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("x", vec3.x); + value.setProperty("y", vec3.y); + value.setProperty("z", vec3.z); + value.setPrototype(prototype); + return value; +} + +QScriptValue u8vec3ColorToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_u8vec3_color__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_u8vec3_color__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("red", vec3.x); + value.setProperty("green", vec3.y); + value.setProperty("blue", vec3.z); + value.setPrototype(prototype); + return value; +} + +void u8vec3FromScriptValue(const QScriptValue& object, glm::u8vec3& vec3) { + if (object.isNumber()) { + vec3 = glm::vec3(object.toVariant().toUInt()); + } else if (object.isString()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + vec3.x = (uint8_t)qColor.red(); + vec3.y = (uint8_t)qColor.green(); + vec3.z = (uint8_t)qColor.blue(); + } + } else if (object.isArray()) { + QVariantList list = object.toVariant().toList(); + if (list.length() == 3) { + vec3.x = list[0].toUInt(); + vec3.y = list[1].toUInt(); + vec3.z = list[2].toUInt(); + } + } else { + QScriptValue x = object.property("x"); + if (!x.isValid()) { + x = object.property("r"); + } + if (!x.isValid()) { + x = object.property("red"); + } + + QScriptValue y = object.property("y"); + if (!y.isValid()) { + y = object.property("g"); + } + if (!y.isValid()) { + y = object.property("green"); + } + + QScriptValue z = object.property("z"); + if (!z.isValid()) { + z = object.property("b"); + } + if (!z.isValid()) { + z = object.property("blue"); + } + + vec3.x = x.toVariant().toUInt(); + vec3.y = y.toVariant().toUInt(); + vec3.z = z.toVariant().toUInt(); + } +} + +QVariant vec3toVariant(const glm::vec3& vec3) { + if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { + // if vec3 contains a NaN don't try to convert it + return QVariant(); + } + QVariantMap result; + result["x"] = vec3.x; + result["y"] = vec3.y; + result["z"] = vec3.z; + return result; +} + +glm::vec3 vec3FromVariant(const QVariant& object, bool& valid) { + glm::vec3 v; + valid = false; + if (!object.isValid() || object.isNull()) { + return v; + } else if (object.canConvert()) { + v = glm::vec3(object.toFloat()); + valid = true; + } else if (object.canConvert()) { + auto qvec3 = qvariant_cast(object); + v.x = qvec3.x(); + v.y = qvec3.y(); + v.z = qvec3.z(); + valid = true; + } else if (object.canConvert()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else if (object.canConvert()) { + QColor qColor = qvariant_cast(object); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else { + auto map = object.toMap(); + auto x = map["x"]; + if (!x.isValid()) { + x = map["r"]; + } + if (!x.isValid()) { + x = map["red"]; + } + + auto y = map["y"]; + if (!y.isValid()) { + y = map["g"]; + } + if (!y.isValid()) { + y = map["green"]; + } + + auto z = map["z"]; + if (!z.isValid()) { + z = map["b"]; + } + if (!z.isValid()) { + z = map["blue"]; + } + + if (x.canConvert() && y.canConvert() && z.canConvert()) { + v.x = x.toFloat(); + v.y = y.toFloat(); + v.z = z.toFloat(); + valid = true; + } + } + return v; +} + +glm::vec3 vec3FromVariant(const QVariant& object) { + bool valid = false; + return vec3FromVariant(object, valid); +} + +QVariant u8vec3toVariant(const glm::u8vec3& vec3) { + QVariantMap result; + result["x"] = vec3.x; + result["y"] = vec3.y; + result["z"] = vec3.z; + return result; +} + +QVariant u8vec3ColortoVariant(const glm::u8vec3& vec3) { + QVariantMap result; + result["red"] = vec3.x; + result["green"] = vec3.y; + result["blue"] = vec3.z; + return result; +} + +glm::u8vec3 u8vec3FromVariant(const QVariant& object, bool& valid) { + glm::u8vec3 v; + valid = false; + if (!object.isValid() || object.isNull()) { + return v; + } else if (object.canConvert()) { + v = glm::vec3(object.toUInt()); + valid = true; + } else if (object.canConvert()) { + auto qvec3 = qvariant_cast(object); + v.x = (uint8_t)qvec3.x(); + v.y = (uint8_t)qvec3.y(); + v.z = (uint8_t)qvec3.z(); + valid = true; + } else if (object.canConvert()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else if (object.canConvert()) { + QColor qColor = qvariant_cast(object); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else { + auto map = object.toMap(); + auto x = map["x"]; + if (!x.isValid()) { + x = map["r"]; + } + if (!x.isValid()) { + x = map["red"]; + } + + auto y = map["y"]; + if (!y.isValid()) { + y = map["g"]; + } + if (!y.isValid()) { + y = map["green"]; + } + + auto z = map["z"]; + if (!z.isValid()) { + z = map["b"]; + } + if (!z.isValid()) { + z = map["blue"]; + } + + if (x.canConvert() && y.canConvert() && z.canConvert()) { + v.x = x.toUInt(); + v.y = y.toUInt(); + v.z = z.toUInt(); + valid = true; + } + } + return v; +} + +glm::u8vec3 u8vec3FromVariant(const QVariant& object) { + bool valid = false; + return u8vec3FromVariant(object, valid); +} + +QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", vec4.x); + obj.setProperty("y", vec4.y); + obj.setProperty("z", vec4.z); + obj.setProperty("w", vec4.w); + return obj; +} + +void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { + vec4.x = object.property("x").toVariant().toFloat(); + vec4.y = object.property("y").toVariant().toFloat(); + vec4.z = object.property("z").toVariant().toFloat(); + vec4.w = object.property("w").toVariant().toFloat(); +} + +QVariant vec4toVariant(const glm::vec4& vec4) { + if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) { + // if vec4 contains a NaN don't try to convert it + return QVariant(); + } + QVariantMap result; + result["x"] = vec4.x; + result["y"] = vec4.y; + result["z"] = vec4.z; + result["w"] = vec4.w; + return result; +} + +glm::vec4 vec4FromVariant(const QVariant& object, bool& valid) { + glm::vec4 v; + valid = false; + if (!object.isValid() || object.isNull()) { + return v; + } else if (object.canConvert()) { + v = glm::vec4(object.toFloat()); + valid = true; + } else if (object.canConvert()) { + auto qvec4 = qvariant_cast(object); + v.x = qvec4.x(); + v.y = qvec4.y(); + v.z = qvec4.z(); + v.w = qvec4.w(); + valid = true; + } else { + auto map = object.toMap(); + auto x = map["x"]; + auto y = map["y"]; + auto z = map["z"]; + auto w = map["w"]; + if (x.canConvert() && y.canConvert() && z.canConvert() && w.canConvert()) { + v.x = x.toFloat(); + v.y = y.toFloat(); + v.z = z.toFloat(); + v.w = w.toFloat(); + valid = true; + } + } + return v; +} + +glm::vec4 vec4FromVariant(const QVariant& object) { + bool valid = false; + return vec4FromVariant(object, valid); +} + QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4) { QScriptValue obj = engine->newObject(); obj.setProperty("r0c0", mat4[0][0]); @@ -102,168 +635,42 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) { mat4[3][3] = object.property("r3c3").toVariant().toFloat(); } -QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", vec4.x); - obj.setProperty("y", vec4.y); - obj.setProperty("z", vec4.z); - obj.setProperty("w", vec4.w); - return obj; -} - -void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { - vec4.x = object.property("x").toVariant().toFloat(); - vec4.y = object.property("y").toVariant().toFloat(); - vec4.z = object.property("z").toVariant().toFloat(); - vec4.w = object.property("w").toVariant().toFloat(); -} - -QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) { - QScriptValue obj = engine->newObject(); - if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { - // if vec3 contains a NaN don't try to convert it - return obj; +QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector) { + QScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, vec3ColorToScriptValue(engine, vector.at(i))); } - obj.setProperty("x", vec3.x); - obj.setProperty("y", vec3.y); - obj.setProperty("z", vec3.z); - obj.setProperty("red", vec3.x); - obj.setProperty("green", vec3.y); - obj.setProperty("blue", vec3.z); - return obj; -} - -void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { - auto x = object.property("x").toVariant(); - if (!x.isValid()) { - x = object.property("red").toVariant(); - } - auto y = object.property("y").toVariant(); - if (!y.isValid()) { - y = object.property("green").toVariant(); - } - auto z = object.property("z").toVariant(); - if (!z.isValid()) { - z = object.property("blue").toVariant(); - } - vec3.x = x.toFloat(); - vec3.y = y.toFloat(); - vec3.z = z.toFloat(); -} - -QVariant vec3toVariant(const glm::vec3& vec3) { - if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { - // if vec3 contains a NaN don't try to convert it - return QVariant(); - } - QVariantMap result; - result["x"] = vec3.x; - result["y"] = vec3.y; - result["z"] = vec3.z; - return result; -} - -QVariant vec4toVariant(const glm::vec4& vec4) { - if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) { - // if vec4 contains a NaN don't try to convert it - return QVariant(); - } - QVariantMap result; - result["x"] = vec4.x; - result["y"] = vec4.y; - result["z"] = vec4.z; - result["w"] = vec4.w; - return result; + return array; } QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector) { QScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, vec3toScriptValue(engine, vector.at(i))); + array.setProperty(i, vec3ToScriptValue(engine, vector.at(i))); } return array; } +QVector qVectorVec3FromScriptValue(const QScriptValue& array) { + QVector newVector; + int length = array.property("length").toInteger(); -glm::vec3 vec3FromVariant(const QVariant& object, bool& valid) { - glm::vec3 v; - valid = false; - if (!object.isValid() || object.isNull()) { - return v; - } else if (object.canConvert()) { - v = glm::vec3(object.toFloat()); - valid = true; - } else if (object.canConvert()) { - auto qvec3 = qvariant_cast(object); - v.x = qvec3.x(); - v.y = qvec3.y(); - v.z = qvec3.z(); - valid = true; - } else { - auto map = object.toMap(); - auto x = map["x"]; - auto y = map["y"]; - auto z = map["z"]; - if (!x.isValid()) { - x = map["width"]; - } - if (!y.isValid()) { - y = map["height"]; - } - if (!y.isValid()) { - z = map["depth"]; - } - - if (x.canConvert() && y.canConvert() && z.canConvert()) { - v.x = x.toFloat(); - v.y = y.toFloat(); - v.z = z.toFloat(); - valid = true; - } + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array.property(i), newVec3); + newVector << newVec3; } - return v; + return newVector; } -glm::vec3 vec3FromVariant(const QVariant& object) { - bool valid = false; - return vec3FromVariant(object, valid); -} +void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector) { + int length = array.property("length").toInteger(); -glm::vec4 vec4FromVariant(const QVariant& object, bool& valid) { - glm::vec4 v; - valid = false; - if (!object.isValid() || object.isNull()) { - return v; - } else if (object.canConvert()) { - v = glm::vec4(object.toFloat()); - valid = true; - } else if (object.canConvert()) { - auto qvec4 = qvariant_cast(object); - v.x = qvec4.x(); - v.y = qvec4.y(); - v.z = qvec4.z(); - v.w = qvec4.w(); - valid = true; - } else { - auto map = object.toMap(); - auto x = map["x"]; - auto y = map["y"]; - auto z = map["z"]; - auto w = map["w"]; - if (x.canConvert() && y.canConvert() && z.canConvert() && w.canConvert()) { - v.x = x.toFloat(); - v.y = y.toFloat(); - v.z = z.toFloat(); - v.w = w.toFloat(); - valid = true; - } + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array.property(i), newVec3); + vector << newVec3; } - return v; -} - -glm::vec4 vec4FromVariant(const QVariant& object) { - bool valid = false; - return vec4FromVariant(object, valid); } QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) { @@ -297,11 +704,11 @@ void quatFromScriptValue(const QScriptValue& object, glm::quat &quat) { glm::quat quatFromVariant(const QVariant &object, bool& isValid) { glm::quat q; if (object.canConvert()) { - auto qvec3 = qvariant_cast(object); - q.x = qvec3.x(); - q.y = qvec3.y(); - q.z = qvec3.z(); - q.w = qvec3.scalar(); + auto qquat = qvariant_cast(object); + q.x = qquat.x(); + q.y = qquat.y(); + q.z = qquat.z(); + q.w = qquat.scalar(); // enforce normalized quaternion float length = glm::length(q); @@ -340,7 +747,7 @@ glm::quat quatFromVariant(const QVariant& object) { QVariant quatToVariant(const glm::quat& quat) { if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z) { - // if vec3 contains a NaN don't try to convert it + // if quat contains a NaN don't try to convert it return QVariant(); } QVariantMap result; @@ -432,29 +839,6 @@ void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vec } } -// -QVector qVectorVec3FromScriptValue(const QScriptValue& array){ - QVector newVector; - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::vec3 newVec3 = glm::vec3(); - vec3FromScriptValue(array.property(i), newVec3); - newVector << newVec3; - } - return newVector; -} - -void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector ) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::vec3 newVec3 = glm::vec3(); - vec3FromScriptValue(array.property(i), newVec3); - vector << newVec3; - } -} - QVector qVectorQuatFromScriptValue(const QScriptValue& array){ QVector newVector; int length = array.property("length").toInteger(); @@ -495,71 +879,6 @@ void qVectorBoolFromScriptValue(const QScriptValue& array, QVector& vector } } -QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", vec2.x); - obj.setProperty("y", vec2.y); - return obj; -} - -void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) { - vec2.x = object.property("x").toVariant().toFloat(); - vec2.y = object.property("y").toVariant().toFloat(); -} - -QVariant vec2toVariant(const glm::vec2 &vec2) { - if (vec2.x != vec2.x || vec2.y != vec2.y) { - // if vec2 contains a NaN don't try to convert it - return QVariant(); - } - QVariantMap result; - result["x"] = vec2.x; - result["y"] = vec2.y; - return result; -} - -glm::vec2 vec2FromVariant(const QVariant &object, bool& isValid) { - isValid = false; - glm::vec2 result; - if (object.canConvert()) { - result = glm::vec2(object.toFloat()); - } else if (object.canConvert()) { - auto qvec2 = qvariant_cast(object); - result.x = qvec2.x(); - result.y = qvec2.y(); - } else { - auto map = object.toMap(); - auto x = map["x"]; - if (!x.isValid()) { - x = map["width"]; - } - auto y = map["y"]; - if (!y.isValid()) { - y = map["height"]; - } - if (x.isValid() && y.isValid()) { - result.x = x.toFloat(&isValid); - if (isValid) { - result.y = y.toFloat(&isValid); - } - } - } - return result; -} - -glm::vec2 vec2FromVariant(const QVariant &object) { - bool valid; - return vec2FromVariant(object, valid); -} - -/**jsdoc - * Defines a rectangular portion of an image or screen, or similar. - * @typedef {object} Rect - * @property {number} x - Left, x-coordinate value. - * @property {number} y - Top, y-coordinate value. - * @property {number} width - Width of the rectangle. - * @property {number} height - Height of the rectangle. - */ QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) { QScriptValue obj = engine->newObject(); obj.setProperty("x", rect.x()); @@ -654,94 +973,6 @@ QRectF qRectFFromVariant(const QVariant& object) { return qRectFFromVariant(object, valid); } - -QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { - QScriptValue obj = engine->newObject(); - obj.setProperty("red", color.red); - obj.setProperty("green", color.green); - obj.setProperty("blue", color.blue); - return obj; -} - -void xColorFromScriptValue(const QScriptValue &object, xColor& color) { - if (!object.isValid()) { - return; - } - if (object.isNumber()) { - color.red = color.green = color.blue = (uint8_t)object.toUInt32(); - } else if (object.isString()) { - QColor qcolor(object.toString()); - if (qcolor.isValid()) { - color.red = (uint8_t)qcolor.red(); - color.blue = (uint8_t)qcolor.blue(); - color.green = (uint8_t)qcolor.green(); - } - } else { - color.red = object.property("red").toVariant().toInt(); - color.green = object.property("green").toVariant().toInt(); - color.blue = object.property("blue").toVariant().toInt(); - } -} - -/**jsdoc - * An RGB color value. - * @typedef {object} Color - * @property {number} red - Red component value. Integer in the range 0 - 255. - * @property {number} green - Green component value. Integer in the range 0 - 255. - * @property {number} blue - Blue component value. Integer in the range 0 - 255. - */ -QVariant xColorToVariant(const xColor& color) { - QVariantMap obj; - obj["red"] = color.red; - obj["green"] = color.green; - obj["blue"] = color.blue; - return obj; -} - -xColor xColorFromVariant(const QVariant &object, bool& isValid) { - isValid = false; - xColor color { 0, 0, 0 }; - if (!object.isValid()) { - return color; - } - if (object.canConvert()) { - isValid = true; - color.red = color.green = color.blue = (uint8_t)object.toInt(); - } else if (object.canConvert()) { - QColor qcolor(object.toString()); - if (qcolor.isValid()) { - isValid = true; - color.red = (uint8_t)qcolor.red(); - color.blue = (uint8_t)qcolor.blue(); - color.green = (uint8_t)qcolor.green(); - } - } else if (object.canConvert()) { - QColor qcolor = qvariant_cast(object); - if (qcolor.isValid()) { - isValid = true; - color.red = (uint8_t)qcolor.red(); - color.blue = (uint8_t)qcolor.blue(); - color.green = (uint8_t)qcolor.green(); - } - } else { - QVariantMap map = object.toMap(); - color.red = map["red"].toInt(&isValid); - if (isValid) { - color.green = map["green"].toInt(&isValid); - } - if (isValid) { - color.blue = map["blue"].toInt(&isValid); - } - } - return color; -} - -xColor xColorFromVariant(const QVariant &object) { - bool valid; - return xColorFromVariant(object, valid); -} - - QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { QScriptValue object = engine->newObject(); object.setProperty("red", color.red()); @@ -804,9 +1035,9 @@ void qURLFromScriptValue(const QScriptValue& object, QUrl& url) { QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) { QScriptValue obj = engine->newObject(); - QScriptValue origin = vec3toScriptValue(engine, pickRay.origin); + QScriptValue origin = vec3ToScriptValue(engine, pickRay.origin); obj.setProperty("origin", origin); - QScriptValue direction = vec3toScriptValue(engine, pickRay.direction); + QScriptValue direction = vec3ToScriptValue(engine, pickRay.direction); obj.setProperty("direction", direction); return obj; } @@ -850,9 +1081,9 @@ QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& coll obj.setProperty("type", collision.type); obj.setProperty("idA", quuidToScriptValue(engine, collision.idA)); obj.setProperty("idB", quuidToScriptValue(engine, collision.idB)); - obj.setProperty("penetration", vec3toScriptValue(engine, collision.penetration)); - obj.setProperty("contactPoint", vec3toScriptValue(engine, collision.contactPoint)); - obj.setProperty("velocityChange", vec3toScriptValue(engine, collision.velocityChange)); + obj.setProperty("penetration", vec3ToScriptValue(engine, collision.penetration)); + obj.setProperty("contactPoint", vec3ToScriptValue(engine, collision.contactPoint)); + obj.setProperty("velocityChange", vec3ToScriptValue(engine, collision.velocityChange)); return obj; } diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 9379269d13..ed637fe771 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -29,13 +29,12 @@ class QColor; class QUrl; -Q_DECLARE_METATYPE(glm::vec4) -Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) +Q_DECLARE_METATYPE(glm::u8vec3) +Q_DECLARE_METATYPE(glm::vec3) +Q_DECLARE_METATYPE(glm::vec4) Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(glm::mat4) -Q_DECLARE_METATYPE(xColor) -Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(AACube) Q_DECLARE_METATYPE(std::function); @@ -47,6 +46,101 @@ void registerMetaTypes(QScriptEngine* engine); QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4); void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); +/**jsdoc +* A 2-dimensional vector. +* +* @typedef {object} Vec2 +* @property {number} x - X-coordinate of the vector. Synonyms: u. +* @property {number} y - Y-coordinate of the vector. Synonyms: v. +* @example Vec2s can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { materialMappingPos: { x: 0.1, y: 0.2 }}); // { x: 0.1, y: 0.2 } +* Entities.editEntity(, { materialMappingPos: { u: 0.3, v: 0.4 }}); // { x: 0.3, y: 0.4 } +* Entities.editEntity(, { materialMappingPos: [0.5, 0.6] }); // { x: 0.5, y: 0.6 } +* Entities.editEntity(, { materialMappingPos: 0.7 }); // { x: 0.7, y: 0.7 } +* var color = Entities.getEntityProperties().materialMappingPos; // { x: 0.7, y: 0.7 } +* color.v = 0.8; // { x: 0.7, y: 0.8 } +*/ +QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2); +void vec2FromScriptValue(const QScriptValue& object, glm::vec2& vec2); + +QVariant vec2ToVariant(const glm::vec2& vec2); +glm::vec2 vec2FromVariant(const QVariant& object, bool& valid); +glm::vec2 vec2FromVariant(const QVariant& object); + +/**jsdoc +* A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} Vec3 +* @property {number} x - X-coordinate of the vector. Synonyms: r, red. +* @property {number} y - Y-coordinate of the vector. Synonyms: g, green. +* @property {number} z - Z-coordinate of the vector. Synonyms: b, blue. +* @example Vec3s can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { position: { x: 1, y: 2, z: 3 }}); // { x: 1, y: 2, z: 3 } +* Entities.editEntity(, { position: { r: 4, g: 5, b: 6 }}); // { x: 4, y: 5, z: 6 } +* Entities.editEntity(, { position: { red: 7, green: 8, blue: 9 }}); // { x: 7, y: 8, z: 9 } +* Entities.editEntity(, { position: [10, 11, 12] }); // { x: 10, y: 11, z: 12 } +* Entities.editEntity(, { position: 13 }); // { x: 13, y: 13, z: 13 } +* var position = Entities.getEntityProperties().position; // { x: 13, y: 13, z: 13 } +* position.g = 14; // { x: 13, y: 14, z: 13 } +* position.blue = 15; // { x: 13, y: 14, z: 15 } +* Entities.editEntity(, { position: "red"}); // { x: 255, y: 0, z: 0 } +* Entities.editEntity(, { position: "#00FF00"}); // { x: 0, y: 255, z: 0 } +*/ +QScriptValue vec3ToScriptValue(QScriptEngine* engine, const glm::vec3& vec3); +QScriptValue vec3ColorToScriptValue(QScriptEngine* engine, const glm::vec3& vec3); +void vec3FromScriptValue(const QScriptValue& object, glm::vec3& vec3); + +QVariant vec3toVariant(const glm::vec3& vec3); +glm::vec3 vec3FromVariant(const QVariant &object, bool& valid); +glm::vec3 vec3FromVariant(const QVariant &object); + +/**jsdoc +* A color vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} Color +* @property {number} red - Red component value. Integer in the range 0 - 255. Synonyms: r, x. +* @property {number} green - Green component value. Integer in the range 0 - 255. Synonyms: g, y. +* @property {number} blue - Blue component value. Integer in the range 0 - 255. Synonyms: b, z. +* @example Colors can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { color: { x: 1, y: 2, z: 3 }}); // { red: 1, green: 2, blue: 3 } +* Entities.editEntity(, { color: { r: 4, g: 5, b: 6 }}); // { red: 4, green: 5, blue: 6 } +* Entities.editEntity(, { color: { red: 7, green: 8, blue: 9 }}); // { red: 7, green: 8, blue: 9 } +* Entities.editEntity(, { color: [10, 11, 12] }); // { red: 10, green: 11, blue: 12 } +* Entities.editEntity(, { color: 13 }); // { red: 13, green: 13, blue: 13 } +* var color = Entities.getEntityProperties().color; // { red: 13, green: 13, blue: 13 } +* color.g = 14; // { red: 13, green: 14, blue: 13 } +* color.blue = 15; // { red: 13, green: 14, blue: 15 } +* Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } +* Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } +*/ +/**jsdoc +* A color vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} ColorFloat +* @property {number} red - Red component value. Real in the range 0 - 255. Synonyms: r, x. +* @property {number} green - Green component value. Real in the range 0 - 255. Synonyms: g, y. +* @property {number} blue - Blue component value. Real in the range 0 - 255. Synonyms: b, z. +* @example ColorFloats can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { color: { x: 1, y: 2, z: 3 }}); // { red: 1, green: 2, blue: 3 } +* Entities.editEntity(, { color: { r: 4, g: 5, b: 6 }}); // { red: 4, green: 5, blue: 6 } +* Entities.editEntity(, { color: { red: 7, green: 8, blue: 9 }}); // { red: 7, green: 8, blue: 9 } +* Entities.editEntity(, { color: [10, 11, 12] }); // { red: 10, green: 11, blue: 12 } +* Entities.editEntity(, { color: 13 }); // { red: 13, green: 13, blue: 13 } +* var color = Entities.getEntityProperties().color; // { red: 13, green: 13, blue: 13 } +* color.g = 14; // { red: 13, green: 14, blue: 13 } +* color.blue = 15; // { red: 13, green: 14, blue: 15 } +* Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } +* Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } +*/ +QScriptValue u8vec3ToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3); +QScriptValue u8vec3ColorToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3); +void u8vec3FromScriptValue(const QScriptValue& object, glm::u8vec3& vec3); + +QVariant u8vec3toVariant(const glm::u8vec3& vec3); +QVariant u8vec3ColortoVariant(const glm::u8vec3& vec3); +glm::u8vec3 u8vec3FromVariant(const QVariant &object, bool& valid); +glm::u8vec3 u8vec3FromVariant(const QVariant &object); + /**jsdoc * A 4-dimensional vector. * @@ -56,36 +150,12 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); * @property {number} z - Z-coordinate of the vector. * @property {number} w - W-coordinate of the vector. */ -// Vec4 QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4); void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4); QVariant vec4toVariant(const glm::vec4& vec4); glm::vec4 vec4FromVariant(const QVariant &object, bool& valid); glm::vec4 vec4FromVariant(const QVariant &object); -// Vec3 -QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); -void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); - -QVariant vec3toVariant(const glm::vec3& vec3); -glm::vec3 vec3FromVariant(const QVariant &object, bool& valid); -glm::vec3 vec3FromVariant(const QVariant &object); - -/**jsdoc - * A 2-dimensional vector. - * - * @typedef {object} Vec2 - * @property {number} x - X-coordinate of the vector. - * @property {number} y - Y-coordinate of the vector. - */ -// Vec2 -QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2); -void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2); - -QVariant vec2toVariant(const glm::vec2 &vec2); -glm::vec2 vec2FromVariant(const QVariant &object, bool& valid); -glm::vec2 vec2FromVariant(const QVariant &object); - // Quaternions QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat); void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); @@ -94,25 +164,26 @@ QVariant quatToVariant(const glm::quat& quat); glm::quat quatFromVariant(const QVariant &object, bool& isValid); glm::quat quatFromVariant(const QVariant &object); -// Rect +/**jsdoc + * Defines a rectangular portion of an image or screen, or similar. + * @typedef {object} Rect + * @property {number} x - Left, x-coordinate value. + * @property {number} y - Top, y-coordinate value. + * @property {number} width - Width of the rectangle. + * @property {number} height - Height of the rectangle. + */ QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect); void qRectFromScriptValue(const QScriptValue& object, QRect& rect); QRect qRectFromVariant(const QVariant& object, bool& isValid); QRect qRectFromVariant(const QVariant& object); QVariant qRectToVariant(const QRect& rect); + QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect); void qRectFFromScriptValue(const QScriptValue& object, QRectF& rect); QRectF qRectFFromVariant(const QVariant& object, bool& isValid); QRectF qRectFFromVariant(const QVariant& object); QVariant qRectFToVariant(const QRectF& rect); -// xColor -QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); -void xColorFromScriptValue(const QScriptValue &object, xColor& color); - -QVariant xColorToVariant(const xColor& color); -xColor xColorFromVariant(const QVariant &object, bool& isValid); - // QColor QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color); void qColorFromScriptValue(const QScriptValue& object, QColor& color); @@ -121,11 +192,14 @@ QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url); void qURLFromScriptValue(const QScriptValue& object, QUrl& url); // vector +Q_DECLARE_METATYPE(QVector) QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector); +QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorVec3FromScriptValue(const QScriptValue& array); // vector +Q_DECLARE_METATYPE(QVector) QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorQuatFromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorQuatFromScriptValue(const QScriptValue& array); @@ -195,6 +269,7 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); * * @typedef {object} StylusTip * @property {number} side - The hand the tip is attached to: 0 for left, 1 for right. + * @property {Vec3} tipOffset - the position offset of the stylus tip. * @property {Vec3} position - The position of the stylus tip. * @property {Quat} orientation - The orientation of the stylus tip. * @property {Vec3} velocity - The velocity of the stylus tip. @@ -202,12 +277,14 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); class StylusTip : public MathPick { public: StylusTip() : position(NAN), velocity(NAN) {} - StylusTip(const bilateral::Side& side, const glm::vec3& position = Vectors::ZERO, const glm::quat& orientation = Quaternions::IDENTITY, const glm::vec3& velocity = Vectors::ZERO) : - side(side), position(position), orientation(orientation), velocity(velocity) {} - StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])), - orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} + StylusTip(const bilateral::Side& side, const glm::vec3& tipOffset = Vectors::ZERO ,const glm::vec3& position = Vectors::ZERO, + const glm::quat& orientation = Quaternions::IDENTITY, const glm::vec3& velocity = Vectors::ZERO) : + side(side), tipOffset(tipOffset), position(position), orientation(orientation), velocity(velocity) {} + StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), tipOffset(vec3FromVariant(pickVariant["tipOffset"])), + position(vec3FromVariant(pickVariant["position"])), orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} bilateral::Side side { bilateral::Side::Invalid }; + glm::vec3 tipOffset; glm::vec3 position; glm::quat orientation; glm::vec3 velocity; @@ -215,12 +292,13 @@ public: operator bool() const override { return side != bilateral::Side::Invalid; } bool operator==(const StylusTip& other) const { - return (side == other.side && position == other.position && orientation == other.orientation && velocity == other.velocity); + return (side == other.side && tipOffset == other.tipOffset && position == other.position && orientation == other.orientation && velocity == other.velocity); } QVariantMap toVariantMap() const override { QVariantMap stylusTip; stylusTip["side"] = (int)side; + stylusTip["tipOffset"] = vec3toVariant(tipOffset); stylusTip["position"] = vec3toVariant(position); stylusTip["orientation"] = quatToVariant(orientation); stylusTip["velocity"] = vec3toVariant(velocity); diff --git a/libraries/shared/src/ResourceRequestObserver.cpp b/libraries/shared/src/ResourceRequestObserver.cpp new file mode 100644 index 0000000000..5e0925520a --- /dev/null +++ b/libraries/shared/src/ResourceRequestObserver.cpp @@ -0,0 +1,28 @@ +// +// ResourceAccessMonitor.h +// libraries/networking/src +// +// Created by Kerry Ivan Kurian on 9/27/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include +#include +#include +#include +#include "ResourceRequestObserver.h" + +void ResourceRequestObserver::update(const QUrl& requestUrl, + const qint64 callerId, + const QString& extra) { + QJsonArray array; + QJsonObject data { { "url", requestUrl.toString() }, + { "callerId", callerId }, + { "extra", extra } + }; + emit resourceRequestEvent(data.toVariantMap()); +} diff --git a/libraries/shared/src/ResourceRequestObserver.h b/libraries/shared/src/ResourceRequestObserver.h new file mode 100644 index 0000000000..1b1bc322e5 --- /dev/null +++ b/libraries/shared/src/ResourceRequestObserver.h @@ -0,0 +1,29 @@ +// +// ResourceRequestObserver.h +// libraries/commerce/src +// +// Created by Kerry Ivan Kurian on 9/27/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include +#include +#include + +#include "DependencyManager.h" + + +class ResourceRequestObserver : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + void update(const QUrl& requestUrl, const qint64 callerId = -1, const QString& extra = ""); + +signals: + void resourceRequestEvent(QVariantMap result); +}; diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 152e305bf2..df8e61114d 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -148,12 +148,12 @@ void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollec _hashKey.clear(); } -void ShapeInfo::setCapsuleY(float radius, float halfHeight) { +void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) { _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); - halfHeight = glm::max(halfHeight, 0.0f); - _halfExtents = glm::vec3(radius, halfHeight, radius); + cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f); + _halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius); _hashKey.clear(); } @@ -261,27 +261,27 @@ bool ShapeInfo::contains(const glm::vec3& point) const { case SHAPE_TYPE_CYLINDER_Z: return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y; case SHAPE_TYPE_CAPSULE_X: { - if (glm::abs(point.x) <= _halfExtents.x) { - return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z; + if (glm::abs(point.x) <= _halfExtents.x - _halfExtents.y) { + return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.y; } else { - glm::vec3 absPoint = glm::abs(point) - _halfExtents.x; - return glm::length(absPoint) <= _halfExtents.z; + glm::vec3 absPoint = glm::abs(point) - glm::vec3(_halfExtents.x, 0.0f, 0.0f); + return glm::length(absPoint) <= _halfExtents.y; } } case SHAPE_TYPE_CAPSULE_Y: { - if (glm::abs(point.y) <= _halfExtents.y) { - return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x; + if (glm::abs(point.y) <= _halfExtents.y - _halfExtents.z) { + return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.z; } else { - glm::vec3 absPoint = glm::abs(point) - _halfExtents.y; - return glm::length(absPoint) <= _halfExtents.x; + glm::vec3 absPoint = glm::abs(point) - glm::vec3(0.0f, _halfExtents.y, 0.0f); + return glm::length(absPoint) <= _halfExtents.z; } } case SHAPE_TYPE_CAPSULE_Z: { - if (glm::abs(point.z) <= _halfExtents.z) { - return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y; + if (glm::abs(point.z) <= _halfExtents.z - _halfExtents.x) { + return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.x; } else { - glm::vec3 absPoint = glm::abs(point) - _halfExtents.z; - return glm::length(absPoint) <= _halfExtents.y; + glm::vec3 absPoint = glm::abs(point) - glm::vec3(0.0f, 0.0f, _halfExtents.z); + return glm::length(absPoint) <= _halfExtents.x; } } case SHAPE_TYPE_BOX: diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 5020e492cf..16e260d9db 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -67,7 +67,7 @@ public: void setBox(const glm::vec3& halfExtents); void setSphere(float radius); void setPointCollection(const PointCollection& pointCollection); - void setCapsuleY(float radius, float halfHeight); + void setCapsuleY(float radius, float cylinderHalfHeight); void setOffset(const glm::vec3& offset); ShapeType getType() const { return _type; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 012e7aa1f5..39def1cab9 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -24,7 +24,6 @@ #include - #ifdef Q_OS_WIN #include #include "CPUIdent.h" @@ -348,7 +347,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, } auto voxelSizeInBytes = bytesRequiredForCodeLength(voxelSizeInOctets); // (voxelSizeInBits/8)+1; - auto voxelBufferSize = voxelSizeInBytes + sizeof(rgbColor); // 3 for color + auto voxelBufferSize = voxelSizeInBytes + sizeof(glm::u8vec3); // 3 for color // allocate our resulting buffer unsigned char* voxelOut = new unsigned char[voxelBufferSize]; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 3b24110f18..f36574bed6 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -80,43 +80,6 @@ const int BYTES_PER_COLOR = 3; const int BYTES_PER_FLAGS = 1; typedef unsigned char colorPart; typedef unsigned char nodeColor[BYTES_PER_COLOR + BYTES_PER_FLAGS]; -typedef unsigned char rgbColor[BYTES_PER_COLOR]; - -inline QDebug& operator<<(QDebug& dbg, const rgbColor& c) { - dbg.nospace() << "{type='rgbColor'" - ", red=" << c[0] << - ", green=" << c[1] << - ", blue=" << c[2] << - "}"; - return dbg; -} - -struct xColor { - xColor() {} - xColor(unsigned char r, unsigned char g, unsigned char b) : red(r), green(g), blue(b) {} - unsigned char red; - unsigned char green; - unsigned char blue; -}; - -inline QDebug& operator<<(QDebug& dbg, const xColor& c) { - dbg.nospace() << "{type='xColor'" - ", red=" << c.red << - ", green=" << c.green << - ", blue=" << c.blue << - "}"; - return dbg; -} - -inline bool operator==(const xColor& lhs, const xColor& rhs) -{ - return (lhs.red == rhs.red) && (lhs.green == rhs.green) && (lhs.blue == rhs.blue); -} - -inline bool operator!=(const xColor& lhs, const xColor& rhs) -{ - return (lhs.red != rhs.red) || (lhs.green != rhs.green) || (lhs.blue != rhs.blue); -} // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)"; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4b8768704a..97e20f5627 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -74,8 +74,13 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { } }); - bool success = false; - getParentPointer(success); + if (!_parentKnowsMe) { + bool success = false; + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + } } Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const { @@ -155,12 +160,14 @@ void SpatiallyNestable::beParentOfChild(SpatiallyNestablePointer newChild) const _childrenLock.withWriteLock([&] { _children[newChild->getID()] = newChild; }); + // Our QueryAACube will automatically be updated to include our new child } void SpatiallyNestable::forgetChild(SpatiallyNestablePointer newChild) const { _childrenLock.withWriteLock([&] { _children.remove(newChild->getID()); }); + _queryAACubeSet = false; // We need to reset our queryAACube when we lose a child } void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) { @@ -1092,7 +1099,23 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { return AACube(getWorldPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize); } -const float PARENTED_EXPANSION_FACTOR = 3.0f; +AACube SpatiallyNestable::calculateInitialQueryAACube(bool& success) { + success = false; + AACube maxAACube = getMaximumAACube(success); + if (!success) { + return AACube(); + } + + success = true; + if (shouldPuffQueryAACube()) { + // make an expanded AACube centered on the object + const float PARENTED_EXPANSION_FACTOR = 3.0f; + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + return AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + } else { + return maxAACube; + } +} bool SpatiallyNestable::updateQueryAACube() { if (!queryAACubeNeedsUpdate()) { @@ -1100,20 +1123,12 @@ bool SpatiallyNestable::updateQueryAACube() { } bool success; - AACube maxAACube = getMaximumAACube(success); + AACube initialQueryAACube = calculateInitialQueryAACube(success); if (!success) { return false; } - - if (shouldPuffQueryAACube()) { - // make an expanded AACube centered on the object - float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); - _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); - _queryAACubeIsPuffed = true; - } else { - _queryAACube = maxAACube; - _queryAACubeIsPuffed = false; - } + _queryAACube = initialQueryAACube; + _queryAACubeIsPuffed = shouldPuffQueryAACube(); forEachDescendant([&](const SpatiallyNestablePointer& descendant) { bool childSuccess; @@ -1128,6 +1143,12 @@ bool SpatiallyNestable::updateQueryAACube() { }); _queryAACubeSet = true; + + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + return true; } @@ -1158,7 +1179,7 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { // make sure children are still in their boxes, also. bool childNeedsUpdate = false; forEachDescendantTest([&](const SpatiallyNestablePointer& descendant) { - if (!childNeedsUpdate && descendant->queryAACubeNeedsUpdate()) { + if (descendant->queryAACubeNeedsUpdate() || !_queryAACube.contains(descendant->getQueryAACube())) { childNeedsUpdate = true; // Don't recurse further return false; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index a50e687a9b..03ed97afbd 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -19,7 +19,6 @@ #include "SpatialParentFinder.h" #include "shared/ReadWriteLockable.h" - class SpatiallyNestable; using SpatiallyNestableWeakPointer = std::weak_ptr; using SpatiallyNestableWeakConstPointer = std::weak_ptr; @@ -112,6 +111,7 @@ public: virtual glm::vec3 getParentAngularVelocity(bool& success) const; virtual AACube getMaximumAACube(bool& success) const; + virtual AACube calculateInitialQueryAACube(bool& success); virtual void setQueryAACube(const AACube& queryAACube); virtual bool queryAACubeNeedsUpdate() const; diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 9ed0e24593..abacb012e5 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -78,6 +78,11 @@ QDebug& operator<<(QDebug& dbg, const glm::vec3& v) { return dbg; } +QDebug& operator<<(QDebug& dbg, const glm::u8vec3& v) { + dbg.nospace() << '(' << v.x << ", " << v.y << ", " << v.z << ')'; + return dbg; +} + QDebug& operator<<(QDebug& dbg, const glm::vec4& v) { dbg.nospace() << '(' << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ')'; return dbg; diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index caf7b565f4..b6d6c522c5 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -42,6 +42,7 @@ class QDebug; // Add support for writing these to qDebug(). QDebug& operator<<(QDebug& s, const glm::vec2& v); QDebug& operator<<(QDebug& s, const glm::vec3& v); +QDebug& operator<<(QDebug& s, const glm::u8vec3& v); QDebug& operator<<(QDebug& s, const glm::vec4& v); QDebug& operator<<(QDebug& s, const glm::quat& q); QDebug& operator<<(QDebug& s, const glm::mat4& m); diff --git a/libraries/shared/src/TransformNode.h b/libraries/shared/src/TransformNode.h index 1c10bed1c0..c9340bddf0 100644 --- a/libraries/shared/src/TransformNode.h +++ b/libraries/shared/src/TransformNode.h @@ -12,7 +12,8 @@ class TransformNode { public: - virtual ~TransformNode() {} + virtual ~TransformNode() = default; + virtual Transform getTransform() = 0; }; diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index c7cbf0e724..298a1ea85d 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -68,6 +68,20 @@ vec4 vec4FromJsonValue(const QJsonValue& v) { return glmFromJson(v); } +QJsonValue toJsonValueHelper(const QVariant& variant, int type) { + // User-registered types need explicit conversion + if (type == qMetaTypeId()) { + return toJsonValue(variant.value()); + } else if (type == qMetaTypeId()) { + return toJsonValue(variant.value()); + } else if (type == qMetaTypeId()) { + return toJsonValue(variant.value()); + } else { + // Qt types are converted automatically + return QJsonValue::fromVariant(variant); + } +} + QJsonValue toJsonValue(const QObject& o) { QJsonObject json{}; @@ -76,20 +90,8 @@ QJsonValue toJsonValue(const QObject& o) { for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) { QString name = QString::fromLatin1(meta->property(i).name()); auto type = meta->property(i).userType(); - QVariant variant{ meta->property(i).read(&o) }; - QJsonValue value; - - // User-registered types need explicit conversion - if (type == qMetaTypeId()) { - value = toJsonValue(variant.value()); - } else if (type == qMetaTypeId()) { - value = toJsonValue(variant.value()); - } else if (type == qMetaTypeId()) { - value = toJsonValue(variant.value()); - } else { - // Qt types are converted automatically - value = QJsonValue::fromVariant(variant); - } + QVariant variant { meta->property(i).read(&o) }; + QJsonValue value = toJsonValueHelper(variant, type); json.insert(name, value); } @@ -106,6 +108,24 @@ QJsonValue toJsonValue(const QObject& o) { return json; } +QJsonValue toJsonValue(const QObject& o, const std::vector& props) { + QJsonObject json {}; + + const auto& meta = o.metaObject(); + // Only add the properties in props + for (auto& prop : props) { + int i = meta->indexOfProperty(prop.toStdString().c_str()); + QString name = QString::fromLatin1(meta->property(i).name()); + auto type = meta->property(i).userType(); + QVariant variant { meta->property(i).read(&o) }; + QJsonValue value = toJsonValueHelper(variant, type); + + json.insert(name, value); + } + + return json; +} + void qObjectFromJsonValue(const QJsonValue& j, QObject& o) { const QJsonObject object = j.toObject(); for (auto it = object.begin(); it != object.end(); it++) { diff --git a/libraries/shared/src/shared/JSONHelpers.h b/libraries/shared/src/shared/JSONHelpers.h index 735d33b5a5..a7e7077904 100644 --- a/libraries/shared/src/shared/JSONHelpers.h +++ b/libraries/shared/src/shared/JSONHelpers.h @@ -15,7 +15,9 @@ QJsonValue toJsonValue(const quat& q); QJsonValue toJsonValue(const vec3& v); QJsonValue toJsonValue(const vec4& v); +QJsonValue toJsonValueHelper(const QVariant& variant, int type); QJsonValue toJsonValue(const QObject& o); +QJsonValue toJsonValue(const QObject& o, const std::vector& props); quat quatFromJsonValue(const QJsonValue& q); vec3 vec3FromJsonValue(const QJsonValue& v); diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index 3878c5719b..e8e95b4df4 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -175,7 +175,7 @@ public: template using ModelIO = Model; Job(const ConceptPointer& concept) : _concept(concept) {} - virtual ~Job() {} + virtual ~Job() = default; const std::string& getName() const { return _concept->getName(); } const Varying getInput() const { return _concept->getInput(); } @@ -418,6 +418,7 @@ protected: template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5 > using VaryingSet6 = task::VaryingSet6; \ template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > using VaryingSet7 = task::VaryingSet7; \ template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 > using VaryingSet8 = task::VaryingSet8; \ + template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8 > using VaryingSet9 = task::VaryingSet9; \ template < class T, int NUM > using VaryingArray = task::VaryingArray; diff --git a/libraries/task/src/task/Varying.h b/libraries/task/src/task/Varying.h index 7b7b9907fb..9536db2799 100644 --- a/libraries/task/src/task/Varying.h +++ b/libraries/task/src/task/Varying.h @@ -328,6 +328,45 @@ public: Varying asVarying() const { return Varying((*this)); } }; +template +class VaryingSet9 : public std::tuple { +public: + using Parent = std::tuple; + + VaryingSet9() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4()), Varying(T5()), Varying(T6()), Varying(T7()), Varying(T8())) {} + VaryingSet9(const VaryingSet9& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src), std::get<5>(src), std::get<6>(src), std::get<7>(src), std::get<8>(src)) {} + VaryingSet9(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth, const Varying& sixth, const Varying& seventh, const Varying& eighth, const Varying& nine) : Parent(first, second, third, fourth, fifth, sixth, seventh, eighth, nine) {} + + const T0& get0() const { return std::get<0>((*this)).template get(); } + T0& edit0() { return std::get<0>((*this)).template edit(); } + + const T1& get1() const { return std::get<1>((*this)).template get(); } + T1& edit1() { return std::get<1>((*this)).template edit(); } + + const T2& get2() const { return std::get<2>((*this)).template get(); } + T2& edit2() { return std::get<2>((*this)).template edit(); } + + const T3& get3() const { return std::get<3>((*this)).template get(); } + T3& edit3() { return std::get<3>((*this)).template edit(); } + + const T4& get4() const { return std::get<4>((*this)).template get(); } + T4& edit4() { return std::get<4>((*this)).template edit(); } + + const T5& get5() const { return std::get<5>((*this)).template get(); } + T5& edit5() { return std::get<5>((*this)).template edit(); } + + const T6& get6() const { return std::get<6>((*this)).template get(); } + T6& edit6() { return std::get<6>((*this)).template edit(); } + + const T7& get7() const { return std::get<7>((*this)).template get(); } + T7& edit7() { return std::get<7>((*this)).template edit(); } + + const T8& get8() const { return std::get<8>((*this)).template get(); } + T8& edit8() { return std::get<8>((*this)).template edit(); } + + Varying asVarying() const { return Varying((*this)); } +}; + template < class T, int NUM > class VaryingArray : public std::array { public: diff --git a/libraries/ui/src/InteractiveWindow.cpp b/libraries/ui/src/InteractiveWindow.cpp index 6c7f2d503f..5f7999f826 100644 --- a/libraries/ui/src/InteractiveWindow.cpp +++ b/libraries/ui/src/InteractiveWindow.cpp @@ -292,8 +292,8 @@ int InteractiveWindow::getPresentationMode() const { return _qmlWindow->property(PRESENTATION_MODE_PROPERTY).toInt(); } -#ifdef Q_OS_WIN void InteractiveWindow::parentNativeWindowToMainWindow() { +#ifdef Q_OS_WIN if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "parentNativeWindowToMainWindow"); return; @@ -307,8 +307,8 @@ void InteractiveWindow::parentNativeWindowToMainWindow() { } const auto nativeWindow = qvariant_cast(nativeWindowProperty); SetWindowLongPtr((HWND)nativeWindow->winId(), GWLP_HWNDPARENT, (LONG)MainWindow::findMainWindow()->winId()); -} #endif +} void InteractiveWindow::setPresentationMode(int presentationMode) { if (QThread::currentThread() != thread()) { diff --git a/libraries/ui/src/InteractiveWindow.h b/libraries/ui/src/InteractiveWindow.h index f456b32e8d..a25d559557 100644 --- a/libraries/ui/src/InteractiveWindow.h +++ b/libraries/ui/src/InteractiveWindow.h @@ -84,9 +84,7 @@ private: Q_INVOKABLE void setPresentationMode(int presentationMode); Q_INVOKABLE int getPresentationMode() const; -#ifdef Q_OS_WIN Q_INVOKABLE void parentNativeWindowToMainWindow(); -#endif public slots: diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 74098f69c7..f67a356078 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -250,6 +250,7 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { engine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory); auto importList = engine->importPathList(); + importList.insert(importList.begin(), PathUtils::resourcesPath() + "qml/"); importList.insert(importList.begin(), PathUtils::resourcesPath()); engine->setImportPathList(importList); for (const auto& path : importList) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 1081f8c4e7..52d359ad0d 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -888,6 +888,12 @@ void TabletProxy::desktopWindowClosed() { gotoHomeScreen(); } +void TabletProxy::unfocus() { + if (_qmlOffscreenSurface) { + _qmlOffscreenSurface->lowerKeyboard(); + } +} + QQuickItem* TabletProxy::getQmlTablet() const { if (!_qmlTabletRoot) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 2d37402d01..9821ad1263 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -232,6 +232,7 @@ public: const QString getName() const { return _name; } bool getToolbarMode() const { return _toolbarMode; } void setToolbarMode(bool toolbarMode); + void unfocus(); /**jsdoc * @function TabletProxy#gotoMenuScreen diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 7f192d6e52..a3b3b7dc57 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -70,9 +70,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, // check if this is a request to a highfidelity URL bool isAuthable = isAuthableHighFidelityURL(info.requestUrl()); + auto accountManager = DependencyManager::get(); if (isAuthable) { // if we have an access token, add it to the right HTTP header for authorization - auto accountManager = DependencyManager::get(); if (accountManager->hasValidAccessToken()) { static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization"; @@ -84,13 +84,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, static const QString USER_AGENT = "User-Agent"; const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" }; const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" }; + const QString tokenStringLimitedCommerce{ "Chrome/48.0 (HighFidelityInterface limitedCommerce)" }; - // During the period in which we have HFC commerce in the system, but not applied everywhere: - const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" }; - Setting::Handle _settingSwitch{ "commerce", true }; - bool isMoney = _settingSwitch.get(); - - const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse); + const QString tokenString = !isAuthable ? tokenStringMobile : (accountManager->getLimitedCommerce() ? tokenStringLimitedCommerce : tokenStringMetaverse); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); } diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 9d0c693149..0a268df9fc 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -31,13 +31,25 @@ void SetupViews::run(const WorkloadContextPointer& renderContext, const Input& i auto& outViews = outputs; outViews.clear(); - // Filter the first view centerer on the avatar head if needed if (_views.size() >= 2) { + // when inputs contains two or more views: + // index 0 = view from avatar's head + // index 1 = view from camera + // index 2 and higher = secondary camera and whatever if (data.useAvatarView) { + // for debug purposes we keep the head view and skip that of the camera outViews.push_back(_views[0]); outViews.insert(outViews.end(), _views.begin() + 2, _views.end()); } else { - outViews.insert(outViews.end(), _views.begin() + 1, _views.end()); + // otherwise we use all of the views... + const float MIN_HEAD_CAMERA_SEPARATION_SQUARED = MIN_VIEW_BACK_FRONTS[0][1] * MIN_VIEW_BACK_FRONTS[0][1]; + if (glm::distance2(_views[0].origin, _views[1].origin) < MIN_HEAD_CAMERA_SEPARATION_SQUARED) { + // ... unless the first two are close enough to be considered the same + // in which case we only keep one of them + outViews.insert(outViews.end(), _views.begin() + 1, _views.end()); + } else { + outViews = _views; + } } } else { outViews = _views; @@ -177,7 +189,6 @@ void ControlViews::regulateViews(workload::Views& outViews, const workload::Timi outView.regionBackFronts[workload::Region::R1] = regionBackFronts[workload::Region::R1]; outView.regionBackFronts[workload::Region::R2] = regionBackFronts[workload::Region::R2]; outView.regionBackFronts[workload::Region::R3] = regionBackFronts[workload::Region::R3]; - workload::View::updateRegionsFromBackFronts(outView); } } diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index c2909c54fb..c99908d5b7 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -52,6 +52,7 @@ protected: friend class NeuronPlugin; InputDevice() : controller::InputDevice("Neuron") {} + virtual ~InputDevice() = default; // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; diff --git a/plugins/hifiSdl2/src/Joystick.h b/plugins/hifiSdl2/src/Joystick.h index 7ea17739e3..ae90470974 100644 --- a/plugins/hifiSdl2/src/Joystick.h +++ b/plugins/hifiSdl2/src/Joystick.h @@ -15,7 +15,7 @@ #include #include -#include +#include #undef main #include diff --git a/plugins/hifiSdl2/src/SDL2Manager.h b/plugins/hifiSdl2/src/SDL2Manager.h index 0daad6fd8b..ac2f446205 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.h +++ b/plugins/hifiSdl2/src/SDL2Manager.h @@ -12,7 +12,7 @@ #ifndef hifi__SDL2Manager_h #define hifi__SDL2Manager_h -#include +#include #include #include diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 893b7f48b1..abce753b4d 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -8,9 +8,6 @@ if (WIN32 AND (NOT USE_GLES)) - # we're using static GLEW, so define GLEW_STATIC - add_definitions(-DGLEW_STATIC) - # if we were passed an Oculus App ID for entitlement checks, send that along if (DEFINED ENV{OCULUS_APP_ID}) add_definitions(-DOCULUS_APP_ID="$ENV{OCULUS_APP_ID}") @@ -19,7 +16,7 @@ if (WIN32 AND (NOT USE_GLES)) set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) link_hifi_libraries( - shared task gl gpu ${PLATFORM_GL_BACKEND} controllers ui qml + shared task gl shaders gpu ${PLATFORM_GL_BACKEND} controllers ui qml plugins ui-plugins display-plugins input-plugins audio-client networking render-utils ${PLATFORM_GL_BACKEND} diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 511984c657..402b05f39c 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -85,6 +85,11 @@ private: if (!OVR_SUCCESS(ovr_Create(&session, &luid))) { qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError(); return; + } else { + ovrResult setFloorLevelOrigin = ovr_SetTrackingOriginType(session, ovrTrackingOrigin::ovrTrackingOrigin_FloorLevel); + if (!OVR_SUCCESS(setFloorLevelOrigin)) { + qCWarning(oculusLog) << "Failed to set the Oculus tracking origin to floor level" << ovr::getError(); + } } } diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index 00e90fb6d7..33d27c4e9d 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -13,7 +13,7 @@ if (APPLE) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() - link_hifi_libraries(shared gl gpu plugins ui ui-plugins display-plugins input-plugins midi ${PLATFORM_GL_BACKEND}) + link_hifi_libraries(shared shaders gl gpu plugins ui ui-plugins display-plugins input-plugins midi ${PLATFORM_GL_BACKEND}) include_hifi_library_headers(octree) diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index ff94152d57..7c3671991e 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -7,20 +7,13 @@ # if (WIN32 AND (NOT USE_GLES)) - # we're using static GLEW, so define GLEW_STATIC - add_definitions(-DGLEW_STATIC) set(TARGET_NAME openvr) setup_hifi_plugin(Gui Qml Multimedia) link_hifi_libraries(shared task gl qml networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine - audio-client render-utils graphics gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND}) - + audio-client render-utils graphics shaders gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND}) include_hifi_library_headers(octree) - add_dependency_external_projects(OpenVR) - - find_package(OpenVR REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) + target_openvr() target_link_libraries(${TARGET_NAME} Winmm.lib) endif() diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index ef0ac65c2a..99c861871d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -463,7 +463,7 @@ bool OpenVrDisplayPlugin::internalActivate() { auto chaperone = vr::VRChaperone(); if (chaperone) { float const UI_RADIUS = 1.0f; - float const UI_HEIGHT = 1.6f; + float const UI_HEIGHT = 0.0f; float const UI_Z_OFFSET = 0.5; float xSize, zSize; diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 3e26f304f8..69797340dd 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -129,6 +129,28 @@ static glm::mat4 calculateResetMat() { return glm::mat4(); } +static QString outOfRangeDataStrategyToString(ViveControllerManager::OutOfRangeDataStrategy strategy) { + switch (strategy) { + default: + case ViveControllerManager::OutOfRangeDataStrategy::None: + return "None"; + case ViveControllerManager::OutOfRangeDataStrategy::Freeze: + return "Freeze"; + case ViveControllerManager::OutOfRangeDataStrategy::Drop: + return "Drop"; + } +} + +static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrategy(const QString& string) { + if (string == "Drop") { + return ViveControllerManager::OutOfRangeDataStrategy::Drop; + } else if (string == "Freeze") { + return ViveControllerManager::OutOfRangeDataStrategy::Freeze; + } else { + return ViveControllerManager::OutOfRangeDataStrategy::None; + } +} + bool ViveControllerManager::isDesktopMode() { if (_container) { return !_container->getActiveDisplayPlugin()->isHmd(); @@ -288,8 +310,10 @@ void ViveControllerManager::loadSettings() { if (_inputDevice) { const double DEFAULT_ARM_CIRCUMFERENCE = 0.33; const double DEFAULT_SHOULDER_WIDTH = 0.48; + const QString DEFAULT_OUT_OF_RANGE_STRATEGY = "Drop"; _inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble(); _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); + _inputDevice->_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(settings.value("outOfRangeDataStrategy", QVariant(DEFAULT_OUT_OF_RANGE_STRATEGY)).toString()); } } settings.endGroup(); @@ -303,6 +327,7 @@ void ViveControllerManager::saveSettings() const { if (_inputDevice) { settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference); settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); + settings.setValue(QString("outOfRangeDataStrategy"), outOfRangeDataStrategyToString(_inputDevice->_outOfRangeDataStrategy)); } } settings.endGroup(); @@ -446,6 +471,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso hmdDesktopTracking = iter.value().toBool(); } else if (iter.key() == "desktopMode") { hmdDesktopMode = iter.value().toBool(); + } else if (iter.key() == "outOfRangeDataStrategy") { + _outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(iter.value().toString()); } iter++; } @@ -468,6 +495,7 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() { configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; + configurationSettings["outOfRangeDataStrategy"] = outOfRangeDataStrategyToString(_outOfRangeDataStrategy); return configurationSettings; } @@ -484,6 +512,10 @@ void ViveControllerManager::InputDevice::emitCalibrationStatus() { emit inputConfiguration->calibrationStatus(status); } +static controller::Pose buildPose(const glm::mat4& mat, const glm::vec3& linearVelocity, const glm::vec3& angularVelocity) { + return controller::Pose(extractTranslation(mat), glmExtractRotation(mat), linearVelocity, angularVelocity); +} + void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) { uint32_t poseIndex = controller::TRACKED_OBJECT_00 + deviceIndex; printDeviceTrackingResultChange(deviceIndex); @@ -492,35 +524,48 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde _nextSimPoseData.vrPoses[deviceIndex].bPoseIsValid && poseIndex <= controller::TRACKED_OBJECT_15) { - mat4& mat = mat4(); - vec3 linearVelocity = vec3(); - vec3 angularVelocity = vec3(); - // check if the device is tracking out of range, then process the correct pose depending on the result. - if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult != vr::TrackingResult_Running_OutOfRange) { - mat = _nextSimPoseData.poses[deviceIndex]; - linearVelocity = _nextSimPoseData.linearVelocities[deviceIndex]; - angularVelocity = _nextSimPoseData.angularVelocities[deviceIndex]; - } else { - mat = _lastSimPoseData.poses[deviceIndex]; - linearVelocity = _lastSimPoseData.linearVelocities[deviceIndex]; - angularVelocity = _lastSimPoseData.angularVelocities[deviceIndex]; - - // make sure that we do not overwrite the pose in the _lastSimPose with incorrect data. - _nextSimPoseData.poses[deviceIndex] = _lastSimPoseData.poses[deviceIndex]; - _nextSimPoseData.linearVelocities[deviceIndex] = _lastSimPoseData.linearVelocities[deviceIndex]; - _nextSimPoseData.angularVelocities[deviceIndex] = _lastSimPoseData.angularVelocities[deviceIndex]; + controller::Pose pose; + switch (_outOfRangeDataStrategy) { + case OutOfRangeDataStrategy::Drop: + default: + // Drop - Mark all non Running_OK results as invald + if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult == vr::TrackingResult_Running_OK) { + pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]); + } else { + pose.valid = false; + } + break; + case OutOfRangeDataStrategy::None: + // None - Ignore eTrackingResult all together + pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]); + break; + case OutOfRangeDataStrategy::Freeze: + // Freeze - Dont invalide non Running_OK poses, instead just return the last good pose. + if (_nextSimPoseData.vrPoses[deviceIndex].eTrackingResult == vr::TrackingResult_Running_OK) { + pose = buildPose(_nextSimPoseData.poses[deviceIndex], _nextSimPoseData.linearVelocities[deviceIndex], _nextSimPoseData.angularVelocities[deviceIndex]); + } else { + pose = buildPose(_lastSimPoseData.poses[deviceIndex], _lastSimPoseData.linearVelocities[deviceIndex], _lastSimPoseData.angularVelocities[deviceIndex]); + // make sure that we do not overwrite the pose in the _lastSimPose with incorrect data. + _nextSimPoseData.poses[deviceIndex] = _lastSimPoseData.poses[deviceIndex]; + _nextSimPoseData.linearVelocities[deviceIndex] = _lastSimPoseData.linearVelocities[deviceIndex]; + _nextSimPoseData.angularVelocities[deviceIndex] = _lastSimPoseData.angularVelocities[deviceIndex]; + } + break; } - controller::Pose pose(extractTranslation(mat), glmExtractRotation(mat), linearVelocity, angularVelocity); + if (pose.valid) { + // transform into avatar frame + glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; + _poseStateMap[poseIndex] = pose.transform(controllerToAvatar); - // transform into avatar frame - glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; - _poseStateMap[poseIndex] = pose.transform(controllerToAvatar); - - // but _validTrackedObjects remain in sensor frame - _validTrackedObjects.push_back(std::make_pair(poseIndex, pose)); - _trackedControllers++; + // but _validTrackedObjects remain in sensor frame + _validTrackedObjects.push_back(std::make_pair(poseIndex, pose)); + _trackedControllers++; + } else { + // insert invalid pose into state map + _poseStateMap[poseIndex] = pose; + } } else { controller::Pose invalidPose; _poseStateMap[poseIndex] = invalidPose; diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 30f8590062..06e13e1c49 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -60,11 +60,18 @@ public: virtual void saveSettings() const override; virtual void loadSettings() override; + enum class OutOfRangeDataStrategy { + None, + Freeze, + Drop + }; + private: class InputDevice : public controller::InputDevice { public: InputDevice(vr::IVRSystem*& system); bool isHeadControllerMounted() const { return _overrideHead; } + private: // Device functions controller::Input::NamedVector getAvailableInputs() const override; @@ -162,6 +169,7 @@ private: FilteredStick _filteredLeftStick; FilteredStick _filteredRightStick; std::string _headsetName {""}; + OutOfRangeDataStrategy _outOfRangeDataStrategy { OutOfRangeDataStrategy::Drop }; std::vector _validTrackedObjects; std::map _pucksPostOffset; diff --git a/prebuild.py b/prebuild.py new file mode 100644 index 0000000000..dacc49a86e --- /dev/null +++ b/prebuild.py @@ -0,0 +1,257 @@ +#!python + +import argparse +import concurrent +import hashlib +import importlib +import json +import os +import platform +import shutil +import ssl +import subprocess +import sys +import tarfile +import tempfile +import time +import urllib.request +import functools + +print = functools.partial(print, flush=True) + +def executeSubprocess(processArgs, folder=None, env=None): + restoreDir = None + if folder != None: + restoreDir = os.getcwd() + os.chdir(folder) + + process = subprocess.Popen( + processArgs, stdout=sys.stdout, stderr=sys.stderr, env=env) + process.wait() + + if (0 != process.returncode): + raise RuntimeError('Call to "{}" failed.\n\narguments:\n{}\n'.format( + processArgs[0], + ' '.join(processArgs[1:]), + )) + + if restoreDir != None: + os.chdir(restoreDir) + + +def hashFile(file): + hasher = hashlib.sha512() + with open(file, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hasher.update(chunk) + return hasher.hexdigest() + + +def hashFolder(folder): + hasher = hashlib.sha256() + for dirName, subdirList, fileList in os.walk(folder): + for fname in fileList: + with open(os.path.join(folder, dirName, fname), "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hasher.update(chunk) + return hasher.hexdigest() + + +def downloadAndExtract(url, destPath, hash=None): + tempFileDescriptor, tempFileName = tempfile.mkstemp() + # OSX Python doesn't support SSL, so we need to bypass it. + # However, we still validate the downloaded file's sha512 hash + context = ssl._create_unverified_context() + with urllib.request.urlopen(url, context=context) as response, open(tempFileDescriptor, 'wb') as tempFile: + shutil.copyfileobj(response, tempFile) + + # Verify the hash + if hash and hash != hashFile(tempFileName): + raise RuntimeError("Downloaded file does not match hash") + + # Extract the archive + with tarfile.open(tempFileName, 'r:gz') as tgz: + tgz.extractall(destPath) + os.remove(tempFileName) + + +class VcpkgRepo: + def __init__(self): + global args + scriptPath = os.path.dirname(os.path.realpath(sys.argv[0])) + # our custom ports, relative to the script location + self.sourcePortsPath = os.path.join(scriptPath, 'cmake', 'ports') + # FIXME Revert to ports hash before release + self.id = hashFolder(self.sourcePortsPath)[:8] + + if args.vcpkg_root is not None: + print("override vcpkg path with " + args.vcpkg_root) + self.path = args.vcpkg_root + else: + defaultBasePath = os.path.join(tempfile.gettempdir(), 'hifi', 'vcpkg') + basePath = os.getenv('HIFI_VCPKG_BASE', defaultBasePath) + if (not os.path.isdir(basePath)): + os.makedirs(basePath) + self.path = os.path.join(basePath, self.id) + + self.tagFile = os.path.join(self.path, '.id') + # A format version attached to the tag file... increment when you want to force the build systems to rebuild + # without the contents of the ports changing + self.version = 1 + self.tagContents = "{}_{}".format(self.id, self.version) + + print("prebuild path: " + self.path) + # OS dependent information + system = platform.system() + if 'Windows' == system: + self.exe = os.path.join(self.path, 'vcpkg.exe') + self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-win32.tar.gz?versionId=YZYkDejDRk7L_hrK_WVFthWvisAhbDzZ' + self.vcpkgHash = '3e0ff829a74956491d57666109b3e6b5ce4ed0735c24093884317102387b2cb1b2cd1ff38af9ed9173501f6e32ffa05cc6fe6d470b77a71ca1ffc3e0aa46ab9e' + self.hostTriplet = 'x64-windows' + elif 'Darwin' == system: + self.exe = os.path.join(self.path, 'vcpkg') + self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-osx.tar.gz?versionId=_fhqSxjfrtDJBvEsQ8L_ODcdUjlpX9cc' + self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d' + self.hostTriplet = 'x64-osx' + else: + self.exe = os.path.join(self.path, 'vcpkg') + self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-linux.tar.gz?versionId=97Nazh24etEVKWz33XwgLY0bvxEfZgMU' + self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d' + self.hostTriplet = 'x64-linux' + + if args.android: + self.triplet = 'arm64-android' + else: + self.triplet = self.hostTriplet + + def outOfDate(self): + global args + # Prevent doing a clean if we've explcitly set a directory for vcpkg + if args.vcpkg_root is not None: + return False + if args.force_build: + return True + print("Looking for tag file {}".format(self.tagFile)) + if not os.path.isfile(self.tagFile): + return True + with open(self.tagFile, 'r') as f: + storedTag = f.read() + print("Found stored tag {}".format(storedTag)) + if storedTag != self.tagContents: + print("Doesn't match computed tag {}".format(self.tagContents)) + return True + return False + + def clean(self): + cleanPath = self.path + print("Cleaning vcpkg installation at {}".format(cleanPath)) + if os.path.isdir(self.path): + print("Removing {}".format(cleanPath)) + shutil.rmtree(cleanPath, ignore_errors=True) + + def bootstrap(self): + global args + if self.outOfDate(): + self.clean() + + # don't download the vcpkg binaries if we're working with an explicit + # vcpkg directory (possibly a git checkout) + if args.vcpkg_root is None: + downloadVcpkg = False + if args.force_bootstrap: + print("Forcing bootstrap") + downloadVcpkg = True + + if not downloadVcpkg and not os.path.isfile(self.exe): + print("Missing executable, boostrapping") + downloadVcpkg = True + + # Make sure we have a vcpkg executable + testFile = os.path.join(self.path, '.vcpkg-root') + if not downloadVcpkg and not os.path.isfile(testFile): + print("Missing {}, bootstrapping".format(testFile)) + downloadVcpkg = True + + if downloadVcpkg: + print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path)) + downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash) + + print("Replacing port files") + portsPath = os.path.join(self.path, 'ports') + if (os.path.islink(portsPath)): + os.unlink(portsPath) + if (os.path.isdir(portsPath)): + shutil.rmtree(portsPath, ignore_errors=True) + shutil.copytree(self.sourcePortsPath, portsPath) + + def downloadAndroidDependencies(self): + url = "https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-arm64-android.tar.gz" + hash = "832f82a4d090046bdec25d313e20f56ead45b54dd06eee3798c5c8cbdd64cce4067692b1c3f26a89afe6ff9917c10e4b601c118bea06d23f8adbfe5c0ec12bc3" + dest = os.path.join(self.path, 'installed') + downloadAndExtract(url, dest, hash) + + def run(self, commands): + actualCommands = [self.exe, '--vcpkg-root', self.path] + actualCommands.extend(commands) + print("Running command") + print(actualCommands) + executeSubprocess(actualCommands, folder=self.path) + + def buildDependencies(self): + global args + print("Installing host tools") + self.run(['install', '--triplet', self.hostTriplet, 'hifi-host-tools']) + # Special case for android, grab a bunch of binaries + if args.android: + self.downloadAndroidDependencies() + return + + print("Installing build dependencies") + self.run(['install', '--triplet', self.triplet, 'hifi-client-deps']) + # Remove temporary build artifacts + builddir = os.path.join(self.path, 'buildtrees') + if os.path.isdir(builddir): + print("Wiping build trees") + shutil.rmtree(builddir, ignore_errors=True) + + def writeConfig(self): + global args + configFilePath = os.path.join(args.build_root, 'vcpkg.cmake') + print("Writing cmake config to {}".format(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') + cmakeTemplate = 'set(CMAKE_TOOLCHAIN_FILE "{}" CACHE FILEPATH "Toolchain file")\n' + cmakeTemplate += 'set(VCPKG_INSTALL_ROOT "{}" CACHE FILEPATH "vcpkg installed packages path")\n' + cmakeTemplate += 'set(VCPKG_TOOLS_DIR "{}" CACHE FILEPATH "vcpkg installed packages path")\n' + cmakeConfig = cmakeTemplate.format(cmakeScript, installPath, toolsPath).replace('\\', '/') + with open(configFilePath, 'w') as f: + f.write(cmakeConfig) + + def writeTag(self): + print("Writing tag {} to {}".format(self.tagContents, self.tagFile)) + with open(self.tagFile, 'w') as f: + f.write(self.tagContents) + +def main(): + vcpkg = VcpkgRepo() + vcpkg.bootstrap() + vcpkg.buildDependencies() + vcpkg.writeConfig() + vcpkg.writeTag() + + + +from argparse import ArgumentParser +parser = ArgumentParser(description='Prepare build dependencies.') +parser.add_argument('--android', action='store_true') +parser.add_argument('--debug', action='store_true') +parser.add_argument('--force-bootstrap', action='store_true') +parser.add_argument('--force-build', action='store_true') +parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution') +parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build') + +args = parser.parse_args() +main() + diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 9efb040624..5df1b3e511 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -45,7 +45,7 @@ if (Window.interstitialModeEnabled) { } // add a menu item for debugging -var MENU_CATEGORY = "Developer"; +var MENU_CATEGORY = "Developer > Scripting"; var MENU_ITEM = "Debug defaultScripts.js"; var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; diff --git a/scripts/developer/accelerationFilterApp.js b/scripts/developer/accelerationFilterApp.js new file mode 100644 index 0000000000..a2ef937e52 --- /dev/null +++ b/scripts/developer/accelerationFilterApp.js @@ -0,0 +1,222 @@ +var LEFT_HAND_INDEX = 0; +var RIGHT_HAND_INDEX = 1; +var LEFT_FOOT_INDEX = 2; +var RIGHT_FOOT_INDEX = 3; +var HIPS_INDEX = 4; +var SPINE2_INDEX = 5; + +var mappingJson = { + name: "com.highfidelity.testing.accelerationTest", + channels: [ + { + from: "Standard.LeftHand", + to: "Actions.LeftHand", + filters: [ + { + type: "accelerationLimiter", + rotationAccelerationLimit: 2000.0, + translationAccelerationLimit: 100.0, + } + ] + }, + { + from: "Standard.RightHand", + to: "Actions.RightHand", + filters: [ + { + type: "accelerationLimiter", + rotationAccelerationLimit: 2000.0, + translationAccelerationLimit: 100.0, + } + ] + }, + { + from: "Standard.LeftFoot", + to: "Actions.LeftFoot", + filters: [ + { + type: "accelerationLimiter", + rotationAccelerationLimit: 2000.0, + translationAccelerationLimit: 100.0, + } + ] + }, + { + from: "Standard.RightFoot", + to: "Actions.RightFoot", + filters: [ + { + type: "accelerationLimiter", + rotationAccelerationLimit: 2000.0, + translationAccelerationLimit: 100.0, + } + ] + }, + { + from: "Standard.Hips", + to: "Actions.Hips", + filters: [ + { + type: "accelerationLimiter", + rotationAccelerationLimit: 2000.0, + translationAccelerationLimit: 100.0, + } + ] + }, + { + from: "Standard.Spine2", + to: "Actions.Spine2", + filters: [ + { + type: "accelerationLimiter", + rotationAccelerationLimit: 2000.0, + translationAccelerationLimit: 100.0, + } + ] + } + ] +}; + +// +// tablet app boiler plate +// + +var TABLET_BUTTON_NAME = "ACCFILT"; +var HTML_URL = "https://s3.amazonaws.com/hifi-public/tony/html/accelerationFilterApp.html?2"; + +var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); +var tabletButton = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: "https://s3.amazonaws.com/hifi-public/tony/icons/tpose-i.svg", + activeIcon: "https://s3.amazonaws.com/hifi-public/tony/icons/tpose-a.svg" +}); + +tabletButton.clicked.connect(function () { + if (shown) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(HTML_URL); + } +}); + +var shown = false; + +function onScreenChanged(type, url) { + if (type === "Web" && url === HTML_URL) { + tabletButton.editProperties({isActive: true}); + if (!shown) { + // hook up to event bridge + tablet.webEventReceived.connect(onWebEventReceived); + shownChanged(true); + } + shown = true; + } else { + tabletButton.editProperties({isActive: false}); + if (shown) { + // disconnect from event bridge + tablet.webEventReceived.disconnect(onWebEventReceived); + shownChanged(false); + } + shown = false; + } +} + +function getTranslationAccelerationLimit(i) { + return mappingJson.channels[i].filters[0].translationAccelerationLimit; +} +function setTranslationAccelerationLimit(i, value) { + mappingJson.channels[i].filters[0].translationAccelerationLimit = value; + mappingChanged(); +} +function getRotationAccelerationLimit(i) { + return mappingJson.channels[i].filters[0].rotationAccelerationLimit; +} +function setRotationAccelerationLimit(i, value) { + mappingJson.channels[i].filters[0].rotationAccelerationLimit = value; mappingChanged(); +} + +function onWebEventReceived(msg) { + if (msg.name === "init-complete") { + var values = [ + {name: "left-hand-translation-acceleration-limit", val: getTranslationAccelerationLimit(LEFT_HAND_INDEX), checked: false}, + {name: "left-hand-rotation-acceleration-limit", val: getRotationAccelerationLimit(LEFT_HAND_INDEX), checked: false}, + {name: "right-hand-translation-acceleration-limit", val: getTranslationAccelerationLimit(RIGHT_HAND_INDEX), checked: false}, + {name: "right-hand-rotation-acceleration-limit", val: getRotationAccelerationLimit(RIGHT_HAND_INDEX), checked: false}, + {name: "left-foot-translation-acceleration-limit", val: getTranslationAccelerationLimit(LEFT_FOOT_INDEX), checked: false}, + {name: "left-foot-rotation-acceleration-limit", val: getRotationAccelerationLimit(LEFT_FOOT_INDEX), checked: false}, + {name: "right-foot-translation-acceleration-limit", val: getTranslationAccelerationLimit(RIGHT_FOOT_INDEX), checked: false}, + {name: "right-foot-rotation-acceleration-limit", val: getRotationAccelerationLimit(RIGHT_FOOT_INDEX), checked: false}, + {name: "hips-translation-acceleration-limit", val: getTranslationAccelerationLimit(HIPS_INDEX), checked: false}, + {name: "hips-rotation-acceleration-limit", val: getRotationAccelerationLimit(HIPS_INDEX), checked: false}, + {name: "spine2-translation-acceleration-limit", val: getTranslationAccelerationLimit(SPINE2_INDEX), checked: false}, + {name: "spine2-rotation-acceleration-limit", val: getRotationAccelerationLimit(SPINE2_INDEX), checked: false} + ]; + tablet.emitScriptEvent(JSON.stringify(values)); + } else if (msg.name === "left-hand-translation-acceleration-limit") { + setTranslationAccelerationLimit(LEFT_HAND_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "left-hand-rotation-acceleration-limit") { + setRotationAccelerationLimit(LEFT_HAND_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "right-hand-translation-acceleration-limit") { + setTranslationAccelerationLimit(RIGHT_HAND_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "right-hand-rotation-acceleration-limit") { + setRotationAccelerationLimit(RIGHT_HAND_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "left-foot-translation-acceleration-limit") { + setTranslationAccelerationLimit(LEFT_FOOT_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "left-foot-rotation-acceleration-limit") { + setRotationAccelerationLimit(LEFT_FOOT_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "right-foot-translation-acceleration-limit") { + setTranslationAccelerationLimit(RIGHT_FOOT_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "right-foot-rotation-acceleration-limit") { + setRotationAccelerationLimit(RIGHT_FOOT_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "hips-translation-acceleration-limit") { + setTranslationAccelerationLimit(HIPS_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "hips-rotation-acceleration-limit") { + setRotationAccelerationLimit(HIPS_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "spine2-translation-acceleration-limit") { + setTranslationAccelerationLimit(SPINE2_INDEX, parseInt(msg.val, 10)); + } else if (msg.name === "spine2-rotation-acceleration-limit") { + setRotationAccelerationLimit(SPINE2_INDEX, parseInt(msg.val, 10)); + } +} + +tablet.screenChanged.connect(onScreenChanged); + +function shutdownTabletApp() { + tablet.removeButton(tabletButton); + if (shown) { + tablet.webEventReceived.disconnect(onWebEventReceived); + tablet.gotoHomeScreen(); + } + tablet.screenChanged.disconnect(onScreenChanged); +} + +// +// end tablet app boiler plate +// + +var mapping; +function mappingChanged() { + if (mapping) { + mapping.disable(); + } + mapping = Controller.parseMapping(JSON.stringify(mappingJson)); + mapping.enable(); +} + +function shownChanged(newShown) { + if (newShown) { + mappingChanged(); + } else { + mapping.disable(); + } +} + +mappingChanged(); + +Script.scriptEnding.connect(function() { + if (mapping) { + mapping.disable(); + } + tablet.removeButton(tabletButton); +}); + diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index 993ca49a40..84bd3c323c 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -19,6 +19,23 @@ if (scripts.length >= 2) { return; } +var SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME = "Developer" +var SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME = "Suppress messages from default scripts in Debug Window"; +var DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS = 'debugWindowSuppressDefaultScripts'; +var suppressDefaultScripts = Settings.getValue(DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS, false) +Menu.addMenuItem({ + menuName: SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME, + menuItemName: SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME, + isCheckable: true, + isChecked: suppressDefaultScripts +}); + +Menu.menuItemEvent.connect(function(menuItem) { + if (menuItem === SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME) { + suppressDefaultScripts = Menu.isOptionChecked(SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME); + } +}); + // Set up the qml ui var qml = Script.resolvePath('debugWindow.qml'); @@ -61,17 +78,24 @@ window.visibleChanged.connect(function() { window.closed.connect(function () { Script.stop(); }); +function shouldLogMessage(scriptFileName) { + return !suppressDefaultScripts + || (scriptFileName !== "defaultScripts.js" && scriptFileName != "controllerScripts.js"); +} + var getFormattedDate = function() { var date = new Date(); return date.getMonth() + "/" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds(); }; var sendToLogWindow = function(type, message, scriptFileName) { - var typeFormatted = ""; - if (type) { - typeFormatted = type + " - "; + if (shouldLogMessage(scriptFileName)) { + var typeFormatted = ""; + if (type) { + typeFormatted = type + " - "; + } + window.sendToQml("[" + getFormattedDate() + "] " + "[" + scriptFileName + "] " + typeFormatted + message); } - window.sendToQml("[" + getFormattedDate() + "] " + "[" + scriptFileName + "] " + typeFormatted + message); }; ScriptDiscoveryService.printedMessage.connect(function(message, scriptFileName) { @@ -95,6 +119,10 @@ ScriptDiscoveryService.clearDebugWindow.connect(function() { }); Script.scriptEnding.connect(function () { + Settings.setValue(DEBUG_WINDOW_SUPPRESS_DEFAULTS_SCRIPTS, + Menu.isOptionChecked(SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME)); + Menu.removeMenuItem(SUPPRESS_DEFAULT_SCRIPTS_MENU_NAME, SUPPRESS_DEFAULT_SCRIPTS_ITEM_NAME); + var geometry = JSON.stringify({ x: window.position.x, y: window.position.y, diff --git a/scripts/developer/exponentialFilterApp.js b/scripts/developer/exponentialFilterApp.js new file mode 100644 index 0000000000..774ea95533 --- /dev/null +++ b/scripts/developer/exponentialFilterApp.js @@ -0,0 +1,240 @@ +var LEFT_HAND_INDEX = 0; +var RIGHT_HAND_INDEX = 1; +var LEFT_FOOT_INDEX = 2; +var RIGHT_FOOT_INDEX = 3; +var HIPS_INDEX = 4; +var SPINE2_INDEX = 5; + +var HAND_SMOOTHING_TRANSLATION = 0.3; +var HAND_SMOOTHING_ROTATION = 0.15; +var FOOT_SMOOTHING_TRANSLATION = 0.3; +var FOOT_SMOOTHING_ROTATION = 0.15; +var TORSO_SMOOTHING_TRANSLATION = 0.3; +var TORSO_SMOOTHING_ROTATION = 0.16; + +var mappingJson = { + name: "com.highfidelity.testing.exponentialFilterApp", + channels: [ + { + from: "Standard.LeftHand", + to: "Actions.LeftHand", + filters: [ + { + type: "exponentialSmoothing", + translation: HAND_SMOOTHING_TRANSLATION, + rotation: HAND_SMOOTHING_ROTATION + } + ] + }, + { + from: "Standard.RightHand", + to: "Actions.RightHand", + filters: [ + { + type: "exponentialSmoothing", + translation: HAND_SMOOTHING_TRANSLATION, + rotation: HAND_SMOOTHING_ROTATION + } + ] + }, + { + from: "Standard.LeftFoot", + to: "Actions.LeftFoot", + filters: [ + { + type: "exponentialSmoothing", + translation: FOOT_SMOOTHING_TRANSLATION, + rotation: FOOT_SMOOTHING_ROTATION + } + ] + }, + { + from: "Standard.RightFoot", + to: "Actions.RightFoot", + filters: [ + { + type: "exponentialSmoothing", + translation: FOOT_SMOOTHING_TRANSLATION, + rotation: FOOT_SMOOTHING_ROTATION + } + ] + }, + { + from: "Standard.Hips", + to: "Actions.Hips", + filters: [ + { + type: "exponentialSmoothing", + translation: TORSO_SMOOTHING_TRANSLATION, + rotation: TORSO_SMOOTHING_ROTATION + } + ] + }, + { + from: "Standard.Spine2", + to: "Actions.Spine2", + filters: [ + { + type: "exponentialSmoothing", + translation: TORSO_SMOOTHING_TRANSLATION, + rotation: TORSO_SMOOTHING_ROTATION + } + ] + } + ] +}; + +// +// tablet app boiler plate +// + +var TABLET_BUTTON_NAME = "EXPFILT"; +var HTML_URL = "https://s3.amazonaws.com/hifi-public/tony/html/exponentialFilterApp.html?7"; + +var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); +var tabletButton = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: "https://s3.amazonaws.com/hifi-public/tony/icons/tpose-i.svg", + activeIcon: "https://s3.amazonaws.com/hifi-public/tony/icons/tpose-a.svg" +}); + +tabletButton.clicked.connect(function () { + if (shown) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(HTML_URL); + } +}); + +var shown = false; + +function onScreenChanged(type, url) { + if (type === "Web" && url === HTML_URL) { + tabletButton.editProperties({isActive: true}); + if (!shown) { + // hook up to event bridge + tablet.webEventReceived.connect(onWebEventReceived); + shownChanged(true); + } + shown = true; + } else { + tabletButton.editProperties({isActive: false}); + if (shown) { + // disconnect from event bridge + tablet.webEventReceived.disconnect(onWebEventReceived); + shownChanged(false); + } + shown = false; + } +} + +function getTranslation(i) { + return mappingJson.channels[i].filters[0].translation; +} +function setTranslation(i, value) { + mappingJson.channels[i].filters[0].translation = value; + mappingChanged(); +} +function getRotation(i) { + return mappingJson.channels[i].filters[0].rotation; +} +function setRotation(i, value) { + mappingJson.channels[i].filters[0].rotation = value; mappingChanged(); +} + +function onWebEventReceived(msg) { + if (msg.name === "init-complete") { + var values = [ + {name: "enable-filtering", val: filterEnabled ? "on" : "off", checked: false}, + {name: "left-hand-translation", val: getTranslation(LEFT_HAND_INDEX), checked: false}, + {name: "left-hand-rotation", val: getRotation(LEFT_HAND_INDEX), checked: false}, + {name: "right-hand-translation", val: getTranslation(RIGHT_HAND_INDEX), checked: false}, + {name: "right-hand-rotation", val: getRotation(RIGHT_HAND_INDEX), checked: false}, + {name: "left-foot-translation", val: getTranslation(LEFT_FOOT_INDEX), checked: false}, + {name: "left-foot-rotation", val: getRotation(LEFT_FOOT_INDEX), checked: false}, + {name: "right-foot-translation", val: getTranslation(RIGHT_FOOT_INDEX), checked: false}, + {name: "right-foot-rotation", val: getRotation(RIGHT_FOOT_INDEX), checked: false}, + {name: "hips-translation", val: getTranslation(HIPS_INDEX), checked: false}, + {name: "hips-rotation", val: getRotation(HIPS_INDEX), checked: false}, + {name: "spine2-translation", val: getTranslation(SPINE2_INDEX), checked: false}, + {name: "spine2-rotation", val: getRotation(SPINE2_INDEX), checked: false} + ]; + tablet.emitScriptEvent(JSON.stringify(values)); + } else if (msg.name === "enable-filtering") { + if (msg.val === "on") { + filterEnabled = true; + } else if (msg.val === "off") { + filterEnabled = false; + } + mappingChanged(); + } else if (msg.name === "left-hand-translation") { + setTranslation(LEFT_HAND_INDEX, Number(msg.val)); + } else if (msg.name === "left-hand-rotation") { + setRotation(LEFT_HAND_INDEX, Number(msg.val)); + } else if (msg.name === "right-hand-translation") { + setTranslation(RIGHT_HAND_INDEX, Number(msg.val)); + } else if (msg.name === "right-hand-rotation") { + setRotation(RIGHT_HAND_INDEX, Number(msg.val)); + } else if (msg.name === "left-foot-translation") { + setTranslation(LEFT_FOOT_INDEX, Number(msg.val)); + } else if (msg.name === "left-foot-rotation") { + setRotation(LEFT_FOOT_INDEX, Number(msg.val)); + } else if (msg.name === "right-foot-translation") { + setTranslation(RIGHT_FOOT_INDEX, Number(msg.val)); + } else if (msg.name === "right-foot-rotation") { + setRotation(RIGHT_FOOT_INDEX, Number(msg.val)); + } else if (msg.name === "hips-translation") { + setTranslation(HIPS_INDEX, Number(msg.val)); + } else if (msg.name === "hips-rotation") { + setRotation(HIPS_INDEX, Number(msg.val)); + } else if (msg.name === "spine2-translation") { + setTranslation(SPINE2_INDEX, Number(msg.val)); + } else if (msg.name === "spine2-rotation") { + setRotation(SPINE2_INDEX, Number(msg.val)); + } +} + +tablet.screenChanged.connect(onScreenChanged); + +function shutdownTabletApp() { + tablet.removeButton(tabletButton); + if (shown) { + tablet.webEventReceived.disconnect(onWebEventReceived); + tablet.gotoHomeScreen(); + } + tablet.screenChanged.disconnect(onScreenChanged); +} + +// +// end tablet app boiler plate +// + +var filterEnabled = true; +var mapping; +function mappingChanged() { + if (mapping) { + mapping.disable(); + } + if (filterEnabled) { + mapping = Controller.parseMapping(JSON.stringify(mappingJson)); + mapping.enable(); + } +} + +function shownChanged(newShown) { + if (newShown) { + mappingChanged(); + } else { + mapping.disable(); + } +} + +mappingChanged(); + +Script.scriptEnding.connect(function() { + if (mapping) { + mapping.disable(); + } + tablet.removeButton(tabletButton); +}); + diff --git a/scripts/developer/tests/ControlsGallery.qml b/scripts/developer/tests/ControlsGallery.qml index ceb8a26dc9..9685fa6fe8 100644 --- a/scripts/developer/tests/ControlsGallery.qml +++ b/scripts/developer/tests/ControlsGallery.qml @@ -2,16 +2,9 @@ import QtQuick 2.10 import QtQuick.Window 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit -//uncomment to use from qmlscratch tool -//import '../../../interface/resources/qml/controls-uit' as HifiControlsUit -//import '../../../interface/resources/qml/styles-uit' - -//uncomment to use with HIFI_USE_SOURCE_TREE_RESOURCES=1 -//import '../../../resources/qml/controls-uit' as HifiControlsUit -//import '../../../resources/qml/styles-uit' +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit Item { visible: true diff --git a/scripts/developer/tests/ambientSoundTest.js b/scripts/developer/tests/ambientSoundTest.js index d048d5f73d..98dbf8bba0 100644 --- a/scripts/developer/tests/ambientSoundTest.js +++ b/scripts/developer/tests/ambientSoundTest.js @@ -10,7 +10,7 @@ var uuid = Entities.addEntity({ maxVolume: 0.1, range: 25, disabled: true, - grabbableKey: { wantsTrigger: true }, + grab: { triggerable: true } }), lifetime: 600, }); diff --git a/scripts/developer/tests/controllerTableTest.js b/scripts/developer/tests/controllerTableTest.js index 239c7fd0ce..517ff0ab1a 100644 --- a/scripts/developer/tests/controllerTableTest.js +++ b/scripts/developer/tests/controllerTableTest.js @@ -1,7 +1,7 @@ "use strict"; /* jslint bitwise: true */ -/* global Script, Entities, MyAvatar, Vec3, Quat, Mat4 */ +/* global Script, Entities, MyAvatar, Vec3, Quat, Mat4, Overlays */ (function() { // BEGIN LOCAL_SCOPE @@ -19,7 +19,7 @@ var sectionCenterB = 0; var sectionCenterSign = 0; var yFlip = 0; - + var objects = []; var overlays = []; @@ -35,41 +35,41 @@ createPropsCube(index, false, false, true, true); createPropsModel(index, false, false, true, true); createSign(index, "Clone Dynamic Entity"); - }; + } function createCloneEntity(index) { createPropsCube(index, false, false, true, false); createPropsModel(index, false, false, true, false); createSign(index, "Clone Non-Dynamic Entity"); - }; + } function createNearGrabOverlay(index) { createPropsCubeOverlay(index, false, false, true, true); createPropsModelOverlay(index, false, false, true, true); createSign(index, "Near Grab Overlay"); - }; + } function createNearGrabEntity(index) { createPropsCube(index, false, false, false, false); createPropsModel(index, false, false, false, false); createSign(index, "Near Grab Entity"); - }; + } function createFarGrabEntity(index) { createPropsCube(index, true, false, false, false); createPropsModel(index, true, false, false, false); createSign(index, "Far Grab Entity"); - }; + } function createPropsModel(i, dynamic, collisionless, clone, cloneDynamic) { var propsModel = { name: "controller-tests model object " + i, type: "Model", modelURL: "http://headache.hungry.com/~seth/hifi/controller-tests/color-cube.obj", - + position: sectionCenterA, rotation: sectionRotation, - + gravity: (dynamic && !collisionless) ? { x: 0, y: -1, z: 0 } : { x: 0, y: 0, z: 0 }, dimensions: { x: 0.2, y: 0.2, z: 0.2 }, userData: JSON.stringify({ @@ -270,8 +270,8 @@ Entities.deleteEntity(nearbyID); } - for (var i = 0; i < overlays.length; i++) { - var overlayID = overlays[i]; + for (var j = 0; j < overlays.length; j++) { + var overlayID = overlays[j]; Overlays.deleteOverlay(overlayID); } }); diff --git a/scripts/developer/tests/viveTrackedObjects.js b/scripts/developer/tests/drawTrackedObjects.js similarity index 62% rename from scripts/developer/tests/viveTrackedObjects.js rename to scripts/developer/tests/drawTrackedObjects.js index 1d60f658d9..c7d886c319 100644 --- a/scripts/developer/tests/viveTrackedObjects.js +++ b/scripts/developer/tests/drawTrackedObjects.js @@ -21,16 +21,14 @@ function shutdown() { var BLUE = {x: 0, y: 0, z: 1, w: 1}; function update(dt) { - if (Controller.Hardware.Vive) { - TRACKED_OBJECT_POSES.forEach(function (key) { - var pose = Controller.getPoseValue(Controller.Standard[key]); - if (pose.valid) { - DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, BLUE); - } else { - DebugDraw.removeMyAvatarMarker(key); - } - }); - } + TRACKED_OBJECT_POSES.forEach(function (key) { + var pose = Controller.getPoseValue(Controller.Standard[key]); + if (pose.valid) { + DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, BLUE); + } else { + DebugDraw.removeMyAvatarMarker(key); + } + }); } init(); diff --git a/scripts/developer/tests/filtered-puck-attach.js b/scripts/developer/tests/filtered-puck-attach.js new file mode 100644 index 0000000000..ad9b17a0e4 --- /dev/null +++ b/scripts/developer/tests/filtered-puck-attach.js @@ -0,0 +1,438 @@ +// +// Created by Anthony J. Thibault on 2017/06/20 +// Modified by Robbie Uvanni to support multiple pucks and easier placement of pucks on entities, on 2017/08/01 +// Copyright 2017 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 +// +// When this script is running, a new app button, named "PUCKTACH", will be added to the toolbar/tablet. +// Click this app to bring up the puck attachment panel. +// + +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ +/* global Xform */ +Script.include("/~/system/libraries/Xform.js"); + +(function() { // BEGIN LOCAL_SCOPE + +var TABLET_BUTTON_NAME = "PUCKATTACH"; +var TABLET_APP_URL = "https://s3.amazonaws.com/hifi-public/tony/html/filtered-puck-attach.html?2"; +var NUM_TRACKED_OBJECTS = 16; + +var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); +var tabletButton = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: "https://s3.amazonaws.com/hifi-public/tony/icons/puck-i.svg", + activeIcon: "https://s3.amazonaws.com/hifi-public/tony/icons/puck-a.svg" +}); + +var shown = false; +function onScreenChanged(type, url) { + if (type === "Web" && url === TABLET_APP_URL) { + tabletButton.editProperties({isActive: true}); + if (!shown) { + // hook up to event bridge + tablet.webEventReceived.connect(onWebEventReceived); + shownChanged(true); + } + shown = true; + } else { + tabletButton.editProperties({isActive: false}); + if (shown) { + // disconnect from event bridge + tablet.webEventReceived.disconnect(onWebEventReceived); + shownChanged(false); + } + shown = false; + } +} +tablet.screenChanged.connect(onScreenChanged); + +function pad(num, size) { + var tempString = "000000000" + num; + return tempString.substr(tempString.length - size); +} +function indexToTrackedObjectName(index) { + return "TrackedObject" + pad(index, 2); +} +function getAvailableTrackedObjects() { + var available = []; + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + var key = indexToTrackedObjectName(i); + var pose = Controller.getPoseValue(Controller.Standard[key]); + if (pose && pose.valid) { + available.push(i); + } + } + return available; +} +function sendAvailableTrackedObjects() { + tablet.emitScriptEvent(JSON.stringify({ + pucks: getAvailableTrackedObjects(), + selectedPuck: ((lastPuck === undefined) ? -1 : lastPuck.name) + })); +} + +function getRelativePosition(origin, rotation, offset) { + var relativeOffset = Vec3.multiplyQbyV(rotation, offset); + var worldPosition = Vec3.sum(origin, relativeOffset); + return worldPosition; +} +function getPropertyForEntity(entityID, propertyName) { + return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; +} +function entityExists(entityID) { + return Object.keys(Entities.getEntityProperties(entityID)).length > 0; +} + +var VIVE_PUCK_MODEL = "https://s3.amazonaws.com/hifi-public/tony/vive_tracker_puck_y180z180.obj"; +var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model +var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 0.5; // metres +var VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE = 10.0; // metres +var VIVE_PUCK_NAME = "Tracked Puck"; + +var trackedPucks = { }; +var lastPuck; + +var DEFAULT_ROTATION_SMOOTHING_CONSTANT = 1.0; // no smoothing +var DEFAULT_TRANSLATION_SMOOTHING_CONSTANT = 1.0; // no smoothing +var DEFAULT_TRANSLATION_ACCELERATION_LIMIT = 1000; // only extreme accelerations are smoothed +var DEFAULT_ROTATION_ACCELERATION_LIMIT = 10000; // only extreme accelerations are smoothed +var DEFAULT_TRANSLATION_SNAP_THRESHOLD = 0; // no snapping +var DEFAULT_ROTATION_SNAP_THRESHOLD = 0; // no snapping + +function buildMappingJson() { + var obj = {name: "com.highfidelity.testing.filteredPuckAttach", channels: []}; + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + obj.channels.push({ + from: "Vive." + indexToTrackedObjectName(i), + to: "Standard." + indexToTrackedObjectName(i), + filters: [ + { + type: "accelerationLimiter", + translationAccelerationLimit: DEFAULT_TRANSLATION_ACCELERATION_LIMIT, + rotationAccelerationLimit: DEFAULT_ROTATION_ACCELERATION_LIMIT, + translationSnapThreshold: DEFAULT_TRANSLATION_SNAP_THRESHOLD, + rotationSnapThreshold: DEFAULT_ROTATION_SNAP_THRESHOLD, + }, + { + type: "exponentialSmoothing", + translation: DEFAULT_TRANSLATION_SMOOTHING_CONSTANT, + rotation: DEFAULT_ROTATION_SMOOTHING_CONSTANT + } + ] + }); + } + return obj; +} + +var mappingJson = buildMappingJson(); + +var mapping; +function mappingChanged() { + if (mapping) { + mapping.disable(); + } + mapping = Controller.parseMapping(JSON.stringify(mappingJson)); + mapping.enable(); +} + +function shownChanged(newShown) { + if (newShown) { + mappingChanged(); + } else { + mapping.disable(); + } +} + +function setTranslationAccelerationLimit(value) { + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + mappingJson.channels[i].filters[0].translationAccelerationLimit = value; + } + mappingChanged(); +} + +function setTranslationSnapThreshold(value) { + // convert from mm + var MM_PER_M = 1000; + var meters = value / MM_PER_M; + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + mappingJson.channels[i].filters[0].translationSnapThreshold = meters; + } + mappingChanged(); +} + +function setRotationAccelerationLimit(value) { + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + mappingJson.channels[i].filters[0].rotationAccelerationLimit = value; + } + mappingChanged(); +} + +function setRotationSnapThreshold(value) { + // convert from degrees + var PI_IN_DEGREES = 180; + var radians = value * (Math.pi / PI_IN_DEGREES); + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + mappingJson.channels[i].filters[0].translationSnapThreshold = radians; + } + mappingChanged(); +} + +function setTranslationSmoothingConstant(value) { + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + mappingJson.channels[i].filters[1].translation = value; + } + mappingChanged(); +} + +function setRotationSmoothingConstant(value) { + var i; + for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { + mappingJson.channels[i].filters[1].rotation = value; + } + mappingChanged(); +} + + +function createPuck(puck) { + // create a puck entity and add it to our list of pucks + var action = indexToTrackedObjectName(puck.puckno); + var pose = Controller.getPoseValue(Controller.Standard[action]); + + if (pose && pose.valid) { + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + + // if we've already created this puck, destroy it + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + // if we had an unfinalized puck, destroy it + if (lastPuck !== undefined) { + destroyPuck(lastPuck.name); + } + // create our new unfinalized puck + trackedPucks[puck.puckno] = { + puckEntityID: puckEntityID, + trackedEntityID: "" + }; + lastPuck = trackedPucks[puck.puckno]; + lastPuck.name = Number(puck.puckno); + } +} +function finalizePuck(puckName) { + // find nearest entity and change its parent to the puck + + if (!trackedPucks.hasOwnProperty(puckName)) { + print('2'); + return; + } + if (lastPuck === undefined) { + print('3'); + return; + } + if (lastPuck.name !== Number(puckName)) { + print('1'); + return; + } + + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); + var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); + + var foundEntity; + var leastDistance = Number.MAX_VALUE; + + for (var i = 0; i < foundEntities.length; i++) { + var entity = foundEntities[i]; + + if (getPropertyForEntity(entity, "name") !== VIVE_PUCK_NAME) { + var entityPosition = getPropertyForEntity(entity, "position"); + var d = Vec3.distance(entityPosition, puckPosition); + + if (d < leastDistance) { + leastDistance = d; + foundEntity = entity; + } + } + } + + if (foundEntity) { + lastPuck.trackedEntityID = foundEntity; + // remember the userdata and collisionless flag for the tracked entity since + // we're about to remove it and make it ungrabbable and collisionless + lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + lastPuck.trackedEntityCollisionFlag = getPropertyForEntity(foundEntity, "collisionless"); + // update properties of the tracked entity + Entities.editEntity(lastPuck.trackedEntityID, { + "parentID": lastPuck.puckEntityID, + "userData": '{ "grabbableKey": { "grabbable": false } }', + "collisionless": 1 + }); + // remove reference to puck since it is now calibrated and finalized + lastPuck = undefined; + } +} +function updatePucks() { + // for each puck, update its position and orientation + for (var puckName in trackedPucks) { + if (!trackedPucks.hasOwnProperty(puckName)) { + continue; + } + var action = indexToTrackedObjectName(puckName); + var pose = Controller.getPoseValue(Controller.Standard[action]); + if (pose && pose.valid) { + var puck = trackedPucks[puckName]; + if (puck.trackedEntityID) { + if (entityExists(puck.trackedEntityID)) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, puckXform); + + var d = Vec3.distance(MyAvatar.position, finalXform.pos); + if (d > VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE) { + print('tried to move tracked object too far away: ' + d); + return; + } + + Entities.editEntity(puck.puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID + }); + } else { + destroyPuck(puckName); + } + } + } + } +} +function destroyPuck(puckName) { + // unparent entity and delete its parent + if (!trackedPucks.hasOwnProperty(puckName)) { + return; + } + + var puck = trackedPucks[puckName]; + var puckEntityID = puck.puckEntityID; + var trackedEntityID = puck.trackedEntityID; + + // remove the puck as a parent entity and restore the tracked entities + // former userdata and collision flag + Entities.editEntity(trackedEntityID, { + "parentID": "{00000000-0000-0000-0000-000000000000}", + "userData": puck.trackedEntityUserData, + "collisionless": puck.trackedEntityCollisionFlag + }); + + delete trackedPucks[puckName]; + + // in some cases, the entity deletion may occur before the parent change + // has been processed, resulting in both the puck and the tracked entity + // to be deleted so we wait 100ms before deleting the puck, assuming + // that the parent change has occured + var DELETE_TIMEOUT = 100; // ms + Script.setTimeout(function() { + // delete the puck + Entities.deleteEntity(puckEntityID); + }, DELETE_TIMEOUT); +} +function destroyPucks() { + // remove all pucks and unparent entities + for (var puckName in trackedPucks) { + if (trackedPucks.hasOwnProperty(puckName)) { + destroyPuck(puckName); + } + } +} + +function onWebEventReceived(msg) { + var obj = {}; + + try { + obj = JSON.parse(msg); + } catch (err) { + return; + } + + switch (obj.cmd) { + case "ready": + sendAvailableTrackedObjects(); + break; + case "create": + createPuck(obj); + break; + case "finalize": + finalizePuck(obj.puckno); + break; + case "destroy": + destroyPuck(obj.puckno); + break; + case "translation-acceleration-limit": + setTranslationAccelerationLimit(Number(obj.val)); + break; + case "translation-snap-threshold": + setTranslationSnapThreshold(Number(obj.val)); + break; + case "rotation-acceleration-limit": + setRotationAccelerationLimit(Number(obj.val)); + break; + case "rotation-snap-threshold": + setRotationSnapThreshold(Number(obj.val)); + break; + case "translation-smoothing-constant": + setTranslationSmoothingConstant(Number(obj.val)); + break; + case "rotation-smoothing-constant": + setRotationSmoothingConstant(Number(obj.val)); + break; + } +} + +Script.update.connect(updatePucks); +Script.scriptEnding.connect(function () { + tablet.removeButton(tabletButton); + destroyPucks(); + if (shown) { + tablet.webEventReceived.disconnect(onWebEventReceived); + tablet.gotoHomeScreen(); + } + tablet.screenChanged.disconnect(onScreenChanged); + if (mapping) { + mapping.disable(); + } +}); +tabletButton.clicked.connect(function () { + if (shown) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(TABLET_APP_URL); + } +}); +}()); // END LOCAL_SCOPE diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 04d5db5710..019a911535 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -85,7 +85,7 @@ function entityExists(entityID) { return Object.keys(Entities.getEntityProperties(entityID)).length > 0; } -var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_MODEL = "https://s3.amazonaws.com/hifi-public/tony/vive_tracker_puck_y180z180.obj"; var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres var VIVE_PUCK_SPAWN_DISTANCE = 0.5; // metres @@ -304,4 +304,4 @@ tabletButton.clicked.connect(function () { tablet.gotoWebScreen(TABLET_APP_URL); } }); -}()); // END LOCAL_SCOPE \ No newline at end of file +}()); // END LOCAL_SCOPE diff --git a/scripts/developer/utilities/audio/Stats.qml b/scripts/developer/utilities/audio/Stats.qml index f359e9b04c..e2291e485d 100644 --- a/scripts/developer/utilities/audio/Stats.qml +++ b/scripts/developer/utilities/audio/Stats.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:////qml//controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls Column { id: stats diff --git a/scripts/developer/utilities/audio/TabletStats.qml b/scripts/developer/utilities/audio/TabletStats.qml index 2f8d212a2a..b50acabec4 100644 --- a/scripts/developer/utilities/audio/TabletStats.qml +++ b/scripts/developer/utilities/audio/TabletStats.qml @@ -11,8 +11,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 - -import "qrc:////qml//styles-uit" +import stylesUit 1.0 Item { id: dialog diff --git a/scripts/developer/utilities/lib/jet/qml/TaskList.qml b/scripts/developer/utilities/lib/jet/qml/TaskList.qml index 5b1aa0afb5..166f604666 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskList.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskList.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../jet.js" as Jet diff --git a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml index 2c75865698..0f083aa72c 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../jet.js" as Jet diff --git a/scripts/developer/utilities/lib/plotperf/Color.qml b/scripts/developer/utilities/lib/plotperf/Color.qml index 15d7f9fcc9..1ad72fe2e6 100644 --- a/scripts/developer/utilities/lib/plotperf/Color.qml +++ b/scripts/developer/utilities/lib/plotperf/Color.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { diff --git a/scripts/developer/utilities/render/antialiasing.qml b/scripts/developer/utilities/render/antialiasing.qml index 1a8f9dac2d..5abfd30935 100644 --- a/scripts/developer/utilities/render/antialiasing.qml +++ b/scripts/developer/utilities/render/antialiasing.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/plotperf" diff --git a/scripts/developer/utilities/render/configSlider/ConfigSlider.qml b/scripts/developer/utilities/render/configSlider/ConfigSlider.qml index 41de77fb09..bf9089d82c 100644 --- a/scripts/developer/utilities/render/configSlider/ConfigSlider.qml +++ b/scripts/developer/utilities/render/configSlider/ConfigSlider.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { diff --git a/scripts/developer/utilities/render/configSlider/RichSlider.qml b/scripts/developer/utilities/render/configSlider/RichSlider.qml index 01b14f3d48..ff16cb32ad 100644 --- a/scripts/developer/utilities/render/configSlider/RichSlider.qml +++ b/scripts/developer/utilities/render/configSlider/RichSlider.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index a9479b2935..4bc4941358 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/jet/qml" as Jet @@ -279,11 +279,27 @@ Rectangle { } } Separator {} - HifiControls.Button { - text: "Engine" - // activeFocusOnPress: false - onClicked: { - sendToScript({method: "openEngineView"}); + Row { + HifiControls.Button { + text: "Engine" + // activeFocusOnPress: false + onClicked: { + sendToScript({method: "openEngineView"}); + } + } + HifiControls.Button { + text: "LOD" + // activeFocusOnPress: false + onClicked: { + sendToScript({method: "openEngineLODView"}); + } + } + HifiControls.Button { + text: "Cull" + // activeFocusOnPress: false + onClicked: { + sendToScript({method: "openCullInspectorView"}); + } } } } diff --git a/scripts/developer/utilities/render/engineInspector.qml b/scripts/developer/utilities/render/engineInspector.qml index 1b9941e64e..16dd8eb985 100644 --- a/scripts/developer/utilities/render/engineInspector.qml +++ b/scripts/developer/utilities/render/engineInspector.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../lib/jet/qml" as Jet diff --git a/scripts/developer/utilities/render/highlight.qml b/scripts/developer/utilities/render/highlight.qml index 88d6a807ae..d8af2a828e 100644 --- a/scripts/developer/utilities/render/highlight.qml +++ b/scripts/developer/utilities/render/highlight.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/plotperf" import "highlight" diff --git a/scripts/developer/utilities/render/highlight/HighlightStyle.qml b/scripts/developer/utilities/render/highlight/HighlightStyle.qml index 371b7e81f7..475aadfdce 100644 --- a/scripts/developer/utilities/render/highlight/HighlightStyle.qml +++ b/scripts/developer/utilities/render/highlight/HighlightStyle.qml @@ -12,8 +12,8 @@ import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import "../configSlider" import "../../lib/plotperf" -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { id: root diff --git a/scripts/developer/utilities/render/lod.qml b/scripts/developer/utilities/render/lod.qml index 889d8db836..892b43d8be 100644 --- a/scripts/developer/utilities/render/lod.qml +++ b/scripts/developer/utilities/render/lod.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../lib/plotperf" import "configSlider" diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index cb5b01f9b2..cffeb615c9 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -11,122 +11,131 @@ // (function() { - var TABLET_BUTTON_NAME = "LUCI"; - var QMLAPP_URL = Script.resolvePath("./deferredLighting.qml"); - var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg"); - var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg"); - - - var onLuciScreen = false; - - function onClicked() { - if (onLuciScreen) { - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(QMLAPP_URL); - } - } - - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - text: TABLET_BUTTON_NAME, - icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL - }); - - var hasEventBridge = false; - - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; - } - } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; - } - } - } - - function onScreenChanged(type, url) { - if (url === QMLAPP_URL) { - onLuciScreen = true; - } else { - onLuciScreen = false; - } - - button.editProperties({isActive: onLuciScreen}); - wireEventBridge(onLuciScreen); - } - - button.clicked.connect(onClicked); - tablet.screenChanged.connect(onScreenChanged); + var AppUi = Script.require('appUi'); var moveDebugCursor = false; - Controller.mousePressEvent.connect(function (e) { + var onMousePressEvent = function (e) { if (e.isMiddleButton) { moveDebugCursor = true; setDebugCursor(e.x, e.y); } - }); - Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); - Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); + }; + Controller.mousePressEvent.connect(onMousePressEvent); + var onMouseReleaseEvent = function () { + moveDebugCursor = false; + }; + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + var onMouseMoveEvent = function (e) { + if (moveDebugCursor) { + setDebugCursor(e.x, e.y); + } + }; + Controller.mouseMoveEvent.connect(onMouseMoveEvent); function setDebugCursor(x, y) { - nx = 2.0 * (x / Window.innerWidth) - 1.0; - ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); + var nx = 2.0 * (x / Window.innerWidth) - 1.0; + var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); - Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; + Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; } + function Page(title, qmlurl, width, height) { + this.title = title; + this.qml = qmlurl; + this.width = width; + this.height = height; + + this.window; + + print("Page: New Page:" + JSON.stringify(this)); + } + + Page.prototype.killView = function () { + print("Page: Kill window for page:" + JSON.stringify(this)); + if (this.window) { + print("Page: Kill window for page:" + this.title); + //this.window.closed.disconnect(function () { + // this.killView(); + //}); + this.window.close(); + this.window = false; + } + }; + + Page.prototype.createView = function () { + var that = this; + if (!this.window) { + print("Page: New window for page:" + this.title); + this.window = Desktop.createWindow(Script.resolvePath(this.qml), { + title: this.title, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: this.width, y: this.height} + }); + this.window.closed.connect(function () { + that.killView(); + }); + } + }; + + + var Pages = function () { + this._pages = {}; + }; + + Pages.prototype.addPage = function (command, title, qmlurl, width, height) { + this._pages[command] = new Page(title, qmlurl, width, height); + }; + + Pages.prototype.open = function (command) { + print("Pages: command = " + command); + if (!this._pages[command]) { + print("Pages: unknown command = " + command); + return; + } + this._pages[command].createView(); + }; + + Pages.prototype.clear = function () { + for (var p in this._pages) { + print("Pages: kill page: " + p); + this._pages[p].killView(); + delete this._pages[p]; + } + this._pages = {}; + }; + var pages = new Pages(); + + pages.addPage('openEngineView', 'Render Engine', 'engineInspector.qml', 300, 400); + pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400); + pages.addPage('openCullInspectorView', 'Cull Inspector', 'culling.qml', 300, 400); function fromQml(message) { - switch (message.method) { - case "openEngineView": - openEngineTaskView(); - break; - } + if (pages.open(message.method)) { + return; + } } - - var engineInspectorView = null - function openEngineTaskView() { - if (engineInspectorView == null) { - var qml = Script.resolvePath('engineInspector.qml'); - var window = new OverlayWindow({ - title: 'Render Engine', - source: qml, - width: 300, - height: 400 - }); - window.setPosition(200, 50); - engineInspectorView = window - window.closed.connect(function() { engineInspectorView = null; }); - } else { - engineInspectorView.setPosition(200, 50); - } + var ui; + function startup() { + ui = new AppUi({ + buttonName: "LUCI", + home: Script.resolvePath("deferredLighting.qml"), + additionalAppScreens: Script.resolvePath("engineInspector.qml"), + onMessage: fromQml, + normalButton: Script.resolvePath("../../../system/assets/images/luci-i.svg"), + activeButton: Script.resolvePath("../../../system/assets/images/luci-a.svg") + }); } - - - + startup(); Script.scriptEnding.connect(function () { - if (onLuciScreen) { - tablet.gotoHomeScreen(); - } - button.clicked.disconnect(onClicked); - tablet.screenChanged.disconnect(onScreenChanged); - tablet.removeButton(button); - - if (engineInspectorView !== null) { - engineInspectorView.close() - } + Controller.mousePressEvent.disconnect(onMousePressEvent); + Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); + Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); + pages.clear(); + // killEngineInspectorView(); + // killCullInspectorView(); + // killEngineLODWindow(); }); -}()); \ No newline at end of file +}()); diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 464fe00eb9..a1d6777a68 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" diff --git a/scripts/developer/utilities/render/transition.qml b/scripts/developer/utilities/render/transition.qml index f74468a273..c150c523f9 100644 --- a/scripts/developer/utilities/render/transition.qml +++ b/scripts/developer/utilities/render/transition.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.0 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/plotperf" diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index 2eaa9d8133..746a572f29 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../render/configSlider" import "../lib/jet/qml" as Jet import "../lib/plotperf" diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index efb842a9bb..e267293977 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -95,16 +95,16 @@ function AppUi(properties) { activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton }); }; - that.notificationPollTimeout = false; - that.notificationPollTimeoutMs = 60000; - that.notificationPollEndpoint = false; - that.notificationPollStopPaginatingConditionMet = false; + that.notificationPollTimeout = [false]; + that.notificationPollTimeoutMs = [60000]; + that.notificationPollEndpoint = [false]; + that.notificationPollStopPaginatingConditionMet = [false]; that.notificationDataProcessPage = function (data) { return data; }; - that.notificationPollCallback = that.ignore; - that.notificationPollCaresAboutSince = false; - that.notificationInitialCallbackMade = false; + that.notificationPollCallback = [that.ignore]; + that.notificationPollCaresAboutSince = [false]; + that.notificationInitialCallbackMade = [false]; that.notificationDisplayBanner = function (message) { if (!that.isOpen) { Window.displayAnnouncement(message); @@ -129,7 +129,9 @@ function AppUi(properties) { } that.isOpen = true; } - } else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden? + } else { + // A different screen is now visible, or the tablet has been closed. + // Tablet visibility is controlled separately by `tabletShownChanged()` that.wireEventBridge(false); if (that.isOpen) { that.buttonActive(false); @@ -139,83 +141,124 @@ function AppUi(properties) { that.isOpen = false; } } - // console.debug(that.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type + - // "\nNew screen URL: " + url + "\nCurrent app open status: " + that.isOpen + "\n"); }; // Overwrite with the given properties: - Object.keys(properties).forEach(function (key) { that[key] = properties[key]; }); + Object.keys(properties).forEach(function (key) { + that[key] = properties[key]; + }); // // START Notification Handling // + + var currentDataPageToRetrieve = []; + var concatenatedServerResponse = []; + for (var i = 0; i < that.notificationPollEndpoint.length; i++) { + currentDataPageToRetrieve[i] = 1; + concatenatedServerResponse[i] = new Array(); + } + + var MAX_LOG_LENGTH_CHARACTERS = 300; + function requestCallback(error, response, optionalParams) { + var indexOfRequest = optionalParams.indexOfRequest; + var urlOfRequest = optionalParams.urlOfRequest; + + if (error || (response.status !== 'success')) { + print("Error: unable to get", urlOfRequest, error || response.status); + startNotificationTimer(indexOfRequest); + return; + } + + if (!that.notificationPollStopPaginatingConditionMet[indexOfRequest] || + that.notificationPollStopPaginatingConditionMet[indexOfRequest](response)) { + startNotificationTimer(indexOfRequest); + + var notificationData; + if (concatenatedServerResponse[indexOfRequest].length) { + notificationData = concatenatedServerResponse[indexOfRequest]; + } else { + notificationData = that.notificationDataProcessPage[indexOfRequest](response); + } + console.debug(that.buttonName, that.notificationPollEndpoint[indexOfRequest], + 'truncated notification data for processing:', + JSON.stringify(notificationData).substring(0, MAX_LOG_LENGTH_CHARACTERS)); + that.notificationPollCallback[indexOfRequest](notificationData); + that.notificationInitialCallbackMade[indexOfRequest] = true; + currentDataPageToRetrieve[indexOfRequest] = 1; + concatenatedServerResponse[indexOfRequest] = new Array(); + } else { + concatenatedServerResponse[indexOfRequest] = + concatenatedServerResponse[indexOfRequest].concat(that.notificationDataProcessPage[indexOfRequest](response)); + currentDataPageToRetrieve[indexOfRequest]++; + request({ + json: true, + uri: (urlOfRequest + "&page=" + currentDataPageToRetrieve[indexOfRequest]) + }, requestCallback, optionalParams); + } + } + + var METAVERSE_BASE = Account.metaverseServerURL; - var currentDataPageToRetrieve = 1; - var concatenatedServerResponse = new Array(); - that.notificationPoll = function () { - if (!that.notificationPollEndpoint) { + var MS_IN_SEC = 1000; + that.notificationPoll = function (i) { + if (!that.notificationPollEndpoint[i]) { return; } - // User is "appearing offline" or is offline - if (GlobalServices.findableBy === "none" || Account.username === "") { - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); + // User is "appearing offline" or is not logged in + if (GlobalServices.findableBy === "none" || Account.username === "Unknown user") { + // The notification polling will restart when the user changes their availability + // or when they log in, so it's not necessary to restart a timer here. + console.debug(that.buttonName + " Notifications: User is appearing offline or not logged in. " + + that.buttonName + " will poll for notifications when user logs in and has their availability " + + "set to not appear offline."); return; } - var url = METAVERSE_BASE + that.notificationPollEndpoint; + var url = METAVERSE_BASE + that.notificationPollEndpoint[i]; - var settingsKey = "notifications/" + that.buttonName + "/lastPoll"; + var settingsKey = "notifications/" + that.notificationPollEndpoint[i] + "/lastPoll"; var currentTimestamp = new Date().getTime(); var lastPollTimestamp = Settings.getValue(settingsKey, currentTimestamp); - if (that.notificationPollCaresAboutSince) { - url = url + "&since=" + lastPollTimestamp/1000; + if (that.notificationPollCaresAboutSince[i]) { + url = url + "&since=" + lastPollTimestamp / MS_IN_SEC; } Settings.setValue(settingsKey, currentTimestamp); console.debug(that.buttonName, 'polling for notifications at endpoint', url); - function requestCallback(error, response) { - if (error || (response.status !== 'success')) { - print("Error: unable to get", url, error || response.status); - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); - return; - } - - if (!that.notificationPollStopPaginatingConditionMet || that.notificationPollStopPaginatingConditionMet(response)) { - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); - - var notificationData; - if (concatenatedServerResponse.length) { - notificationData = concatenatedServerResponse; - } else { - notificationData = that.notificationDataProcessPage(response); - } - console.debug(that.buttonName, 'notification data for processing:', JSON.stringify(notificationData)); - that.notificationPollCallback(notificationData); - that.notificationInitialCallbackMade = true; - currentDataPageToRetrieve = 1; - concatenatedServerResponse = new Array(); - } else { - concatenatedServerResponse = concatenatedServerResponse.concat(that.notificationDataProcessPage(response)); - currentDataPageToRetrieve++; - request({ json: true, uri: (url + "&page=" + currentDataPageToRetrieve) }, requestCallback); - } - } - - request({ json: true, uri: url }, requestCallback); + request({ + json: true, + uri: url + }, + requestCallback, + { + indexOfRequest: i, + urlOfRequest: url + }); }; // This won't do anything if there isn't a notification endpoint set - that.notificationPoll(); + for (i = 0; i < that.notificationPollEndpoint.length; i++) { + that.notificationPoll(i); + } + + function startNotificationTimer(indexOfRequest) { + that.notificationPollTimeout[indexOfRequest] = Script.setTimeout(function () { + that.notificationPoll(indexOfRequest); + }, that.notificationPollTimeoutMs[indexOfRequest]); + } function restartNotificationPoll() { - that.notificationInitialCallbackMade = false; - if (that.notificationPollTimeout) { - Script.clearTimeout(that.notificationPollTimeout); - that.notificationPollTimeout = false; + for (var j = 0; j < that.notificationPollEndpoint.length; j++) { + that.notificationInitialCallbackMade[j] = false; + if (that.notificationPollTimeout[j]) { + Script.clearTimeout(that.notificationPollTimeout[j]); + that.notificationPollTimeout[j] = false; + } + that.notificationPoll(j); } - that.notificationPoll(); } // // END Notification Handling @@ -322,9 +365,11 @@ function AppUi(properties) { } that.tablet.removeButton(that.button); } - if (that.notificationPollTimeout) { - Script.clearInterval(that.notificationPollTimeout); - that.notificationPollTimeout = false; + for (var i = 0; i < that.notificationPollTimeout.length; i++) { + if (that.notificationPollTimeout[i]) { + Script.clearInterval(that.notificationPollTimeout[i]); + that.notificationPollTimeout[i] = false; + } } }; // Set up the handlers. @@ -333,7 +378,7 @@ function AppUi(properties) { Script.scriptEnding.connect(that.onScriptEnding); GlobalServices.findableByChanged.connect(restartNotificationPoll); GlobalServices.myUsernameChanged.connect(restartNotificationPoll); - if (that.buttonName == Settings.getValue("startUpApp")) { + if (that.buttonName === Settings.getValue("startUpApp")) { Settings.setValue("startUpApp", ""); Script.setTimeout(function () { that.open(); diff --git a/scripts/modules/request.js b/scripts/modules/request.js index d0037f9b43..37f3ac0d7b 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -18,7 +18,8 @@ module.exports = { // ------------------------------------------------------------------ - request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. + // cb(error, responseOfCorrectContentType, optionalCallbackParameter) of url. A subset of npm request. + request: function (options, callback, optionalCallbackParameter) { var httpRequest = new XMLHttpRequest(), key; // QT bug: apparently doesn't handle onload. Workaround using readyState. httpRequest.onreadystatechange = function () { @@ -38,7 +39,7 @@ module.exports = { if (error) { response = { statusCode: httpRequest.status }; } - callback(error, response); + callback(error, response, optionalCallbackParameter); } }; if (typeof options === 'string') { diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json new file mode 100644 index 0000000000..720d4537ee --- /dev/null +++ b/scripts/system/assets/data/createAppTooltips.json @@ -0,0 +1,485 @@ +{ + "shape": { + "tooltip": "The shape of this entity's geometry." + }, + "color": { + "tooltip": "The RGB value of this entity." + }, + "text": { + "tooltip": "The text to display on the entity." + }, + "textColor": { + "tooltip": "The color of the text." + }, + "backgroundColor": { + "tooltip": "The color of the background." + }, + "lineHeight": { + "tooltip": "The height of each line of text. This determines the size of the text." + }, + "faceCamera": { + "tooltip": "If enabled, the entity follows the camera of each user, creating a billboard effect." + }, + "flyingAllowed": { + "tooltip": "If enabled, users can fly in the zone." + }, + "ghostingAllowed": { + "tooltip": "If enabled, users with avatar collisions turned off will not collide with content in the zone." + }, + "filterURL": { + "tooltip": "The URL of a JS file that checks for changes to entity properties within the zone. Runs periodically." + }, + "keyLightMode": { + "tooltip": "Configures the key light in the zone. This light is directional." + }, + "keyLight.color": { + "tooltip": "The color of the key light." + }, + "keyLight.intensity": { + "tooltip": "The intensity of the key light." + }, + "keyLight.direction.y": { + "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its y axis." + }, + "keyLight.direction.x": { + "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its x axis." + }, + "keyLight.castShadows": { + "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled." + }, + "skyboxMode": { + "tooltip": "Configures the skybox in the zone. The skybox is a cube map image." + }, + "skybox.color": { + "tooltip": "If the URL is blank, this changes the color of the sky, otherwise it modifies the color of the skybox." + }, + "skybox.url": { + "tooltip": "A cube map image that is used to render the sky." + }, + "ambientLightMode": { + "tooltip": "Configures the ambient light in the zone. Use this if you want your skybox to reflect light on the content." + }, + "ambientLight.ambientIntensity": { + "tooltip": "The intensity of the ambient light." + }, + "ambientLight.ambientURL": { + "tooltip": "A cube map image that defines the color of the light coming from each direction." + }, + "hazeMode": { + "tooltip": "Configures the haze in the scene." + }, + "haze.hazeRange": { + "tooltip": "How far the haze extends out. This is measured in meters." + }, + "haze.hazeAltitudeEffect": { + "tooltip": "If enabled, this adjusts the haze intensity as it gets higher." + }, + "haze.hazeBaseRef": { + "tooltip": "The base of the altitude range. Measured in entity space." + }, + "haze.hazeCeiling": { + "tooltip": "The ceiling of the altitude range. Measured in entity space." + }, + "haze.hazeColor": { + "tooltip": "The color of the haze." + }, + "haze.hazeBackgroundBlend": { + "tooltip": "How much the skybox shows through the haze. The higher the value, the more it shows through." + }, + "haze.hazeEnableGlare": { + "tooltip": "If enabled, a glare is enabled on the skybox, based on the key light." + }, + "haze.hazeGlareColor": { + "tooltip": "The color of the glare based on the key light." + }, + "haze.hazeGlareAngle": { + "tooltip": "The angular size of the glare and how much it encompasses the skybox, based on the key light." + }, + "bloomMode": { + "tooltip": "Configures how much bright areas of the scene glow." + }, + "bloom.bloomIntensity": { + "tooltip": "The intensity, or brightness, of the bloom effect." + }, + "bloom.bloomThreshold": { + "tooltip": "The cutoff of the bloom. The higher the value, the more only bright areas of the scene will glow." + }, + "bloom.bloomSize": { + "tooltip": "The radius of bloom. The higher the value, the larger the bloom." + }, + "modelURL": { + "tooltip": "A mesh model from an FBX or OBJ file." + }, + "shapeType": { + "tooltip": "The shape of the collision hull used if collisions are enabled. This affects how an entity collides." + }, + "compoundShapeURL": { + "tooltip": "The OBJ file to use for the compound shape if Collision Shape is \"compound\"." + }, + "animation.url": { + "tooltip": "An animation to play on the model." + }, + "animation.running": { + "tooltip": "If enabled, the animation on the model will play automatically." + }, + "animation.allowTranslation": { + "tooltip": "If enabled, this allows an entity to move in space during an animation." + }, + "animation.loop": { + "tooltip": "If enabled, then the animation will continuously repeat." + }, + "animation.hold": { + "tooltip": "If enabled, then rotations and translations of the last frame played are maintained when the animation stops." + }, + "animation.currentFrame": { + "tooltip": "The current frame being played in the animation." + }, + "animation.firstFrame": { + "tooltip": "The first frame to play in the animation." + }, + "animation.lastFrame": { + "tooltip": "The last frame to play in the animation." + }, + "animation.fps": { + "tooltip": "The speed of the animation." + }, + "textures": { + "tooltip": "A JSON string containing a texture. Use a name from the Original Texture property to override it." + }, + "originalTextures": { + "tooltip": "A JSON string containing the original texture used on the model." + }, + "image": { + "tooltip": "The URL for the image source.", + "jsPropertyName": "textures" + }, + "sourceUrl": { + "tooltip": "The URL for the web page source." + }, + "dpi": { + "tooltip": "The resolution to display the page at, in pixels per inch. Use this to resize your web source in the frame." + }, + "isEmitting": { + "tooltip": "If enabled, then particles are emitted." + }, + "lifespan": { + "tooltip": "How long each particle lives, measured in seconds." + }, + "maxParticles": { + "tooltip": "The maximum number of particles to render at one time. Older particles are swapped out for new ones." + }, + "particleTextures": { + "tooltip": "The URL of a JPG or PNG image file to display for each particle.", + "jsPropertyName": "textures" + }, + "emitRate": { + "tooltip": "The number of particles per second to emit." + }, + "emitSpeed": { + "tooltip": "The speed that each particle is emitted at, measured in m/s." + }, + "speedSpread": { + "tooltip": "The spread in speeds at which particles are emitted at, resulting in a variety of speeds." + }, + "emitDimensions": { + "tooltip": "The outer limit radius in dimensions that the particles can be emitted from." + }, + "emitOrientation": { + "tooltip": "The orientation of particle emission relative to the entity's axes." + }, + "emitRadiusStart": { + "tooltip": "The inner limit radius in dimensions that the particles start emitting from." + }, + "emitterShouldTrail": { + "tooltip": "If enabled, then particles are \"left behind\" as the emitter moves, otherwise they are not." + }, + "particleRadius": { + "tooltip": "The size of each particle." + }, + "radiusSpread": { + "tooltip": "The spread in size that each particle is given, resulting in a variety of sizes." + }, + "particleColor": { + "tooltip": "The color of each particle.", + "jsPropertyName": "color" + }, + "colorSpread": { + "tooltip": "The spread in color that each particle is given, resulting in a variety of colors." + }, + "alpha": { + "tooltip": "The alpha of each particle." + }, + "alphaSpread": { + "tooltip": "The spread in alpha that each particle is given, resulting in a variety of alphas." + }, + "emitAcceleration": { + "tooltip": "The acceleration that is applied to each particle during its lifetime." + }, + "accelerationSpread": { + "tooltip": "The spread in accelerations that each particle is given, resulting in a variety of accelerations." + }, + "particleSpin": { + "tooltip": "The spin of each particle in the system." + }, + "spinSpread": { + "tooltip": "The spread in spin that each particle is given, resulting in a variety of spins." + }, + "rotateWithEntity": { + "tooltip": "If enabled, each particle will spin relative to the roation of the entity as a whole." + }, + "polarStart": { + "tooltip": "The angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." + }, + "azimuthStart": { + "tooltip": "The angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." + }, + "lightColor": { + "tooltip": "The color of the light emitted.", + "jsPropertyName": "color" + }, + "intensity": { + "tooltip": "The brightness of the light." + }, + "falloffRadius": { + "tooltip": "The distance from the light's center where the intensity is reduced." + }, + "isSpotlight": { + "tooltip": "If enabled, then the light is directional, otherwise the light is a point light which emits light in all directions." + }, + "exponent": { + "tooltip": "Affects the softness of the spotlight beam; the higher the value, the softer the beam." + }, + "cutoff": { + "tooltip": "Affects the size of the spotlight beam; the higher the value, the larger the beam." + }, + "materialURL": { + "tooltip": "The URL to an external JSON file or \"materialData\", \"materialData? to use Material Data." + }, + "materialData": { + "tooltip": "Can be used instead of a JSON file when material set to materialData." + }, + "materialNameToReplace": { + "tooltip": "Material name of parent entity to map this material entity on.", + "jsPropertyName": "parentMaterialName" + }, + "submeshToReplace": { + "tooltip": "Submesh index of the parent entity to map this material on.", + "jsPropertyName": "parentMaterialName" + }, + "selectSubmesh": { + "tooltip": "If enabled, \"Select Submesh\" property will show up, otherwise \"Material Name to Replace\" will be shown.", + "skipJSProperty": true + }, + "priority": { + "tooltip": "The priority of the material, where a larger number means higher priority. Original materials = 0." + }, + "materialMappingPos": { + "tooltip": "The offset position of the bottom left of the material within the parent's UV space." + }, + "materialMappingScale": { + "tooltip": "How many times the material will repeat in each direction within the parent's UV space." + }, + "materialMappingRot": { + "tooltip": "How much to rotate the material within the parent's UV-space, in degrees." + }, + "id": { + "tooltip": "The unique identifier of this entity." + }, + "name": { + "tooltip": "The name of this entity." + }, + "description": { + "tooltip": "Use this field to describe the entity." + }, + "position": { + "tooltip": "The global position of this entity." + }, + "localPosition": { + "tooltip": "The local position of this entity." + }, + "rotation": { + "tooltip": "The global rotation of this entity." + }, + "localRotation": { + "tooltip": "The local rotation of this entity." + }, + "dimensions": { + "tooltip": "The global dimensions of this entity." + }, + "localDimensions": { + "tooltip": "The local dimensions of this entity." + }, + "scale": { + "tooltip": "The global scaling of this entity.", + "skipJSProperty": true + }, + "registrationPoint": { + "tooltip": "The point in the entity at which the entity is rotated about." + }, + "visible": { + "tooltip": "If enabled, this entity will be visible." + }, + "locked": { + "tooltip": "If enabled, this entity will be locked." + }, + "collisionless": { + "tooltip": "If enabled, this entity will collide with other entities or avatars." + }, + "dynamic": { + "tooltip": "If enabled, this entity has collisions associated with it that can affect its movement." + }, + "collidesWithStatic": { + "tooltip": "If enabled, this entity will collide with other non-moving, static entities.", + "jsPropertyName": "collidesWith" + }, + "collidesWithDynamic": { + "tooltip": "If enabled, this entity will collide with other dynamic entities.", + "jsPropertyName": "collidesWith" + }, + "collidesWithKinematic": { + "tooltip": "If enabled, this entity will collide with other kinematic entities (they have velocity but are not dynamic).", + "jsPropertyName": "collidesWith" + }, + "collidesWithOtherAvatar": { + "tooltip": "If enabled, this entity will collide with other user's avatars.", + "jsPropertyName": "collidesWith" + }, + "collidesWithMyAvatar": { + "tooltip": "If enabled, this entity will collide with your own avatar.", + "jsPropertyName": "collidesWith" + }, + "collisionSoundURL": { + "tooltip": "The URL of a sound to play when the entity collides with something else." + }, + "grab.grabbable": { + "tooltip": "If enabled, this entity will allow grabbing input and will be moveable." + }, + "grab.triggerable": { + "tooltip": "If enabled, the collider on this entity is used for triggering events." + }, + "cloneable": { + "tooltip": "If enabled, this entity can be duplicated." + }, + "cloneLifetime": { + "tooltip": "The lifetime for clones of this entity." + }, + "cloneLimit": { + "tooltip": "The total number of clones of this entity that can exist in the domain at any given time." + }, + "cloneDynamic": { + "tooltip": "If enabled, then clones created from this entity will be dynamic, allowing the clone to collide." + }, + "cloneAvatarEntity": { + "tooltip": "If enabled, then clones created from this entity will be created as avatar entities." + }, + "grab.grabFollowsController": { + "tooltip": "If enabled, grabbed entities will follow the movements of your hand controller instead of your avatar's hand." + }, + "canCastShadow": { + "tooltip": "If enabled, this geometry of this entity casts shadows when a shadow-casting light source shines on it." + }, + "parentID": { + "tooltip": "The ID of the entity or avatar that this entity is parented to." + }, + "parentJointIndex": { + "tooltip": "If the entity is parented to an avatar, this joint defines where on the avatar the entity is parented." + }, + "href": { + "tooltip": "The URL that will be opened when a user clicks on this entity. Useful for web pages and portals." + }, + "script": { + "tooltip": "The URL to an external JS file to add behaviors to the client." + }, + "serverScripts": { + "tooltip": "The URL to an external JS file to add behaviors to the server." + }, + "serverScriptsStatus": { + "tooltip": "The status of the server script, if provided. This shows if it's running or has an error.", + "skipJSProperty": true + }, + "hasLifetime": { + "tooltip": "If enabled, the entity will disappear after a certain amount of time specified by Lifetime.", + "jsPropertyName": "lifetime" + }, + "lifetime": { + "tooltip": "The time this entity will exist in the environment for." + }, + "userData": { + "tooltip": "Used to store extra data about the entity in JSON format." + }, + "localVelocity": { + "tooltip": "The linear velocity vector of the entity. The velocity at which this entity moves forward in space." + }, + "damping": { + "tooltip": "The linear damping to slow down the linear velocity of an entity over time." + }, + "localAngularVelocity": { + "tooltip": "The angular velocity of the entity in rad/s with respect to its axes, about its pivot point." + }, + "angularDamping": { + "tooltip": "The angular damping to slow down the angular velocity of an entity over time." + }, + "restitution": { + "tooltip": "If enabled, the entity can bounce against other objects that also have Bounciness." + }, + "friction": { + "tooltip": "The friction applied to slow down an entity when it's moving against another entity." + }, + "density": { + "tooltip": "The density of the entity. The higher the density, the harder the entity is to move." + }, + "gravity": { + "tooltip": "The acceleration due to gravity that the entity should move with, in world space." + }, + "acceleration": { + "tooltip": "A acceleration that the entity should move with, in world space." + }, + "alignToGrid": { + "tooltip": "Used to align entities to the grid, or floor of the environment.", + "skipJSProperty": true + }, + "createModel": { + "tooltip": "An entity that is based on a custom mesh created from an .OBJ or .FBX.", + "skipJSProperty": true + }, + "createShape": { + "tooltip": "An entity that has many different primitive shapes.", + "skipJSProperty": true + }, + "createLight": { + "tooltip": "An entity that emits light.", + "skipJSProperty": true + }, + "createText": { + "tooltip": "An entity that displays text on a panel.", + "skipJSProperty": true + }, + "createImage": { + "tooltip": "An entity that displays an image on a panel.", + "skipJSProperty": true + }, + "createWeb": { + "tooltip": "An entity that displays a web page on a panel.", + "skipJSProperty": true + }, + "createZone": { + "tooltip": "An entity that can be used for skyboxes, lighting, and can constrain or change avatar behaviors.", + "skipJSProperty": true + }, + "createParticle": { + "tooltip": "An entity that emits particles.", + "skipJSProperty": true + }, + "createMaterial": { + "tooltip": "An entity that creates a material that can be attached to a Shape or Model.", + "skipJSProperty": true + }, + "useAssetServer": { + "tooltip": "A server that hosts content and assets. You can't take items that are hosted here into other domains.", + "skipJSProperty": true + }, + "importNewEntity": { + "tooltip": "Import a local or hosted file that can be used across domains.", + "skipJSProperty": true + } +} diff --git a/scripts/system/assets/models/black-sphere.fbx b/scripts/system/assets/models/black-sphere.fbx new file mode 100644 index 0000000000..2e6dea233f Binary files /dev/null and b/scripts/system/assets/models/black-sphere.fbx differ diff --git a/scripts/system/assets/sounds/crystals_and_voices.mp3 b/scripts/system/assets/sounds/crystals_and_voices.mp3 new file mode 100644 index 0000000000..1dd2037e6b Binary files /dev/null and b/scripts/system/assets/sounds/crystals_and_voices.mp3 differ diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 10ccb66d96..a55ed74a86 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -28,9 +28,8 @@ function executeLater(callback) { Script.setTimeout(callback, 300); } -var INVALID_JOINT_INDEX = -1 function isWearable(avatarEntity) { - return avatarEntity.properties.visible === true && (avatarEntity.properties.parentJointIndex !== INVALID_JOINT_INDEX || avatarEntity.properties.relayParentJoints === true) && + return avatarEntity.properties.visible === true && (avatarEntity.properties.parentID === MyAvatar.sessionUUID || avatarEntity.properties.parentID === MyAvatar.SELF_ID); } @@ -159,7 +158,7 @@ var selectedAvatarEntityGrabbable = false; var selectedAvatarEntityID = null; var grabbedAvatarEntityChangeNotifier = null; -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; +var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js"); @@ -285,9 +284,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See case 'navigate': var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system") if(message.url.indexOf('app://') === 0) { - if(message.url === 'app://marketplace') { + if (message.url === 'app://marketplace') { tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } else if(message.url === 'app://purchases') { + } else if (message.url === 'app://purchases') { tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); } @@ -329,36 +328,18 @@ function isGrabbable(entityID) { return false; } - var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']); + var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'grab.grabbable']); if (properties.clientOnly) { - var userData; - try { - userData = JSON.parse(properties.userData); - } catch (e) { - userData = {}; - } - - return userData.grabbableKey && userData.grabbableKey.grabbable; + return properties.grab.grabbable; } return false; } function setGrabbable(entityID, grabbable) { - var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']); + var properties = Entities.getEntityProperties(entityID, ['clientOnly']); if (properties.clientOnly) { - var userData; - try { - userData = JSON.parse(properties.userData); - } catch (e) { - userData = {}; - } - - if (userData.grabbableKey === undefined) { - userData.grabbableKey = {}; - } - userData.grabbableKey.grabbable = grabbable; - Entities.editEntity(entityID, {userData: JSON.stringify(userData)}); + Entities.editEntity(entityID, { grab: { grabbable: grabbable }}); } } diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5b91afea33..9fb336f79c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global getConnectionData */ +/* global getConnectionData getControllerWorldLocation openLoginWindow WalletScriptingInterface */ (function () { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/accountUtils.js"); @@ -20,7 +20,6 @@ var AppUi = Script.require('appUi'); var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; - // BEGIN AVATAR SELECTOR LOGIC var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; @@ -48,7 +47,6 @@ ExtendedOverlay.prototype.editOverlay = function (properties) { // change displa function color(selected, hovering) { var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; function scale(component) { - var delta = 0xFF - component; return component; } return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; @@ -105,7 +103,8 @@ ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId // hit(overlay) on the one overlay intersected by pickRay, if any. // noHit() if no ExtendedOverlay was intersected (helps with hover) ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { - var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. + // Depends on nearer coverOverlays to extend closer to us than farther ones. + var pickedOverlay = Overlays.findRayIntersection(pickRay); if (!pickedOverlay.intersects) { if (noHit) { return noHit(); @@ -131,6 +130,7 @@ function addAvatarNode(id) { } var pingPong = true; +var OVERLAY_SCALE = 0.032; function updateOverlays() { var eye = Camera.position; AvatarList.getAvatarIdentifiers().forEach(function (id) { @@ -148,7 +148,8 @@ function updateOverlays() { var target = avatar.position; var distance = Vec3.distance(target, eye); var offset = 0.2; - var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) + // get diff between target and eye (a vector pointing to the eye from avatar position) + var diff = Vec3.subtract(target, eye); var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can if (headIndex > 0) { offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; @@ -164,7 +165,7 @@ function updateOverlays() { overlay.editOverlay({ color: color(ExtendedOverlay.isSelected(id), overlay.hovering), position: target, - dimensions: 0.032 * distance + dimensions: OVERLAY_SCALE * distance }); }); pingPong = !pingPong; @@ -376,10 +377,35 @@ function deleteSendMoneyParticleEffect() { } function onUsernameChanged() { - if (Account.username !== Settings.getValue("wallet/savedUsername")) { - Settings.setValue("wallet/autoLogout", false); - Settings.setValue("wallet/savedUsername", ""); + if (Account.username !== Settings.getValue("keepMeLoggedIn/savedUsername")) { + Settings.setValue("keepMeLoggedIn", false); + Settings.setValue("keepMeLoggedIn/savedUsername", ""); } +} + +var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); +var METAVERSE_SERVER_URL = Account.metaverseServerURL; +var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. +function openMarketplace(optionalItemOrUrl) { + // This is a bit of a kluge, but so is the whole file. + // If given a whole path, use it with no cta. + // If given an id, build the appropriate url and use the id as the cta. + // Otherwise, use home and 'marketplace cta'. + // AND... if call onMarketplaceOpen to setupWallet if we need to. + var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL; + // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id. + if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { + url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; + } + ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); +} + +function setCertificateInfo(itemCertificateId) { + ui.tablet.sendToQml({ + method: 'inspectionCertificate_setCertificateId', + entityId: "", + certificateId: itemCertificateId + }); } // Function Name: fromQml() @@ -387,8 +413,6 @@ function onUsernameChanged() { // Description: // -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML // in the format "{method, params}", like json-rpc. See also sendToQml(). -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); function fromQml(message) { switch (message.method) { case 'passphrasePopup_cancelClicked': @@ -413,6 +437,7 @@ function fromQml(message) { } break; case 'needsLogIn_loginClicked': + ui.close(); openLoginWindow(); break; case 'disableHmdPreview': @@ -422,10 +447,6 @@ function fromQml(message) { case 'transactionHistory_linkClicked': ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); break; - case 'goToPurchases_fromWalletHome': - case 'goToPurchases': - ui.open(MARKETPLACE_PURCHASES_QML_PATH); - break; case 'goToMarketplaceMainPage': ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); break; @@ -450,22 +471,20 @@ function fromQml(message) { removeOverlays(); break; case 'sendAsset_sendPublicly': - if (message.assetName === "") { - deleteSendMoneyParticleEffect(); - sendMoneyRecipient = message.recipient; - var amount = message.amount; - var props = SEND_MONEY_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendMoneyParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendMoneyParticleEffect(); - sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); + deleteSendMoneyParticleEffect(); + sendMoneyRecipient = message.recipient; + var props = SEND_MONEY_PARTICLE_PROPERTIES; + props.parentID = MyAvatar.sessionUUID; + props.position = MyAvatar.position; + props.position.y += 0.2; + if (message.effectImage) { + props.textures = message.effectImage; } + sendMoneyParticleEffect = Entities.addEntity(props, true); + particleEffectTimestamp = Date.now(); + updateSendMoneyParticleEffect(); + sendMoneyParticleEffectUpdateTimer = + Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); break; case 'transactionHistory_goToBank': if (Account.metaverseServerURL.indexOf("staging") >= 0) { @@ -474,6 +493,49 @@ function fromQml(message) { Window.location = "hifi://BankOfHighFidelity"; } break; + case 'purchases_updateWearables': + var currentlyWornWearables = []; + var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case) + + var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS); + + for (var i = 0; i < nearbyEntities.length; i++) { + var currentProperties = Entities.getEntityProperties( + nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID'] + ); + if (currentProperties.parentID === MyAvatar.sessionUUID) { + currentlyWornWearables.push({ + entityID: nearbyEntities[i], + entityCertID: currentProperties.certificateID, + entityEdition: currentProperties.editionNumber + }); + } + } + + ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); + break; + case 'purchases_walletNotSetUp': + ui.tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: "purchases" + }); + break; + case 'purchases_openGoTo': + ui.open("hifi/tablet/TabletAddressDialog.qml"); + break; + case 'purchases_itemInfoClicked': + var itemId = message.itemId; + if (itemId && itemId !== "") { + openMarketplace(itemId); + } + break; + case 'purchases_itemCertificateClicked': + setCertificateInfo(message.itemCertificateId); + break; + case 'clearShouldShowDotHistory': + shouldShowDotHistory = false; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); + break; case 'http.request': // Handled elsewhere, don't log. break; @@ -482,41 +544,76 @@ function fromQml(message) { } } +var isWired = false; function walletOpened() { Users.usernameFromIDReply.connect(usernameFromIDReply); Controller.mousePressEvent.connect(handleMouseEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent); triggerMapping.enable(); triggerPressMapping.enable(); - shouldShowDot = false; - ui.messagesWaiting(shouldShowDot); + isWired = true; + + if (shouldShowDotHistory) { + ui.sendMessage({ + method: 'updateRecentActivityMessageLight', + messagesWaiting: shouldShowDotHistory + }); + } } function walletClosed() { off(); } -function notificationDataProcessPage(data) { +function notificationDataProcessPageUpdates(data) { + return data.data.updates; +} + +function notificationDataProcessPageHistory(data) { return data.data.history; } -var shouldShowDot = false; -function notificationPollCallback(historyArray) { +var shouldShowDotUpdates = false; +function notificationPollCallbackUpdates(updatesArray) { + shouldShowDotUpdates = shouldShowDotUpdates || updatesArray.length > 0; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); + + if (updatesArray.length > 0) { + var message; + if (!ui.notificationInitialCallbackMade[0]) { + message = updatesArray.length + " of your purchased items " + + (updatesArray.length === 1 ? "has an update " : "have updates ") + + "available. Open INVENTORY to update."; + ui.notificationDisplayBanner(message); + + ui.notificationPollCaresAboutSince[0] = true; + } else { + for (var i = 0; i < updatesArray.length; i++) { + message = "Update available for \"" + + updatesArray[i].base_item_title + "\"." + + "Open INVENTORY to update."; + ui.notificationDisplayBanner(message); + } + } + } +} +var shouldShowDotHistory = false; +function notificationPollCallbackHistory(historyArray) { if (!ui.isOpen) { var notificationCount = historyArray.length; - shouldShowDot = shouldShowDot || notificationCount > 0; - ui.messagesWaiting(shouldShowDot); + shouldShowDotHistory = shouldShowDotHistory || notificationCount > 0; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); if (notificationCount > 0) { var message; - if (!ui.notificationInitialCallbackMade) { - message = "You have " + notificationCount + " unread wallet " + - "transaction" + (notificationCount === 1 ? "" : "s") + ". Open WALLET to see all activity."; + if (!ui.notificationInitialCallbackMade[1]) { + message = "You have " + notificationCount + " unread recent " + + "transaction" + (notificationCount === 1 ? "" : "s") + ". Open INVENTORY to see all activity."; ui.notificationDisplayBanner(message); } else { for (var i = 0; i < notificationCount; i++) { message = '"' + (historyArray[i].message) + '" ' + - "Open WALLET to see all activity."; + "Open INVENTORY to see all activity."; ui.notificationDisplayBanner(message); } } @@ -524,7 +621,12 @@ function notificationPollCallback(historyArray) { } } -function isReturnedDataEmpty(data) { +function isReturnedDataEmptyUpdates(data) { + var updatesArray = data.data.updates; + return updatesArray.length === 0; +} + +function isReturnedDataEmptyHistory(data) { var historyArray = data.data.history; return historyArray.length === 0; } @@ -559,10 +661,27 @@ function uninstallMarketplaceItemTester() { } } -var BUTTON_NAME = "WALLET"; +var BUTTON_NAME = "INVENTORY"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; +var NOTIFICATION_POLL_TIMEOUT = 300000; var ui; function startup() { + var notificationPollEndpointArray = ["/api/v1/commerce/available_updates?per_page=10"]; + var notificationPollTimeoutMsArray = [NOTIFICATION_POLL_TIMEOUT]; + var notificationDataProcessPageArray = [notificationDataProcessPageUpdates]; + var notificationPollCallbackArray = [notificationPollCallbackUpdates]; + var notificationPollStopPaginatingConditionMetArray = [isReturnedDataEmptyUpdates]; + var notificationPollCaresAboutSinceArray = [false]; + + if (!WalletScriptingInterface.limitedCommerce) { + notificationPollEndpointArray[1] = "/api/v1/commerce/history?per_page=10"; + notificationPollTimeoutMsArray[1] = NOTIFICATION_POLL_TIMEOUT; + notificationDataProcessPageArray[1] = notificationDataProcessPageHistory; + notificationPollCallbackArray[1] = notificationPollCallbackHistory; + notificationPollStopPaginatingConditionMetArray[1] = isReturnedDataEmptyHistory; + notificationPollCaresAboutSinceArray[1] = true; + } + ui = new AppUi({ buttonName: BUTTON_NAME, sortOrder: 10, @@ -570,12 +689,12 @@ function startup() { onOpened: walletOpened, onClosed: walletClosed, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/commerce/history?per_page=10", - notificationPollTimeoutMs: 300000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: true + notificationPollEndpoint: notificationPollEndpointArray, + notificationPollTimeoutMs: notificationPollTimeoutMsArray, + notificationDataProcessPage: notificationDataProcessPageArray, + notificationPollCallback: notificationPollCallbackArray, + notificationPollStopPaginatingConditionMet: notificationPollStopPaginatingConditionMetArray, + notificationPollCaresAboutSince: notificationPollCaresAboutSinceArray }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); installMarketplaceItemTester(); @@ -583,11 +702,14 @@ function startup() { var isUpdateOverlaysWired = false; function off() { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); + if (isWired) { + Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + triggerMapping.disable(); + triggerPressMapping.disable(); + isWired = false; + } if (isUpdateOverlaysWired) { Script.update.disconnect(updateOverlays); diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index db622a6724..a38a230c66 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -10,9 +10,8 @@ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, - getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, COLORS_GRAB_SEARCHING_HALF_SQUEEZE - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, TRIGGER_ON_VALUE, PointerManager, print getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, + PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE */ @@ -34,6 +33,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var PROFILE = false; var DEBUG = false; + var SHOW_GRAB_SPHERE = false; + if (typeof Test !== "undefined") { PROFILE = true; @@ -51,6 +52,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.tabletID = null; this.blacklist = []; this.pointerManager = new PointerManager(); + this.grabSphereOverlays = [null, null]; // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name @@ -251,7 +253,28 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { if (controllerLocations[h].valid) { var controllerPosition = controllerLocations[h].position; - var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS * sensorScaleFactor); + var findRadius = NEAR_MAX_RADIUS * sensorScaleFactor; + + if (SHOW_GRAB_SPHERE) { + if (this.grabSphereOverlays[h]) { + Overlays.editOverlay(this.grabSphereOverlays[h], { position: controllerLocations[h].position }); + } else { + var grabSphereSize = findRadius * 2; + this.grabSphereOverlays[h] = Overlays.addOverlay("sphere", { + position: controllerLocations[h].position, + dimensions: { x: grabSphereSize, y: grabSphereSize, z: grabSphereSize }, + color: { red: 30, green: 30, blue: 255 }, + alpha: 0.3, + solid: true, + visible: true, + // lineWidth: 2.0, + drawInFront: false, + grabbable: false + }); + } + } + + var nearbyEntityIDs = Entities.findEntities(controllerPosition, findRadius); for (var j = 0; j < nearbyEntityIDs.length; j++) { var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); @@ -505,6 +528,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Selection.disableListHighlight(DISPATCHER_HOVERING_LIST); }; } + function mouseReleaseOnOverlay(overlayID, event) { if (HMD.homeButtonID && overlayID === HMD.homeButtonID && event.button === "Primary") { Messages.sendLocalMessage("home", overlayID); @@ -523,9 +547,11 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } } } + Overlays.mouseReleaseOnOverlay.connect(mouseReleaseOnOverlay); Overlays.mousePressOnOverlay.connect(mousePress); Entities.mousePressOnEntity.connect(mousePress); + var controllerDispatcher = new ControllerDispatcher(); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); Messages.messageReceived.connect(controllerDispatcher.handleHandMessage); diff --git a/scripts/system/controllers/controllerDisplay.js b/scripts/system/controllers/controllerDisplay.js index 8aa0393357..e40b761307 100644 --- a/scripts/system/controllers/controllerDisplay.js +++ b/scripts/system/controllers/controllerDisplay.js @@ -7,7 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* globals createControllerDisplay:true deleteControllerDisplay:true */ +/* globals createControllerDisplay:true, deleteControllerDisplay:true, Controller, Overlays, Vec3, MyAvatar, Quat */ function clamp(value, min, max) { if (value < min) { @@ -188,7 +188,7 @@ createControllerDisplay = function(config) { for (var partName in controller.parts) { var part = controller.parts[partName]; var localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition); - var localRotation = { x: 0, y: 0, z: 0, w: 1 } + var localRotation = { x: 0, y: 0, z: 0, w: 1 }; controllerDisplay.parts[partName] = controller.parts[partName]; @@ -203,7 +203,7 @@ createControllerDisplay = function(config) { if (part.defaultTextureLayer) { var textures = {}; textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL; - properties['textures'] = textures; + properties.textures = textures; } var overlayID = Overlays.addOverlay("model", properties); diff --git a/scripts/system/controllers/controllerDisplayManager.js b/scripts/system/controllers/controllerDisplayManager.js index e3fd74e05b..f93f8b1624 100644 --- a/scripts/system/controllers/controllerDisplayManager.js +++ b/scripts/system/controllers/controllerDisplayManager.js @@ -7,8 +7,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* globals ControllerDisplayManager:true createControllerDisplay deleteControllerDisplay - VIVE_CONTROLLER_CONFIGURATION_LEFT VIVE_CONTROLLER_CONFIGURATION_RIGHT */ +/* globals ControllerDisplayManager:true, createControllerDisplay, deleteControllerDisplay, + VIVE_CONTROLLER_CONFIGURATION_LEFT, VIVE_CONTROLLER_CONFIGURATION_RIGHT, Script, HMD, Controller, + MyAvatar, Overlays, TOUCH_CONTROLLER_CONFIGURATION_LEFT, TOUCH_CONTROLLER_CONFIGURATION_RIGHT, Messages */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function () { @@ -93,7 +94,7 @@ ControllerDisplayManager = function() { if (controllerRight) { controllerRight.resize(sensorScaleFactor); } - }; + } var handleMessages = function(channel, message, sender) { var i, data, name, visible; diff --git a/scripts/system/controllers/controllerModules/disableOtherModule.js b/scripts/system/controllers/controllerModules/disableOtherModule.js index 0928b29d5d..7636c56f65 100644 --- a/scripts/system/controllers/controllerModules/disableOtherModule.js +++ b/scripts/system/controllers/controllerModules/disableOtherModule.js @@ -58,18 +58,16 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (channel === 'Hifi-Hand-Disabler') { if (message === 'left') { leftDisableModules.disableModules = true; - } - if (message === 'right') { + } else if (message === 'right') { rightDisableModules.disableModules = true; - } - if (message === 'both' || message === 'none') { - if (message === 'both') { - leftDisableModules.disableModules = true; - rightDisableModules.disableModules = true; - } else if (message === 'none') { - leftDisableModules.disableModules = false; - rightDisableModules.disableModules = false; - } + } else if (message === 'both') { + leftDisableModules.disableModules = true; + rightDisableModules.disableModules = true; + } else if (message === 'none') { + leftDisableModules.disableModules = false; + rightDisableModules.disableModules = false; + } else { + print("disableOtherModule -- unknown command: " + message); } } } diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 91c8d89daf..12a69d7b27 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -6,11 +6,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera, +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera, print, getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, entityIsFarGrabbedByOther, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode + cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode, getGrabbableData */ Script.include("/~/system/libraries/Xform.js"); @@ -66,12 +66,16 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { overlays: [] }; - var diameter = hotspot.radius * 2; + var dimensions = hotspot.radius * 2 * EQUIP_SPHERE_SCALE_FACTOR; + + if (hotspot.indicatorURL) { + dimensions = hotspot.indicatorScale; + } // override default sphere with a user specified model, if it exists. overlayInfoSet.overlays.push(Overlays.addOverlay("model", { name: "hotspot overlay", - url: hotspot.modelURL ? hotspot.modelURL : DEFAULT_SPHERE_MODEL_URL, + url: hotspot.indicatorURL ? hotspot.indicatorURL : DEFAULT_SPHERE_MODEL_URL, position: hotspot.worldPosition, rotation: { x: 0, @@ -79,8 +83,7 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { z: 0, w: 1 }, - dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, - scale: hotspot.modelScale, + dimensions: dimensions, ignoreRayIntersection: true })); overlayInfoSet.type = "model"; @@ -137,8 +140,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var position = entityXform.xformPoint(overlayInfoSet.localPosition); var dimensions; - if (overlayInfoSet.type === "sphere") { - dimensions = (overlayInfoSet.hotspot.radius / 2) * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; + if (overlayInfoSet.hotspot.indicatorURL) { + var ratio = overlayInfoSet.currentSize / overlayInfoSet.targetSize; + dimensions = { + x: overlayInfoSet.hotspot.dimensions.x * ratio, + y: overlayInfoSet.hotspot.dimensions.y * ratio, + z: overlayInfoSet.hotspot.dimensions.z * ratio + }; } else { dimensions = (overlayInfoSet.hotspot.radius / 2) * overlayInfoSet.currentSize; } @@ -158,12 +166,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } }; + (function() { var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; - var EQUIP_RADIUS = 1.0; // radius used for palm vs equip-hotspot for equipping. - var HAPTIC_PULSE_STRENGTH = 1.0; var HAPTIC_PULSE_DURATION = 13.0; var HAPTIC_TEXTURE_STRENGTH = 0.1; @@ -176,36 +183,25 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var TRIGGER_OFF_VALUE = 0.1; var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab var BUMPER_ON_VALUE = 0.5; - + var EMPTY_PARENT_ID = "{00000000-0000-0000-0000-000000000000}"; - + var UNEQUIP_KEY = "u"; function getWearableData(props) { - var wearable = {}; - try { - if (!props.userDataParsed) { - props.userDataParsed = JSON.parse(props.userData); - } - - wearable = props.userDataParsed.wearable ? props.userDataParsed.wearable : {}; - } catch (err) { - // don't want to spam the logs + if (props.grab.equippable) { + return { + joints: { + LeftHand: [ props.grab.equippableLeftPosition, props.grab.equippableLeftRotation ], + RightHand: [ props.grab.equippableRightPosition, props.grab.equippableRightRotation ] + }, + indicatorURL: props.grab.equippableIndicatorURL, + indicatorScale: props.grab.equippableIndicatorScale, + indicatorOffset: props.grab.equippableIndicatorOffset + }; + } else { + return null } - return wearable; - } - function getEquipHotspotsData(props) { - var equipHotspots = []; - try { - if (!props.userDataParsed) { - props.userDataParsed = JSON.parse(props.userData); - } - - equipHotspots = props.userDataParsed.equipHotspots ? props.userDataParsed.equipHotspots : []; - } catch (err) { - // don't want to spam the logs - } - return equipHotspots; } function getAttachPointSettings() { @@ -275,7 +271,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.handHasBeenRightsideUp = false; this.parameters = makeDispatcherModuleParameters( - 300, + 115, this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "leftHandEquip"], [], 100); @@ -299,51 +295,29 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa // * radius {number} radius of equip hotspot // * joints {Object} keys are joint names values are arrays of two elements: // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. - // * modelURL {string} url for model to use instead of default sphere. - // * modelScale {Vec3} scale factor for model + // * indicatorURL {string} url for model to use instead of default sphere. + // * indicatorScale {Vec3} scale factor for model this.collectEquipHotspots = function(props) { var result = []; var entityID = props.id; var entityXform = new Xform(props.rotation, props.position); - var equipHotspotsProps = getEquipHotspotsData(props); - if (equipHotspotsProps && equipHotspotsProps.length > 0) { - var i, length = equipHotspotsProps.length; - for (i = 0; i < length; i++) { - var hotspot = equipHotspotsProps[i]; - if (hotspot.position && hotspot.radius && hotspot.joints) { - result.push({ - key: entityID.toString() + i.toString(), - entityID: entityID, - localPosition: hotspot.position, - worldPosition: entityXform.xformPoint(hotspot.position), - radius: hotspot.radius, - joints: hotspot.joints, - modelURL: hotspot.modelURL, - modelScale: hotspot.modelScale - }); - } - } - } else { - var wearableProps = getWearableData(props); - var sensorToScaleFactor = MyAvatar.sensorToWorldScale; - if (wearableProps && wearableProps.joints) { - - result.push({ - key: entityID.toString() + "0", - entityID: entityID, - localPosition: { - x: 0, - y: 0, - z: 0 - }, - worldPosition: entityXform.pos, - radius: EQUIP_RADIUS * sensorToScaleFactor, - joints: wearableProps.joints, - modelURL: null, - modelScale: null - }); - } + var wearableProps = getWearableData(props); + var sensorToScaleFactor = MyAvatar.sensorToWorldScale; + if (wearableProps && wearableProps.joints) { + result.push({ + key: entityID.toString() + "0", + entityID: entityID, + localPosition: wearableProps.indicatorOffset, + worldPosition: entityXform.pos, + radius: ((wearableProps.indicatorScale.x + + wearableProps.indicatorScale.y + + wearableProps.indicatorScale.z) / 3) * sensorToScaleFactor, + dimensions: wearableProps.indicatorScale, + joints: wearableProps.joints, + indicatorURL: wearableProps.indicatorURL, + indicatorScale: wearableProps.indicatorScale, + }); } return result; }; @@ -492,7 +466,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); - var grabbedProperties = Entities.getEntityProperties(this.targetEntityID); + var grabbedProperties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + var grabData = getGrabbableData(grabbedProperties); // if an object is "equipped" and has a predefined offset, use it. if (this.grabbedHotspot) { @@ -510,7 +485,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } var handJointIndex; - if (this.ignoreIK) { + if (HMD.mounted && HMD.isHandControllerAvailable() && grabData.grabFollowsController) { handJointIndex = this.controllerJointIndex; } else { handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); @@ -796,7 +771,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (intersection.intersects) { var entityID = intersection.entityID; var entityProperties = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); - var hasEquipData = getWearableData(entityProperties).joints || getEquipHotspotsData(entityProperties).length > 0; + entityProperties.id = entityID; + var hasEquipData = getWearableData(entityProperties); if (hasEquipData && entityProperties.parentID === EMPTY_PARENT_ID && !entityIsFarGrabbedByOther(entityID)) { entityProperties.id = entityID; var rightHandPosition = MyAvatar.getJointPosition("RightHand"); @@ -804,7 +780,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var distanceToRightHand = Vec3.distance(entityProperties.position, rightHandPosition); var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition); var leftHandAvailable = leftEquipEntity.targetEntityID === null; - var rightHandAvailable = rightEquipEntity.targetEntityID === null; + var rightHandAvailable = rightEquipEntity.targetEntityID === null; if (rightHandAvailable && (distanceToRightHand < distanceToLeftHand || !leftHandAvailable)) { // clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags) clearGrabActions(entityID); @@ -817,7 +793,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } }; - + var onKeyPress = function(event) { if (event.text.toLowerCase() === UNEQUIP_KEY) { if (rightEquipEntity.targetEntityID) { @@ -828,7 +804,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } }; - + var deleteEntity = function(entityID) { if (rightEquipEntity.targetEntityID === entityID) { rightEquipEntity.endEquipEntity(); @@ -837,7 +813,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa leftEquipEntity.endEquipEntity(); } }; - + var clearEntities = function() { if (rightEquipEntity.targetEntityID) { rightEquipEntity.endEquipEntity(); @@ -846,7 +822,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa leftEquipEntity.endEquipEntity(); } }; - + Messages.subscribe('Hifi-Hand-Grab'); Messages.subscribe('Hifi-Hand-Drop'); Messages.messageReceived.connect(handleMessage); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 2e73526728..2a360c0f31 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -8,38 +8,19 @@ /* jslint bitwise: true */ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, + getEnabledModuleByName, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - worldPositionToRegistrationFrameMatrix + TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, + Picks, makeLaserLockInfo, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES, Uuid, Picks */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/libraries/Xform.js"); (function() { - var GRABBABLE_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; var MARGIN = 25; @@ -106,9 +87,9 @@ Script.include("/~/system/libraries/Xform.js"); this.locked = false; this.highlightedEntity = null; this.reticleMinX = MARGIN; - this.reticleMaxX; + this.reticleMaxX = null; this.reticleMinY = MARGIN; - this.reticleMaxY; + this.reticleMaxY = null; this.ignoredEntities = []; @@ -212,7 +193,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -317,7 +298,7 @@ Script.include("/~/system/libraries/Xform.js"); }; this.restoreIgnoredEntities = function() { - for (var i = 0; i < this.ignoredEntities; i++) { + for (var i = 0; i < this.ignoredEntities.length; i++) { var data = { action: 'remove', id: this.ignoredEntities[i] @@ -329,22 +310,13 @@ Script.include("/~/system/libraries/Xform.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") || intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) { return true; - } else if (intersection.type === Picks.INTERSECTED_ENTITY && !Window.isPhysicsEnabled()) { - // add to ignored items. - var data = { - action: 'add', - id: intersection.objectID - }; - Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)); - this.ignoredEntities.push(intersection.objectID); - } return false; }; @@ -373,7 +345,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -395,7 +367,7 @@ Script.include("/~/system/libraries/Xform.js"); }; this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.grabbedThingID); + var properties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } @@ -405,7 +377,6 @@ Script.include("/~/system/libraries/Xform.js"); this.isReady = function (controllerData) { if (HMD.active) { if (this.notPointingAtEntity(controllerData)) { - this.restoreIgnoredEntities(); return makeRunningValues(false, [], []); } @@ -417,17 +388,28 @@ Script.include("/~/system/libraries/Xform.js"); return makeRunningValues(true, [], []); } else { this.destroyContextOverlay(); - this.restoreIgnoredEntities(); return makeRunningValues(false, [], []); } } - this.restoreIgnoredEntities(); return makeRunningValues(false, [], []); }; this.run = function (controllerData) { + + var intersection = controllerData.rayPicks[this.hand]; + if (intersection.type === Picks.INTERSECTED_ENTITY && !Window.isPhysicsEnabled()) { + // add to ignored items. + if (this.ignoredEntities.indexOf(intersection.objectID) === -1) { + var data = { + action: 'add', + id: intersection.objectID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)); + this.ignoredEntities.push(intersection.objectID); + } + } if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || - this.notPointingAtEntity(controllerData) || this.targetIsNull()) { + (this.notPointingAtEntity(controllerData) && Window.isPhysicsEnabled()) || this.targetIsNull()) { this.endFarGrabAction(); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); @@ -446,7 +428,8 @@ Script.include("/~/system/libraries/Xform.js"); this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" + this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", + this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" ]; var nearGrabReadiness = []; @@ -460,8 +443,8 @@ Script.include("/~/system/libraries/Xform.js"); // if we are doing a distance grab and the object or tablet gets close enough to the controller, // stop the far-grab so the near-grab or equip can take over. for (var k = 0; k < nearGrabReadiness.length; k++) { - if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.grabbedThingID - || HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { + if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.grabbedThingID || + HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { this.endFarGrabAction(); this.restoreIgnoredEntities(); return makeRunningValues(false, [], []); @@ -487,11 +470,7 @@ Script.include("/~/system/libraries/Xform.js"); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; - var targetProps = Entities.getEntityProperties(entityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); this.restoreIgnoredEntities(); @@ -540,11 +519,7 @@ Script.include("/~/system/libraries/Xform.js"); if (this.highlightedEntity !== targetEntityID) { Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); @@ -574,11 +549,12 @@ Script.include("/~/system/libraries/Xform.js"); if (!_this.entityWithContextOverlay && _this.contextOverlayTimer && _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var props = Entities.getEntityProperties(rayPickInfo.objectID); + var props = Entities.getEntityProperties(rayPickInfo.objectID, DISPATCHER_PROPERTIES); var pointerEvent = { type: "Move", id: _this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, + rayPickInfo.intersection, props), pos3D: rayPickInfo.intersection, normal: rayPickInfo.surfaceNormal, direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js index 78abcb9b20..f983ed1e7d 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js @@ -11,32 +11,14 @@ makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, - Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - Uuid, worldPositionToRegistrationFrameMatrix + Picks, makeLaserLockInfo, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + Uuid, worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/libraries/Xform.js"); (function() { - var GRABBABLE_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; var MARGIN = 25; @@ -45,7 +27,6 @@ Script.include("/~/system/libraries/Xform.js"); this.entityProps = entityProps; this.targetEntityID = null; this.targetEntityProps = null; - this.previousCollisionStatus = null; this.getTargetEntity = function() { var parentPropsLength = this.parentProps.length; @@ -74,7 +55,6 @@ Script.include("/~/system/libraries/Xform.js"); this.potentialEntityWithContextOverlay = false; this.entityWithContextOverlay = false; this.contextOverlayTimer = false; - this.previousCollisionStatus = false; this.locked = false; this.highlightedEntity = null; this.reticleMinX = MARGIN; @@ -182,7 +162,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -284,7 +264,7 @@ Script.include("/~/system/libraries/Xform.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -319,7 +299,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -341,7 +321,7 @@ Script.include("/~/system/libraries/Xform.js"); }; this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.grabbedThingID); + var properties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } @@ -351,7 +331,7 @@ Script.include("/~/system/libraries/Xform.js"); this.getTargetProps = function (controllerData) { var targetEntityID = controllerData.rayPicks[this.hand].objectID; if (targetEntityID) { - return Entities.getEntityProperties(targetEntityID); + return Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); } return null; }; @@ -396,7 +376,8 @@ Script.include("/~/system/libraries/Xform.js"); this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" + this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", + this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" ]; var nearGrabReadiness = []; @@ -435,11 +416,7 @@ Script.include("/~/system/libraries/Xform.js"); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; - var targetProps = Entities.getEntityProperties(entityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); return makeRunningValues(false, [], []); @@ -488,11 +465,7 @@ Script.include("/~/system/libraries/Xform.js"); if (this.highlightedEntity !== targetEntityID) { Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); @@ -522,7 +495,8 @@ Script.include("/~/system/libraries/Xform.js"); if (!_this.entityWithContextOverlay && _this.contextOverlayTimer && _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var pEvProps = Entities.getEntityProperties(rayPickInfo.objectID); + var pEvProps = Entities.getEntityProperties(rayPickInfo.objectID, + DISPATCHER_PROPERTIES); var pointerEvent = { type: "Move", id: _this.hand + 1, // 0 is reserved for hardware mouse diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js index a9ec246a32..f85869aa7f 100644 --- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js @@ -10,34 +10,15 @@ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Quat, getEnabledModuleByName, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, - projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, + projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent, - worldPositionToRegistrationFrameMatrix + worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES, findFarGrabJointChildEntities */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/libraries/Xform.js"); (function() { - var GRABBABLE_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; - var MARGIN = 25; function TargetObject(entityID, entityProps) { @@ -68,6 +49,7 @@ Script.include("/~/system/libraries/Xform.js"); this.hand = hand; this.targetEntityID = null; this.targetObject = null; + this.previouslyUnhooked = {}; this.previousParentID = {}; this.previousParentJointIndex = {}; this.potentialEntityWithContextOverlay = false; @@ -78,6 +60,7 @@ Script.include("/~/system/libraries/Xform.js"); this.reticleMaxX = 0; this.reticleMinY = MARGIN; this.reticleMaxY = 0; + this.lastUnexpectedChildrenCheckTime = 0; var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX @@ -214,7 +197,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.targetEntityID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -275,11 +258,13 @@ Script.include("/~/system/libraries/Xform.js"); this.endFarParentGrab = function (controllerData) { this.hapticTargetID = null; // var endProps = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; - var endProps = Entities.getEntityProperties(this.targetEntityID, GRABBABLE_PROPERTIES); + var endProps = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); if (this.thisFarGrabJointIsParent(endProps)) { Entities.editEntity(this.targetEntityID, { parentID: this.previousParentID[this.targetEntityID], - parentJointIndex: this.previousParentJointIndex[this.targetEntityID] + parentJointIndex: this.previousParentJointIndex[this.targetEntityID], + localVelocity: {x: 0, y: 0, z: 0}, + localAngularVelocity: {x: 0, y: 0, z: 0} }); } @@ -313,7 +298,7 @@ Script.include("/~/system/libraries/Xform.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -348,7 +333,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -369,8 +354,51 @@ Script.include("/~/system/libraries/Xform.js"); } }; + this.checkForUnexpectedChildren = function (controllerData) { + // sometimes things can get parented to a hand and this script is unaware. Search for such entities and + // unhook them. + + var now = Date.now(); + var UNEXPECTED_CHILDREN_CHECK_TIME = 0.1; // seconds + if (now - this.lastUnexpectedChildrenCheckTime > MSECS_PER_SEC * UNEXPECTED_CHILDREN_CHECK_TIME) { + this.lastUnexpectedChildrenCheckTime = now; + + var children = findFarGrabJointChildEntities(this.hand); + var _this = this; + + children.forEach(function(childID) { + // we appear to be holding something and this script isn't in a state that would be holding something. + // unhook it. if we previously took note of this entity's parent, put it back where it was. This + // works around some problems that happen when more than one hand or avatar is passing something around. + if (_this.previousParentID[childID]) { + var previousParentID = _this.previousParentID[childID]; + var previousParentJointIndex = _this.previousParentJointIndex[childID]; + + // The main flaw with keeping track of previous parentage in individual scripts is: + // (1) A grabs something (2) B takes it from A (3) A takes it from B (4) A releases it + // now A and B will take turns passing it back to the other. Detect this and stop the loop here... + var UNHOOK_LOOP_DETECT_MS = 200; + if (_this.previouslyUnhooked[childID]) { + if (now - _this.previouslyUnhooked[childID] < UNHOOK_LOOP_DETECT_MS) { + previousParentID = Uuid.NULL; + previousParentJointIndex = -1; + } + } + _this.previouslyUnhooked[childID] = now; + + Entities.editEntity(childID, { + parentID: previousParentID, + parentJointIndex: previousParentJointIndex + }); + } else { + Entities.editEntity(childID, { parentID: Uuid.NULL }); + } + }); + } + }; + this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.targetEntityID, GRABBABLE_PROPERTIES); + var properties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } @@ -380,7 +408,7 @@ Script.include("/~/system/libraries/Xform.js"); this.getTargetProps = function (controllerData) { var targetEntity = controllerData.rayPicks[this.hand].objectID; if (targetEntity) { - var gtProps = Entities.getEntityProperties(targetEntity, GRABBABLE_PROPERTIES); + var gtProps = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); if (entityIsGrabbable(gtProps)) { // give haptic feedback if (gtProps.id !== this.hapticTargetID) { @@ -416,6 +444,7 @@ Script.include("/~/system/libraries/Xform.js"); return makeRunningValues(true, [], []); } } else { + this.checkForUnexpectedChildren(controllerData); this.destroyContextOverlay(); return makeRunningValues(false, [], []); } @@ -442,7 +471,8 @@ Script.include("/~/system/libraries/Xform.js"); this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" + this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", + this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" ]; var nearGrabReadiness = []; @@ -480,11 +510,7 @@ Script.include("/~/system/libraries/Xform.js"); var entityID = rayPickInfo.objectID; Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; - var targetProps = Entities.getEntityProperties(entityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); return makeRunningValues(false, [], []); @@ -533,11 +559,7 @@ Script.include("/~/system/libraries/Xform.js"); var targetEntityID = rayPickInfo.objectID; if (this.highlightedEntity !== targetEntityID) { Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); @@ -567,7 +589,8 @@ Script.include("/~/system/libraries/Xform.js"); if (!_this.entityWithContextOverlay && _this.contextOverlayTimer && _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var cotProps = Entities.getEntityProperties(rayPickInfo.objectID); + var cotProps = Entities.getEntityProperties(rayPickInfo.objectID, + DISPATCHER_PROPERTIES); var pointerEvent = { type: "Move", id: _this.hand + 1, // 0 is reserved for hardware mouse diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index 25df17ee84..2b003e4732 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -6,19 +6,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Controller, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, +/* global Script, RIGHT_HAND, LEFT_HAND, MyAvatar, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData, makeLaserParams + getGrabbableData, makeLaserParams, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - function entityWantsNearTrigger(props) { + function entityWantsFarTrigger(props) { var grabbableData = getGrabbableData(props); - return grabbableData.triggerable || grabbableData.wantsTrigger; + return grabbableData.triggerable; } function FarTriggerEntity(hand) { @@ -37,11 +36,10 @@ Script.include("/~/system/libraries/controllers.js"); makeLaserParams(this.hand, false)); this.getTargetProps = function (controllerData) { - // nearbyEntityProperties is already sorted by length from controller var targetEntity = controllerData.rayPicks[this.hand].objectID; if (targetEntity) { - var targetProperties = Entities.getEntityProperties(targetEntity); - if (entityWantsNearTrigger(targetProperties)) { + var targetProperties = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); + if (entityWantsFarTrigger(targetProperties)) { return targetProperties; } } diff --git a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js index bc09ebee7a..403b5d5149 100644 --- a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js +++ b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js @@ -8,11 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Controller, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, - makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData, makeLaserParams, entityIsCloneable, Messages, print -*/ +/* global Script, MyAvatar, entityIsCloneable, Messages, print */ "use strict"; @@ -134,7 +130,7 @@ rightHighlightNearbyEntities.removeEntityFromHighlightList(data.entityID); } } catch (e) { - print("Failed to parse message"); + print("highlightNearbyEntities -- Failed to parse message: " + JSON.stringify(message)); } } else if (channel === 'Hifi-unhighlight-all') { leftHighlightNearbyEntities.clearAll(); diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index 04a3911e0b..efbca66d72 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -10,16 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, - enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, - makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI, - makeLaserParams - -*/ +/* global Script, Controller, RIGHT_HAND, LEFT_HAND, HMD, makeLaserParams */ (function() { Script.include("/~/system/libraries/controllers.js"); var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); @@ -33,7 +24,7 @@ this.reticleMinY = MARGIN; this.reticleMaxY; this.parameters = ControllerDispatcherUtils.makeDispatcherModuleParameters( - 540, + 160, // Same as webSurfaceLaserInput. this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100, @@ -72,7 +63,6 @@ this.processLaser = function(controllerData) { var controllerLocation = controllerData.controllerLocations[this.hand]; - var otherModuleRunning = this.getOtherModule().running; if ((controllerData.triggerValues[this.hand] < ControllerDispatcherUtils.TRIGGER_ON_VALUE || !controllerLocation.valid) || this.pointingAtTablet(controllerData)) { return false; diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 15da1537a1..6adfa88fb2 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -8,9 +8,8 @@ /* jslint bitwise: true */ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeRunningValues, - Messages, makeDispatcherModuleParameters, HMD, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, Picks, makeLaserParams, Entities + Messages, makeDispatcherModuleParameters, HMD, getEnabledModuleByName, TRIGGER_ON_VALUE, isInEditMode, Picks, + makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -24,12 +23,12 @@ Script.include("/~/system/libraries/utils.js"); this.triggerClicked = false; this.selectedTarget = null; this.reticleMinX = MARGIN; - this.reticleMaxX; + this.reticleMaxX = null; this.reticleMinY = MARGIN; - this.reticleMaxY; + this.reticleMaxY = null; this.parameters = makeDispatcherModuleParameters( - 160, + 165, // Lower priority than webSurfaceLaserInput and hudOverlayPointer. this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100, @@ -49,8 +48,8 @@ Script.include("/~/system/libraries/utils.js"); }; this.pointingAtTablet = function(objectID) { - return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) - || (HMD.homeButtonID && objectID === HMD.homeButtonID); + return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) || + (HMD.homeButtonID && objectID === HMD.homeButtonID); }; this.calculateNewReticlePosition = function(intersection) { @@ -128,30 +127,43 @@ Script.include("/~/system/libraries/utils.js"); }; this.run = function(controllerData) { - var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightTabletStylusInput" : "LeftTabletStylusInput"); + var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightTabletStylusInput" : "LeftTabletStylusInput"); if (tabletStylusInput) { var tabletReady = tabletStylusInput.isReady(controllerData); - if (tabletReady.active) { return this.exitModule(); } } - var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput"); - if (overlayLaser) { - var overlayLaserReady = overlayLaser.isReady(controllerData); + var webLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput"); + if (webLaser) { + var webLaserReady = webLaser.isReady(controllerData); var target = controllerData.rayPicks[this.hand].objectID; this.sendPointingAtData(controllerData); - if (overlayLaserReady.active && this.pointingAtTablet(target)) { + if (webLaserReady.active && this.pointingAtTablet(target)) { return this.exitModule(); } } - var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); - if (nearOverlay) { - var nearOverlayReady = nearOverlay.isReady(controllerData); + if (!controllerData.triggerClicks[this.hand]) { // Don't grab if trigger pressed when laser starts intersecting. + var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHudOverlayPointer" : "LeftHudOverlayPointer"); + if (hudLaser) { + var hudLaserReady = hudLaser.isReady(controllerData); + if (hudLaserReady.active) { + return this.exitModule(); + } + } + } - if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) { + // Tablet highlight and grabbing. + var tabletHighlight = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"); + if (tabletHighlight) { + var tabletHighlightReady = tabletHighlight.isReady(controllerData); + if (tabletHighlightReady.active) { return this.exitModule(); } } @@ -163,6 +175,23 @@ Script.include("/~/system/libraries/utils.js"); return this.exitModule(); } } + + var stopRunning = false; + + if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)) { + var stopRunning = false; + controllerData.nearbyOverlayIDs[this.hand].forEach(function(overlayID) { + var overlayName = Overlays.getProperty(overlayID, "name"); + if (overlayName === "KeyboardAnchor") { + stopRunning = true; + } + }); + + if (stopRunning) { + return this.exitModule(); + } + } + this.sendPickData(controllerData); return this.isReady(controllerData); }; diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 7b78d5e1c4..0c04918ab1 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -8,7 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, +/* global Script, HMD, Messages, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, makeRunningValues, getEnabledModuleByName, makeLaserParams */ @@ -19,9 +19,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); function InVREditMode(hand) { this.hand = hand; this.disableModules = false; - var NO_HAND_LASER = -1; // Invalid hand parameter so that default laser is not displayed. + this.running = false; + var NO_HAND_LASER = -1; // Invalid hand parameter so that standard laser is not displayed. this.parameters = makeDispatcherModuleParameters( - 200, // Not too high otherwise the tablet laser doesn't work. + 166, // Slightly lower priority than inEditMode. this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], @@ -31,8 +32,37 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); ); this.pointingAtTablet = function (objectID) { - return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) - || (HMD.homeButtonID && objectID === HMD.homeButtonID); + return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) || + (HMD.homeButtonID && objectID === HMD.homeButtonID); + }; + + // The Shapes app has a non-standard laser: in particular, the laser end dot displays on its own when the laser is + // pointing at the Shapes UI. The laser on/off is controlled by this module but the laser is implemented in the Shapes + // app. + // If, in the future, the Shapes app laser interaction is adopted as a standard UI style then the laser could be + // implemented in the controller modules along side the other laser styles. + var INVREDIT_MODULE_RUNNING = "Hifi-InVREdit-Module-Running"; + + this.runModule = function () { + if (!this.running) { + Messages.sendLocalMessage(INVREDIT_MODULE_RUNNING, JSON.stringify({ + hand: this.hand, + running: true + })); + this.running = true; + } + return makeRunningValues(true, [], []); + }; + + this.exitModule = function () { + if (this.running) { + Messages.sendLocalMessage(INVREDIT_MODULE_RUNNING, JSON.stringify({ + hand: this.hand, + running: false + })); + this.running = false; + } + return makeRunningValues(false, [], []); }; this.isReady = function (controllerData) { @@ -45,56 +75,68 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.run = function (controllerData) { // Default behavior if disabling is not enabled. if (!this.disableModules) { - return makeRunningValues(false, [], []); + return this.exitModule(); } // Tablet stylus. - var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightTabletStylusInput" - : "LeftTabletStylusInput"); + var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightTabletStylusInput" : + "LeftTabletStylusInput"); if (tabletStylusInput) { var tabletReady = tabletStylusInput.isReady(controllerData); if (tabletReady.active) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } // Tablet surface. - var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightWebSurfaceLaserInput" - : "LeftWebSurfaceLaserInput"); + var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightWebSurfaceLaserInput" : + "LeftWebSurfaceLaserInput"); if (overlayLaser) { var overlayLaserReady = overlayLaser.isReady(controllerData); var target = controllerData.rayPicks[this.hand].objectID; if (overlayLaserReady.active && this.pointingAtTablet(target)) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } - // Tablet grabbing. - var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightNearParentingGrabOverlay" - : "LeftNearParentingGrabOverlay"); - if (nearOverlay) { - var nearOverlayReady = nearOverlay.isReady(controllerData); - if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) { - return makeRunningValues(false, [], []); + // Tablet highlight and grabbing. + var tabletHighlight = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"); + if (tabletHighlight) { + var tabletHighlightReady = tabletHighlight.isReady(controllerData); + if (tabletHighlightReady.active) { + return this.exitModule(); + } + } + + // HUD overlay. + if (!controllerData.triggerClicks[this.hand]) { + var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHudOverlayPointer" + : "LeftHudOverlayPointer"); + if (hudLaser) { + var hudLaserReady = hudLaser.isReady(controllerData); + if (hudLaserReady.active) { + return this.exitModule(); + } } } // Teleport. - var teleporter = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightTeleporter" - : "LeftTeleporter"); + var teleporter = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightTeleporter" : + "LeftTeleporter"); if (teleporter) { var teleporterReady = teleporter.isReady(controllerData); if (teleporterReady.active) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } // Other behaviors are disabled. - return makeRunningValues(true, [], []); + return this.runModule(); }; } diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js index 101a3502e1..27fe82ca19 100644 --- a/scripts/system/controllers/controllerModules/mouseHMD.js +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -10,6 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Script, HMD, Reticle, Vec3, Controller */ + (function() { var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); @@ -121,7 +123,7 @@ if (this.mouseActivity.expired(now) || this.triggersPressed(controllerData, now) || !hmdActive) { if (!hmdActive) { Reticle.visible = true; - } else { + } else { Reticle.visible = false; } diff --git a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js index ac57c8691f..59a68d98a4 100644 --- a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js +++ b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js @@ -12,7 +12,7 @@ /* jslint bitwise: true */ -/* global Script, print, Entities, Picks, HMD, Controller, MyAvatar, isInEditMode*/ +/* global Script, print, Entities, Messages, Picks, HMD, MyAvatar, isInEditMode, DISPATCHER_PROPERTIES */ (function() { @@ -46,11 +46,7 @@ var targetEntityID = pickResult.objectID; if (this.highlightedEntity !== targetEntityID) { - var targetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); if (this.highlightedEntity) { dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity); diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index a8de76aebd..5ced6080a2 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -10,7 +10,8 @@ propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid, + DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -62,12 +63,12 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var grabbableData = getGrabbableData(targetProps); - this.ignoreIK = grabbableData.ignoreIK; - this.kinematicGrab = grabbableData.kinematic; + this.grabFollowsController = grabbableData.grabFollowsController; + this.kinematicGrab = grabbableData.grabKinematic; var handRotation; var handPosition; - if (this.ignoreIK) { + if (this.grabFollowsController) { var controllerID = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var controllerLocation = getControllerWorldLocation(controllerID, false); @@ -99,7 +100,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); ttl: ACTION_TTL, kinematic: this.kinematicGrab, kinematicSetVelocity: true, - ignoreIK: this.ignoreIK + ignoreIK: this.grabFollowsController }); if (this.actionID === Uuid.NULL) { this.actionID = null; @@ -136,7 +137,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); ttl: ACTION_TTL, kinematic: this.kinematicGrab, kinematicSetVelocity: true, - ignoreIK: this.ignoreIK + ignoreIK: this.grabFollowsController }); if (success) { this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); @@ -238,7 +239,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { var cloneID = cloneEntity(targetProps); - var cloneProps = Entities.getEntityProperties(cloneID); + var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); this.targetEntityID = cloneID; this.startNearGrabAction(controllerData, cloneProps); } else { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index cc88371441..f354067a77 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -12,7 +12,7 @@ findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity, - distanceBetweenEntityLocalPositionAndBoundingBox, GRAB_POINT_SPHERE_OFFSET + distanceBetweenEntityLocalPositionAndBoundingBox, getGrabbableData, getGrabPointSphereOffset, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -28,16 +28,8 @@ Script.include("/~/system/libraries/controllers.js"); var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral function getGrabOffset(handController) { - var offset = GRAB_POINT_SPHERE_OFFSET; - if (handController === Controller.Standard.LeftHand) { - offset = { - x: -GRAB_POINT_SPHERE_OFFSET.x, - y: GRAB_POINT_SPHERE_OFFSET.y, - z: GRAB_POINT_SPHERE_OFFSET.z - }; - } - - offset.y = -GRAB_POINT_SPHERE_OFFSET.y; + var offset = getGrabPointSphereOffset(handController, true); + offset.y = -offset.y; return Vec3.multiply(MyAvatar.sensorToWorldScale, offset); } @@ -101,6 +93,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.startNearParentingGrabEntity = function (controllerData, targetProps) { + var grabData = getGrabbableData(targetProps); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); unhighlightTargetEntity(this.targetEntityID); this.highlightedEntity = null; @@ -111,12 +104,11 @@ Script.include("/~/system/libraries/controllers.js"); Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); var handJointIndex; - // if (this.ignoreIK) { - // handJointIndex = this.controllerJointIndex; - // } else { - // handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - // } - handJointIndex = getControllerJointIndex(this.hand); + if (grabData.grabFollowsController) { + handJointIndex = getControllerJointIndex(this.hand); + } else { + handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(targetProps.id, "startNearGrab", args); @@ -157,20 +149,12 @@ Script.include("/~/system/libraries/controllers.js"); this.hapticTargetID = null; var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; if (this.thisHandIsParent(props) && !this.robbed) { - if (this.previousParentID[this.targetEntityID] === Uuid.NULL || this.previousParentID === undefined) { - Entities.editEntity(this.targetEntityID, { - parentID: this.previousParentID[this.targetEntityID], - parentJointIndex: this.previousParentJointIndex[this.targetEntityID] - }); - } else { - // we're putting this back as a child of some other parent, so zero its velocity - Entities.editEntity(this.targetEntityID, { - parentID: this.previousParentID[this.targetEntityID], - parentJointIndex: this.previousParentJointIndex[this.targetEntityID], - localVelocity: {x: 0, y: 0, z: 0}, - localAngularVelocity: {x: 0, y: 0, z: 0} - }); - } + Entities.editEntity(this.targetEntityID, { + parentID: this.previousParentID[this.targetEntityID], + parentJointIndex: this.previousParentJointIndex[this.targetEntityID], + localVelocity: {x: 0, y: 0, z: 0}, + localAngularVelocity: {x: 0, y: 0, z: 0} + }); } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; @@ -368,7 +352,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.cloneAllowed) { var cloneID = cloneEntity(targetProps); if (cloneID !== null) { - var cloneProps = Entities.getEntityProperties(cloneID); + var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); this.grabbing = true; this.targetEntityID = cloneID; this.startNearParentingGrabEntity(controllerData, cloneProps); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 763a0a0a27..9bddeb236a 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -46,6 +46,10 @@ Script.include("/~/system/libraries/utils.js"); return this.getOtherModule().thisHandIsParent(props); }; + this.isGrabbedThingVisible = function() { + return Overlays.getProperty(this.grabbedThingID, "visible"); + }; + this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== MyAvatar.SELF_ID) { return false; @@ -198,7 +202,7 @@ Script.include("/~/system/libraries/utils.js"); }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { + if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) || !this.isGrabbedThingVisible()) { this.endNearParentingGrabOverlay(); this.robbed = false; return makeRunningValues(false, [], []); diff --git a/scripts/system/controllers/controllerModules/nearTabletHighlight.js b/scripts/system/controllers/controllerModules/nearTabletHighlight.js new file mode 100644 index 0000000000..c24464ab38 --- /dev/null +++ b/scripts/system/controllers/controllerModules/nearTabletHighlight.js @@ -0,0 +1,121 @@ +// +// nearTabletHighlight.js +// +// Highlight the tablet if a hand is near enough to grab it and it isn't grabbed. +// +// Created by David Rowe on 28 Aug 2018. +// 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 +// + +/* global LEFT_HAND, RIGHT_HAND, makeDispatcherModuleParameters, makeRunningValues, enableDispatcherModule, + * disableDispatcherModule */ + +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); + +(function () { + + "use strict"; + + var TABLET_GRABBABLE_SELECTION_NAME = "tabletGrabbableSelection"; + var TABLET_GRABBABLE_SELECTION_STYLE = { + outlineUnoccludedColor: { red: 0, green: 180, blue: 239 }, // #00b4ef + outlineUnoccludedAlpha: 1, + outlineOccludedColor: { red: 0, green: 0, blue: 0 }, + outlineOccludedAlpha: 0, + fillUnoccludedColor: { red: 0, green: 0, blue: 0 }, + fillUnoccludedAlpha: 0, + fillOccludedColor: { red: 0, green: 0, blue: 0 }, + fillOccludedAlpha: 0, + outlineWidth: 4, + isOutlineSmooth: false + }; + + var isTabletNearGrabbable = [false, false]; + var isTabletHighlighted = false; + + function setTabletNearGrabbable(hand, enabled) { + if (enabled === isTabletNearGrabbable[hand]) { + return; + } + + isTabletNearGrabbable[hand] = enabled; + + if (isTabletNearGrabbable[LEFT_HAND] || isTabletNearGrabbable[RIGHT_HAND]) { + if (!isTabletHighlighted) { + Selection.addToSelectedItemsList(TABLET_GRABBABLE_SELECTION_NAME, "overlay", HMD.tabletID); + isTabletHighlighted = true; + } + } else { + if (isTabletHighlighted) { + Selection.removeFromSelectedItemsList(TABLET_GRABBABLE_SELECTION_NAME, "overlay", HMD.tabletID); + isTabletHighlighted = false; + } + } + } + + function NearTabletHighlight(hand) { + this.hand = hand; + + this.parameters = makeDispatcherModuleParameters( + 95, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100 + ); + + this.isNearTablet = function (controllerData) { + return HMD.tabletID && controllerData.nearbyOverlayIDs[this.hand].indexOf(HMD.tabletID) !== -1; + }; + + this.isReady = function (controllerData) { + if (this.isNearTablet(controllerData)) { + return makeRunningValues(true, [], []); + } + setTabletNearGrabbable(this.hand, false); + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData) { + if (!this.isNearTablet(controllerData)) { + setTabletNearGrabbable(this.hand, false); + return makeRunningValues(false, [], []); + } + + if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand]) { + setTabletNearGrabbable(this.hand, false); + return makeRunningValues(false, [], []); + } + + setTabletNearGrabbable(this.hand, true); + return makeRunningValues(true, [], []); + }; + } + + var leftNearTabletHighlight = new NearTabletHighlight(LEFT_HAND); + var rightNearTabletHighlight = new NearTabletHighlight(RIGHT_HAND); + enableDispatcherModule("LeftNearTabletHighlight", leftNearTabletHighlight); + enableDispatcherModule("RightNearTabletHighlight", rightNearTabletHighlight); + + function onDisplayModeChanged() { + if (HMD.active) { + Selection.enableListHighlight(TABLET_GRABBABLE_SELECTION_NAME, TABLET_GRABBABLE_SELECTION_STYLE); + } else { + Selection.disableListHighlight(TABLET_GRABBABLE_SELECTION_NAME); + Selection.clearSelectedItemsList(TABLET_GRABBABLE_SELECTION_NAME); + } + } + HMD.displayModeChanged.connect(onDisplayModeChanged); + HMD.mountedChanged.connect(onDisplayModeChanged); + onDisplayModeChanged(); + + function cleanUp() { + disableDispatcherModule("LeftNearTabletHighlight"); + disableDispatcherModule("RightNearTabletHighlight"); + Selection.disableListHighlight(TABLET_GRABBABLE_SELECTION_NAME); + } + Script.scriptEnding.connect(cleanUp); + +}()); diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 6a9cd9fbcd..f4e39cfbb9 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); function entityWantsNearTrigger(props) { var grabbableData = getGrabbableData(props); - return grabbableData.triggerable || grabbableData.wantsTrigger; + return grabbableData.triggerable; } function NearTriggerEntity(hand) { diff --git a/scripts/system/controllers/controllerModules/scaleEntity.js b/scripts/system/controllers/controllerModules/scaleEntity.js index 9d54eef98e..24c05a1394 100644 --- a/scripts/system/controllers/controllerModules/scaleEntity.js +++ b/scripts/system/controllers/controllerModules/scaleEntity.js @@ -7,7 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Vec3, MyAvatar, RIGHT_HAND */ +/* global Script, Vec3, MyAvatar, Entities, RIGHT_HAND */ (function() { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index 0ca6cd1b04..57066fb2dd 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -5,11 +5,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - enableDispatcherModule, disableDispatcherModule, makeRunningValues, - Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, - getEnabledModuleByName, Pointers, Picks, PickType +/* global Script, MyAvatar, Controller, Uuid, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, + makeRunningValues, Vec3, makeDispatcherModuleParameters, Overlays, HMD, Settings, getEnabledModuleByName, Pointers, + Picks, PickType */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -63,7 +61,13 @@ Script.include("/~/system/libraries/controllers.js"); var farGrabModuleName = this.hand === RIGHT_HAND ? "RightFarActionGrabEntity" : "LeftFarActionGrabEntity"; var farGrabModule = getEnabledModuleByName(farGrabModuleName); var farGrabModuleReady = farGrabModule ? farGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); - return grabOverlayModuleReady.active || farGrabModuleReady.active || grabEntityModuleReady.active; + var nearTabletHighlightModuleName = + this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"; + var nearTabletHighlightModule = getEnabledModuleByName(nearTabletHighlightModuleName); + var nearTabletHighlightModuleReady = nearTabletHighlightModule + ? nearTabletHighlightModule.isReady(controllerData) : makeRunningValues(false, [], []); + return grabOverlayModuleReady.active || farGrabModuleReady.active || grabEntityModuleReady.active + || nearTabletHighlightModuleReady.active; }; this.overlayLaserActive = function(controllerData) { @@ -200,7 +204,7 @@ Script.include("/~/system/libraries/controllers.js"); Overlays.hoverEnterOverlay.connect(mouseHoverEnter); Overlays.hoverLeaveOverlay.connect(mouseHoverLeave); - Overlays.mousePressOnOverlay.connect(mousePress); + Overlays.mousePressOnOverlay.connect(mousePress); this.cleanup = function () { leftTabletStylusInput.cleanup(); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index bf5022cdaf..44aa04b497 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -10,7 +10,7 @@ /* jslint bitwise: true */ -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, +/* global Script, Entities, MyAvatar, Controller, Quat, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3, HMD, Uuid, AvatarList, Picks, Pointers, PickType */ @@ -358,9 +358,9 @@ Script.include("/~/system/libraries/controllers.js"); var sensorToWorldScale = MyAvatar.getSensorToWorldScale(); - var radius = capsuleData.radius / sensorToWorldScale; - var height = (Vec3.distance(capsuleData.start, capsuleData.end) + (capsuleData.radius * 2.0)) / sensorToWorldScale; - var capsuleRatio = 10.0 * radius / height; + var diameter = 2.0 * capsuleData.radius / sensorToWorldScale; + var height = (Vec3.distance(capsuleData.start, capsuleData.end) + diameter) / sensorToWorldScale; + var capsuleRatio = 5.0 * diameter / height; var offset = _this.pickHeightOffset * capsuleRatio; _this.teleportHandCollisionPick = Picks.createPick(PickType.Collision, { @@ -370,9 +370,9 @@ Script.include("/~/system/libraries/controllers.js"); shape: { shapeType: "capsule-y", dimensions: { - x: radius * 2.0, - y: height - (radius * 2.0), - z: radius * 2.0 + x: diameter, + y: height, + z: diameter } }, position: { x: 0, y: offset + height * 0.5, z: 0 }, @@ -386,9 +386,9 @@ Script.include("/~/system/libraries/controllers.js"); shape: { shapeType: "capsule-y", dimensions: { - x: radius * 2.0, - y: height - (radius * 2.0), - z: radius * 2.0 + x: diameter, + y: height, + z: diameter } }, position: { x: 0, y: offset + height * 0.5, z: 0 }, @@ -680,8 +680,8 @@ Script.include("/~/system/libraries/controllers.js"); this.teleportLocked = function () { // Lock teleport if in advanced movement mode and have just transitioned from pressing a direction button. - return Controller.getValue(Controller.Hardware.Application.AdvancedMovement) - && (_this.axisButtonStateX !== 0 || _this.axisButtonStateY !== 0); + return Controller.getValue(Controller.Hardware.Application.AdvancedMovement) && + (_this.axisButtonStateX !== 0 || _this.axisButtonStateY !== 0); }; this.buttonPress = function (value) { @@ -701,6 +701,10 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function(controllerData, deltaTime) { + if (Window.interstitialModeEnabled && !Window.isPhysicsEnabled()) { + return makeRunningValues(false, [], []); + } + var otherModule = this.getOtherModule(); if (!this.disabled && this.buttonValue !== 0 && !otherModule.active) { this.active = true; diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 2412e2fa1c..d2cb7fffd1 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -5,11 +5,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, - makeRunningValues, Messages, Quat, Vec3, makeDispatcherModuleParameters, Overlays, ZERO_VEC, HMD, - INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, ContextOverlay, Picks, makeLaserParams +/* global Script, Entities, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + makeDispatcherModuleParameters, Overlays, HMD, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, + ContextOverlay, Picks, makeLaserParams, Settings, MyAvatar, RIGHT_HAND, LEFT_HAND, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -20,6 +18,7 @@ Script.include("/~/system/libraries/controllers.js"); this.hand = hand; this.otherHand = this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND; this.running = false; + this.ignoredObjects = []; this.parameters = makeDispatcherModuleParameters( 160, @@ -60,6 +59,13 @@ Script.include("/~/system/libraries/controllers.js"); } } } + + var nearTabletHighlightModule = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"); + if (nearTabletHighlightModule) { + return nearTabletHighlightModule.isNearTablet(controllerData); + } + return false; }; @@ -67,6 +73,48 @@ Script.include("/~/system/libraries/controllers.js"); return this.hand === RIGHT_HAND ? leftOverlayLaserInput : rightOverlayLaserInput; }; + this.addObjectToIgnoreList = function(controllerData) { + if (Window.interstitialModeEnabled && !Window.isPhysicsEnabled()) { + var intersection = controllerData.rayPicks[this.hand]; + var objectID = intersection.objectID; + + if (intersection.type === Picks.INTERSECTED_OVERLAY) { + var overlayIndex = this.ignoredObjects.indexOf(objectID); + + var overlayName = Overlays.getProperty(objectID, "name"); + if (overlayName !== "Loading-Destination-Card-Text" && overlayName !== "Loading-Destination-Card-GoTo-Image" && + overlayName !== "Loading-Destination-Card-GoTo-Image-Hover") { + var data = { + action: 'add', + id: objectID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)); + this.ignoredObjects.push(objectID); + } + } else if (intersection.type === Picks.INTERSECTED_ENTITY) { + var entityIndex = this.ignoredObjects.indexOf(objectID); + var data = { + action: 'add', + id: objectID + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)); + this.ignoredObjects.push(objectID); + } + } + }; + + this.restoreIgnoredObjects = function() { + for (var index = 0; index < this.ignoredObjects.length; index++) { + var data = { + action: 'remove', + id: this.ignoredObjects[index] + }; + Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data)); + } + + this.ignoredObjects = []; + }; + this.isPointingAtTriggerable = function(controllerData, triggerPressed, checkEntitiesOnly) { // allow pointing at tablet, unlocked web entities, or web overlays automatically without pressing trigger, // but for pointing at locked web entities or non-web overlays user must be pressing trigger @@ -82,7 +130,7 @@ Script.include("/~/system/libraries/controllers.js"); return overlayType === "web3d" || triggerPressed; } } else if (intersection.type === Picks.INTERSECTED_ENTITY) { - var entityProperties = Entities.getEntityProperties(objectID); + var entityProperties = Entities.getEntityProperties(objectID, DISPATCHER_PROPERTIES); var entityType = entityProperties.type; var isLocked = entityProperties.locked; return entityType === "Web" && (!isLocked || triggerPressed); @@ -91,8 +139,9 @@ Script.include("/~/system/libraries/controllers.js"); }; this.deleteContextOverlay = function() { - var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightFarActionGrabEntity" : "LeftFarActionGrabEntity"); + var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightFarActionGrabEntity" : + "LeftFarActionGrabEntity"); if (farGrabModule) { var entityWithContextOverlay = farGrabModule.entityWithContextOverlay; @@ -131,6 +180,10 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(true, [], []); } } + + if (Window.interstitialModeEnabled && Window.isPhysicsEnabled()) { + this.restoreIgnoredObjects(); + } return makeRunningValues(false, [], []); }; @@ -143,6 +196,7 @@ Script.include("/~/system/libraries/controllers.js"); var allowThisModule = !otherModuleRunning && !grabModuleNeedsToRun; var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; var laserOn = isTriggerPressed || this.parameters.handLaser.allwaysOn; + this.addObjectToIgnoreList(controllerData); if (allowThisModule) { if (isTriggerPressed && !this.isPointingAtTriggerable(controllerData, isTriggerPressed, true)) { // if trigger is down + not pointing at a web entity, keep running web surface laser diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 6899577de2..999e448171 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Script, Menu */ + var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", @@ -19,8 +21,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", - // "controllerModules/farActionGrabEntity.js", - // "controllerModules/farParentGrabEntity.js", + "controllerModules/farActionGrabEntityDynOnly.js", + "controllerModules/farParentGrabEntity.js", "controllerModules/stylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", @@ -35,28 +37,26 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/scaleEntity.js", "controllerModules/highlightNearbyEntities.js", "controllerModules/nearGrabHyperLinkEntity.js", - "controllerModules/mouseHighlightEntities.js" + "controllerModules/mouseHighlightEntities.js", + "controllerModules/nearTabletHighlight.js" ]; -if (Settings.getValue("useFarGrabJoints", false)) { - CONTOLLER_SCRIPTS.push("controllerModules/farActionGrabEntityDynOnly.js"); - CONTOLLER_SCRIPTS.push("controllerModules/farParentGrabEntity.js"); -} else { - CONTOLLER_SCRIPTS.push("controllerModules/farActionGrabEntity.js"); -} - var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; function runDefaultsTogether() { for (var j in CONTOLLER_SCRIPTS) { - Script.include(CONTOLLER_SCRIPTS[j]); + if (CONTOLLER_SCRIPTS.hasOwnProperty(j)) { + Script.include(CONTOLLER_SCRIPTS[j]); + } } } function runDefaultsSeparately() { for (var i in CONTOLLER_SCRIPTS) { - Script.load(CONTOLLER_SCRIPTS[i]); + if (CONTOLLER_SCRIPTS.hasOwnProperty(i)) { + Script.load(CONTOLLER_SCRIPTS[i]); + } } } diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 48229ac9d9..a78a2971e9 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -14,9 +14,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global MyAvatar, Entities, Script, Camera, Vec3, Reticle, Overlays, getEntityCustomData, Messages, Quat, Controller, - isInEditMode, HMD entityIsGrabbable, Picks, PickType, Pointers, unhighlightTargetEntity*/ - +/* global MyAvatar, Entities, Script, HMD, Camera, Vec3, Reticle, Overlays, getEntityCustomData, Messages, Quat, Controller, + isInEditMode, entityIsGrabbable, Picks, PickType, Pointers, unhighlightTargetEntity, DISPATCHER_PROPERTIES, + entityIsGrabbable, entityIsEquipped, getMainTabletIDs +*/ +/* jslint bitwise: true */ (function() { // BEGIN LOCAL_SCOPE @@ -37,7 +39,6 @@ var IDENTITY_QUAT = { z: 0, w: 0 }; -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with handControllerGrab.js var DEFAULT_GRABBABLE_DATA = { grabbable: true, @@ -339,16 +340,14 @@ Grabber.prototype.pressEvent = function(event) { return; } - var props = Entities.getEntityProperties(pickResults.objectID, ["dynamic", "userData", "locked", "type"]); + var props = Entities.getEntityProperties(pickResults.objectID, DISPATCHER_PROPERTIES); var isDynamic = props.dynamic; - var isGrabbable = props.grabbable; if (!entityIsGrabbable(props)) { // only grab grabbable objects return; } - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, pickResults.objectID, DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { + if (!props.grab.grabbable) { return; } @@ -359,7 +358,7 @@ Grabber.prototype.pressEvent = function(event) { mouse.startDrag(event); var clickedEntity = pickResults.objectID; - var entityProperties = Entities.getEntityProperties(clickedEntity); + var entityProperties = Entities.getEntityProperties(clickedEntity, DISPATCHER_PROPERTIES); this.startPosition = entityProperties.position; this.lastRotation = entityProperties.rotation; this.madeDynamic = false; @@ -484,7 +483,7 @@ Grabber.prototype.moveEvent = function(event) { Grabber.prototype.moveEventProcess = function() { this.moveEventTimer = null; // see if something added/restored gravity - var entityProperties = Entities.getEntityProperties(this.entityID); + var entityProperties = Entities.getEntityProperties(this.entityID, DISPATCHER_PROPERTIES); if (!entityProperties || !entityProperties.gravity || HMD.active) { return; } diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 97a24cb3f2..c706d054c1 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -11,7 +11,7 @@ /* jslint bitwise: true */ -/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities +/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities, RayPick */ (function () { @@ -22,8 +22,8 @@ var MSECONDS_AFTER_LOAD = 2000; var updateFingerWithIndex = 0; var untouchableEntities = []; - - // Keys to access finger data + + // Keys to access finger data var fingerKeys = ["pinky", "ring", "middle", "index", "thumb"]; // Additionally close the hands to achieve a grabbing effect @@ -47,7 +47,7 @@ left: new Palm(), right: new Palm() }; - + var handJointNames = {left: "LeftHand", right: "RightHand"}; // Store which fingers are touching - if all false restate the default poses diff --git a/scripts/system/controllers/squeezeHands.js b/scripts/system/controllers/squeezeHands.js index c9de473e28..69f44f46a9 100644 --- a/scripts/system/controllers/squeezeHands.js +++ b/scripts/system/controllers/squeezeHands.js @@ -11,6 +11,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Script, MyAvatar, Messages, Controller */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -20,7 +21,7 @@ var lastRightTrigger = 0; var leftHandOverlayAlpha = 0; var rightHandOverlayAlpha = 0; -var CONTROLLER_DEAD_SPOT = 0.25; +// var CONTROLLER_DEAD_SPOT = 0.25; var TRIGGER_SMOOTH_TIMESCALE = 0.1; var OVERLAY_RAMP_RATE = 8.0; @@ -42,9 +43,9 @@ function clamp(val, min, max) { return Math.min(Math.max(val, min), max); } -function normalizeControllerValue(val) { - return clamp((val - CONTROLLER_DEAD_SPOT) / (1 - CONTROLLER_DEAD_SPOT), 0, 1); -} +// function normalizeControllerValue(val) { +// return clamp((val - CONTROLLER_DEAD_SPOT) / (1 - CONTROLLER_DEAD_SPOT), 0, 1); +// } function lerp(a, b, alpha) { return a * (1 - alpha) + b * alpha; diff --git a/scripts/system/controllers/touchControllerConfiguration.js b/scripts/system/controllers/touchControllerConfiguration.js index f22252f646..991b77b8af 100644 --- a/scripts/system/controllers/touchControllerConfiguration.js +++ b/scripts/system/controllers/touchControllerConfiguration.js @@ -8,7 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals TOUCH_CONTROLLER_CONFIGURATION_LEFT:true TOUCH_CONTROLLER_CONFIGURATION_RIGHT:true */ +/* globals TOUCH_CONTROLLER_CONFIGURATION_LEFT:true, TOUCH_CONTROLLER_CONFIGURATION_RIGHT:true, + Quat, Vec3, Script, MyAvatar, Controller */ /* eslint camelcase: ["error", { "properties": "never" }] */ var leftBaseRotation = Quat.multiply( @@ -22,9 +23,9 @@ var rightBaseRotation = Quat.multiply( // keep these in sync with the values from OculusHelpers.cpp var CONTROLLER_LENGTH_OFFSET = 0.0762; -var CONTROLLER_LATERAL_OFFSET = 0.0381; -var CONTROLLER_VERTICAL_OFFSET = 0.0381; -var CONTROLLER_FORWARD_OFFSET = 0.1524; +// var CONTROLLER_LATERAL_OFFSET = 0.0381; +// var CONTROLLER_VERTICAL_OFFSET = 0.0381; +// var CONTROLLER_FORWARD_OFFSET = 0.1524; var leftBasePosition = Vec3.multiplyQbyV(leftBaseRotation, { x: -CONTROLLER_LENGTH_OFFSET / 2.0, diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js index 60f0b6b88a..09fd8adacc 100644 --- a/scripts/system/controllers/viveControllerConfiguration.js +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -8,11 +8,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals VIVE_CONTROLLER_CONFIGURATION_LEFT:true VIVE_CONTROLLER_CONFIGURATION_RIGHT:true */ +/* globals VIVE_CONTROLLER_CONFIGURATION_LEFT:true, VIVE_CONTROLLER_CONFIGURATION_RIGHT:true, + MyAvatar, Quat, Script, Vec3, Controller */ /* eslint camelcase: ["error", { "properties": "never" }] */ -var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); -var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); +// var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); +// var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); var leftBaseRotation = Quat.multiply( Quat.fromPitchYawRollDegrees(0, 0, 45), @@ -58,10 +59,10 @@ var viveNaturalPosition = { }; var BASE_URL = Script.resourcesPath(); -var TIP_TEXTURE_BASE_URL = BASE_URL + "meshes/controller/vive_tips.fbm/"; +// var TIP_TEXTURE_BASE_URL = BASE_URL + "meshes/controller/vive_tips.fbm/"; var viveModelURL = BASE_URL + "meshes/controller/vive_body.fbx"; -var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx"; +// var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx"; var viveTriggerModelURL = "meshes/controller/vive_trigger.fbx"; VIVE_CONTROLLER_CONFIGURATION_LEFT = { @@ -340,4 +341,3 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { } ] }; - diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 27858722ec..d3e9a475ac 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -12,7 +12,7 @@ /* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, - progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool, OverlaySystemWindow */ + progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow */ (function() { // BEGIN LOCAL_SCOPE @@ -32,7 +32,6 @@ Script.include([ "libraries/gridTool.js", "libraries/entityList.js", "libraries/utils.js", - "particle_explorer/particleExplorerTool.js", "libraries/entityIconOverlayManager.js" ]); @@ -42,6 +41,9 @@ var TITLE_OFFSET = 60; var CREATE_TOOLS_WIDTH = 490; var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942; +var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; +var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; + var createToolsWindow = new CreateWindow( Script.resourcesPath() + "qml/hifi/tablet/EditTools.qml", 'Create Tools', @@ -109,28 +111,6 @@ var entityListTool = new EntityListTool(shouldUseEditTabletApp); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); - - // Update particle explorer - var needToDestroyParticleExplorer = false; - if (selectionManager.selections.length === 1) { - var selectedEntityID = selectionManager.selections[0]; - if (selectedEntityID === selectedParticleEntityID) { - return; - } - var type = Entities.getEntityProperties(selectedEntityID, "type").type; - if (type === "ParticleEffect") { - selectParticleEntity(selectedEntityID); - } else { - needToDestroyParticleExplorer = true; - } - } else { - needToDestroyParticleExplorer = true; - } - - if (needToDestroyParticleExplorer && selectedParticleEntityID !== null) { - selectedParticleEntityID = null; - particleExplorerTool.destroyWebView(); - } }); var KEY_P = 80; //Key code for letter p used for Parenting hotkey. @@ -294,6 +274,202 @@ function checkEditPermissionsAndUpdate() { } } +const DEFAULT_ENTITY_PROPERTIES = { + All: { + description: "", + rotation: { x: 0, y: 0, z: 0, w: 1 }, + collidesWith: "static,dynamic,kinematic,otherAvatar,myAvatar", + collisionSoundURL: "", + cloneable: false, + ignoreIK: true, + canCastShadow: true, + href: "", + script: "", + serverScripts:"", + velocity: { + x: 0, + y: 0, + z: 0 + }, + damping: 0, + angularVelocity: { + x: 0, + y: 0, + z: 0 + }, + angularDamping: 0, + restitution: 0.5, + friction: 0.5, + density: 1000, + gravity: { + x: 0, + y: 0, + z: 0 + }, + acceleration: { + x: 0, + y: 0, + z: 0 + }, + dynamic: false, + }, + Shape: { + shape: "Box", + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + color: { red: 0, green: 180, blue: 239 }, + }, + Text: { + text: "Text", + dimensions: { + x: 0.65, + y: 0.3, + z: 0.01 + }, + textColor: { red: 255, green: 255, blue: 255 }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + lineHeight: 0.06, + faceCamera: false, + }, + Zone: { + dimensions: { + x: 10, + y: 10, + z: 10 + }, + flyingAllowed: true, + ghostingAllowed: true, + filter: "", + keyLightMode: "inherit", + keyLightColor: { red: 255, green: 255, blue: 255 }, + keyLight: { + intensity: 1.0, + direction: { + x: 0.0, + y: -0.707106769084930, // 45 degrees + z: 0.7071067690849304 + }, + castShadows: true + }, + ambientLightMode: "inherit", + ambientLight: { + ambientIntensity: 0.5, + ambientURL: "" + }, + hazeMode: "inherit", + haze: { + hazeRange: 1000, + hazeAltitudeEffect: false, + hazeBaseRef: 0, + hazeColor: { + red: 128, + green: 154, + blue: 179 + }, + hazeBackgroundBlend: 0, + hazeEnableGlare: false, + hazeGlareColor: { + red: 255, + green: 229, + blue: 179 + }, + }, + bloomMode: "inherit" + }, + Model: { + collisionShape: "none", + compoundShapeURL: "", + animation: { + url: "", + running: false, + allowTranslation: false, + loop: true, + hold: false, + currentFrame: 0, + firstFrame: 0, + lastFrame: 100000, + fps: 30.0, + } + }, + Image: { + dimensions: { + x: 0.5385, + y: 0.2819, + z: 0.0092 + }, + shapeType: "box", + collisionless: true, + modelURL: IMAGE_MODEL, + textures: JSON.stringify({ "tex.picture": "" }) + }, + Web: { + dimensions: { + x: 1.6, + y: 0.9, + z: 0.01 + }, + sourceUrl: "https://highfidelity.com/", + dpi: 30, + }, + ParticleEffect: { + lifespan: 1.5, + maxParticles: 10, + textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png", + emitRate: 5.5, + emitSpeed: 0, + speedSpread: 0, + emitDimensions: { x: 0, y: 0, z: 0 }, + emitOrientation: { x: 0, y: 0, z: 0, w: 1 }, + emitterShouldTrail: true, + particleRadius: 0.25, + radiusStart: 0, + radiusFinish: 0.1, + radiusSpread: 0, + particleColor: { + red: 255, + green: 255, + blue: 255 + }, + colorSpread: { + red: 0, + green: 0, + blue: 0 + }, + alpha: 0, + alphaStart: 1, + alphaFinish: 0, + alphaSpread: 0, + emitAcceleration: { + x: 0, + y: 2.5, + z: 0 + }, + accelerationSpread: { + x: 0, + y: 0, + z: 0 + }, + particleSpin: 0, + spinStart: 0, + spinFinish: 0, + spinSpread: 0, + rotateWithEntity: false, + polarStart: 0, + polarFinish: 0, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI + }, + Light: { + color: { red: 255, green: 255, blue: 255 }, + intensity: 5.0, + dimensions: DEFAULT_LIGHT_DIMENSIONS, + falloffRadius: 1.0, + isSpotlight: false, + exponent: 1.0, + cutoff: 75.0, + dimensions: { x: 20, y: 20, z: 20 }, + }, +}; + var toolBar = (function () { var EDIT_SETTING = "io.highfidelity.isEditing"; // for communication with other scripts var that = {}, @@ -303,11 +479,34 @@ var toolBar = (function () { dialogWindow = null, tablet = null; - function createNewEntity(properties) { - var dimensions = properties.dimensions ? properties.dimensions : DEFAULT_DIMENSIONS; + function applyProperties(originalProperties, newProperties) { + for (var key in newProperties) { + originalProperties[key] = newProperties[key]; + } + } + function createNewEntity(requestedProperties) { + var dimensions = requestedProperties.dimensions ? requestedProperties.dimensions : DEFAULT_DIMENSIONS; var position = getPositionToCreateEntity(); var entityID = null; + var properties = {}; + + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.All); + + var type = requestedProperties.type; + if (type === "Box" || type === "Sphere") { + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape); + } else if (type === "Image") { + requestedProperties.type = "Model"; + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Image); + } else { + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]); + } + + // We apply the requested properties first so that they take priority over any default properties. + applyProperties(properties, requestedProperties); + + if (position !== null && position !== undefined) { var direction; if (Camera.mode === "entity" || Camera.mode === "independent") { @@ -345,11 +544,15 @@ var toolBar = (function () { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions); properties.position = position; - if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && - !(properties.type === "Zone" || properties.type === "Light" || properties.type === "ParticleEffect")) { - properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); - } else { - properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); + + if (!properties.grab) { + properties.grab = {}; + if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && + !(properties.type === "Zone" || properties.type === "Light" || properties.type === "ParticleEffect")) { + properties.grab.grabbable = true; + } else { + properties.grab.grabbable = false; + } } SelectionManager.saveProperties(); @@ -359,10 +562,6 @@ var toolBar = (function () { properties: properties }], [], true); - if (properties.type === "ParticleEffect") { - selectParticleEntity(entityID); - } - var POST_ADJUST_ENTITY_TYPES = ["Model"]; if (POST_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box after it has been created and auto-resized. @@ -381,7 +580,7 @@ var toolBar = (function () { Entities.editEntity(entityID, { position: position }); - selectionManager._update(); + selectionManager._update(false, this); } else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) { Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL); } @@ -393,9 +592,9 @@ var toolBar = (function () { properties.type + " would be out of bounds."); } - selectionManager.clearSelections(); + selectionManager.clearSelections(this); entityListTool.sendUpdate(); - selectionManager.setSelections([entityID]); + selectionManager.setSelections([entityID], this); Window.setFocus(); @@ -438,9 +637,9 @@ var toolBar = (function () { function handleNewModelDialogResult(result) { if (result) { - var url = result.textInput; + var url = result.url; var shapeType; - switch (result.comboBox) { + switch (result.collisionShapeIndex) { case SHAPE_TYPE_SIMPLE_HULL: shapeType = "simple-hull"; break; @@ -460,7 +659,7 @@ var toolBar = (function () { shapeType = "none"; } - var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; + var dynamic = result.dynamic !== null ? result.dynamic : DYNAMIC_DEFAULT; if (shapeType === "static-mesh" && dynamic) { // The prompt should prevent this case print("Error: model cannot be both static mesh and dynamic. This should never happen."); @@ -469,6 +668,9 @@ var toolBar = (function () { type: "Model", modelURL: url, shapeType: shapeType, + grab: { + grabbable: result.grabbable + }, dynamic: dynamic, gravity: dynamic ? { x: 0, y: -10, z: 0 } : { x: 0, y: 0, z: 0 } }); @@ -543,7 +745,7 @@ var toolBar = (function () { } deletedEntityTimer = Script.setTimeout(function () { if (entitiesToDelete.length > 0) { - selectionManager.removeEntities(entitiesToDelete); + selectionManager.removeEntities(entitiesToDelete, this); } entityListTool.removeEntities(entitiesToDelete, selectionManager.selections); entitiesToDelete = []; @@ -648,159 +850,48 @@ var toolBar = (function () { addButton("newCubeButton", function () { createNewEntity({ type: "Box", - dimensions: DEFAULT_DIMENSIONS, - color: { - red: 255, - green: 0, - blue: 0 - } }); }); addButton("newSphereButton", function () { createNewEntity({ type: "Sphere", - dimensions: DEFAULT_DIMENSIONS, - color: { - red: 255, - green: 0, - blue: 0 - } }); }); addButton("newLightButton", function () { createNewEntity({ type: "Light", - dimensions: DEFAULT_LIGHT_DIMENSIONS, - isSpotlight: false, - color: { - red: 150, - green: 150, - blue: 150 - }, - - constantAttenuation: 1, - linearAttenuation: 0, - quadraticAttenuation: 0, - exponent: 0, - cutoff: 180 // in degrees }); }); addButton("newTextButton", function () { createNewEntity({ type: "Text", - dimensions: { - x: 0.65, - y: 0.3, - z: 0.01 - }, - backgroundColor: { - red: 64, - green: 64, - blue: 64 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - text: "some text", - lineHeight: 0.06 }); }); addButton("newImageButton", function () { - var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; - var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; createNewEntity({ - type: "Model", - dimensions: { - x: 0.5385, - y: 0.2819, - z: 0.0092 - }, - shapeType: "box", - collisionless: true, - modelURL: IMAGE_MODEL, - textures: JSON.stringify({ "tex.picture": DEFAULT_IMAGE }) + type: "Image", }); }); addButton("newWebButton", function () { createNewEntity({ type: "Web", - dimensions: { - x: 1.6, - y: 0.9, - z: 0.01 - }, - sourceUrl: "https://highfidelity.com/" }); }); addButton("newZoneButton", function () { createNewEntity({ type: "Zone", - dimensions: { - x: 10, - y: 10, - z: 10 - } }); }); addButton("newParticleButton", function () { createNewEntity({ type: "ParticleEffect", - isEmitting: true, - emitterShouldTrail: true, - color: { - red: 200, - green: 200, - blue: 200 - }, - colorSpread: { - red: 0, - green: 0, - blue: 0 - }, - colorStart: { - red: 200, - green: 200, - blue: 200 - }, - colorFinish: { - red: 0, - green: 0, - blue: 0 - }, - emitAcceleration: { - x: -0.5, - y: 2.5, - z: -0.5 - }, - accelerationSpread: { - x: 0.5, - y: 1, - z: 0.5 - }, - emitRate: 5.5, - emitSpeed: 0, - speedSpread: 0, - lifespan: 1.5, - maxParticles: 10, - particleRadius: 0.25, - radiusStart: 0, - radiusFinish: 0.1, - radiusSpread: 0, - alpha: 0, - alphaStart: 1, - alphaFinish: 0, - polarStart: 0, - polarFinish: 0, - textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png" }); }); @@ -859,7 +950,7 @@ var toolBar = (function () { gridTool.setVisible(false); grid.setEnabled(false); propertiesTool.setVisible(false); - selectionManager.clearSelections(); + selectionManager.clearSelections(this); cameraManager.disable(); selectionDisplay.disableTriggerMapping(); tablet.landscape = false; @@ -987,7 +1078,7 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) { var entity = entityIconOverlayManager.findEntity(data.overlayID); if (entity !== null) { - selectionManager.setSelections([entity]); + selectionManager.setSelections([entity], this); } } } @@ -1127,14 +1218,14 @@ function mouseClickEvent(event) { var result, properties, tabletClicked; if (isActive && event.isLeftButton) { result = findClickedEntity(event); - tabletOrEditHandleClicked = wasTabletOrEditHandleClicked(event); + var tabletOrEditHandleClicked = wasTabletOrEditHandleClicked(event); if (tabletOrEditHandleClicked) { return; } if (result === null || result === undefined) { if (!event.isShifted) { - selectionManager.clearSelections(); + selectionManager.clearSelections(this); } return; } @@ -1178,17 +1269,10 @@ function mouseClickEvent(event) { orientation = MyAvatar.orientation; intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation)); - if (event.isShifted) { - particleExplorerTool.destroyWebView(); - } - if (properties.type !== "ParticleEffect") { - particleExplorerTool.destroyWebView(); - } - if (!event.isShifted) { - selectionManager.setSelections([foundEntity]); + selectionManager.setSelections([foundEntity], this); } else { - selectionManager.addEntity(foundEntity, true); + selectionManager.addEntity(foundEntity, true, this); } if (wantDebug) { @@ -1247,7 +1331,7 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "Edit", menuItemName: "Redo", - shortcutKey: 'Ctrl+Shift+Z', + shortcutKey: 'Ctrl+Y', position: 1, }); @@ -1405,7 +1489,6 @@ Script.scriptEnding.connect(function () { Settings.setValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - Settings.setValue(SETTING_EDIT_PREFIX + MENU_CREATE_ENTITIES_GRABBABLE, Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE)); Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LARGE, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LARGE)); Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_SMALL, Menu.isOptionChecked(MENU_ALLOW_SELECTION_SMALL)); Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LIGHTS)); @@ -1458,7 +1541,7 @@ function insideBox(center, dimensions, point) { (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); } -function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { +function selectAllEntitiesInCurrentSelectionBox(keepIfTouching) { if (selectionManager.hasSelection()) { // Get all entities touching the bounding box of the current selection var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, @@ -1487,7 +1570,7 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { } } } - selectionManager.setSelections(entities); + selectionManager.setSelections(entities, this); } } @@ -1604,8 +1687,6 @@ function deleteSelectedEntities() { if (SelectionManager.hasSelection()) { var deletedIDs = []; - selectedParticleEntityID = null; - particleExplorerTool.destroyWebView(); SelectionManager.saveProperties(); var savedProperties = []; var newSortedSelection = sortSelectedEntities(selectionManager.selections); @@ -1627,7 +1708,7 @@ function deleteSelectedEntities() { } if (savedProperties.length > 0) { - SelectionManager.clearSelections(); + SelectionManager.clearSelections(this); pushCommandForSelections([], savedProperties); entityListTool.deleteEntities(deletedIDs); } @@ -1644,7 +1725,7 @@ function toggleSelectedEntitiesLocked() { }); } entityListTool.sendUpdate(); - selectionManager._update(); + selectionManager._update(false, this); } } @@ -1658,7 +1739,7 @@ function toggleSelectedEntitiesVisible() { }); } entityListTool.sendUpdate(); - selectionManager._update(); + selectionManager._update(false, this); } } @@ -1706,7 +1787,7 @@ function onPromptTextChanged(prompt) { } } -function handeMenuEvent(menuItem) { +function handleMenuEvent(menuItem) { if (menuItem === "Allow Selecting of Small Models") { allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); } else if (menuItem === "Allow Selecting of Large Models") { @@ -1739,13 +1820,15 @@ function handeMenuEvent(menuItem) { Window.promptAsync("URL of SVO to import", ""); } } else if (menuItem === "Select All Entities In Box") { - selectAllEtitiesInCurrentSelectionBox(false); + selectAllEntitiesInCurrentSelectionBox(false); } else if (menuItem === "Select All Entities Touching Box") { - selectAllEtitiesInCurrentSelectionBox(true); + selectAllEntitiesInCurrentSelectionBox(true); } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + } else if (menuItem === MENU_CREATE_ENTITIES_GRABBABLE) { + Settings.setValue(SETTING_EDIT_PREFIX + menuItem, Menu.isOptionChecked(menuItem)); } tooltip.show(false); } @@ -1853,7 +1936,7 @@ function importSVO(importURL) { } if (isActive) { - selectionManager.setSelections(pastedEntityIDs); + selectionManager.setSelections(pastedEntityIDs, this); } } else { Window.notifyEditError("Can't import entities: entities would be out of bounds."); @@ -1871,7 +1954,7 @@ function importSVO(importURL) { } Window.svoImportRequested.connect(importSVO); -Menu.menuItemEvent.connect(handeMenuEvent); +Menu.menuItemEvent.connect(handleMenuEvent); var keyPressEvent = function (event) { if (isActive) { @@ -1901,7 +1984,7 @@ function deleteKey(value) { } function deselectKey(value) { if (value === 0) { // on release - selectionManager.clearSelections(); + selectionManager.clearSelections(this); } } function toggleKey(value) { @@ -1925,14 +2008,6 @@ function gridKey(value) { } } } -var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey); -mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); -mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); -mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); -mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); -mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); - function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { var children = parentData.children; @@ -2033,14 +2108,14 @@ var DELETED_ENTITY_MAP = {}; function applyEntityProperties(data) { var editEntities = data.editEntities; var selectedEntityIDs = []; - var selectEdits = data.createEntities.length == 0 || !data.selectCreated; - var i, entityID; + var selectEdits = data.createEntities.length === 0 || !data.selectCreated; + var i, entityID, entityProperties; for (i = 0; i < editEntities.length; i++) { - var entityID = editEntities[i].entityID; + entityID = editEntities[i].entityID; if (DELETED_ENTITY_MAP[entityID] !== undefined) { entityID = DELETED_ENTITY_MAP[entityID]; } - var entityProperties = editEntities[i].properties; + entityProperties = editEntities[i].properties; if (entityProperties !== null) { Entities.editEntity(entityID, entityProperties); } @@ -2050,7 +2125,7 @@ function applyEntityProperties(data) { } for (i = 0; i < data.createEntities.length; i++) { entityID = data.createEntities[i].entityID; - var entityProperties = data.createEntities[i].properties; + entityProperties = data.createEntities[i].properties; var newEntityID = Entities.addEntity(entityProperties); recursiveAdd(newEntityID, data.createEntities[i]); DELETED_ENTITY_MAP[entityID] = newEntityID; @@ -2069,7 +2144,7 @@ function applyEntityProperties(data) { // We might be getting an undo while edit.js is disabled. If that is the case, don't set // our selections, causing the edit widgets to display. if (isActive) { - selectionManager.setSelections(selectedEntityIDs); + selectionManager.setSelections(selectedEntityIDs, this); } } @@ -2186,9 +2261,17 @@ var PropertiesTool = function (opts) { }); } + that.setSpaceMode = function(spaceMode) { + emitScriptEvent({ + type: 'setSpaceMode', + spaceMode: spaceMode + }) + }; + function updateSelections(selectionUpdated) { var data = { - type: 'update' + type: 'update', + spaceMode: selectionDisplay.getSpaceMode() }; if (selectionUpdated) { @@ -2218,9 +2301,14 @@ var PropertiesTool = function (opts) { if (entity.properties.rotation !== undefined) { entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); } + if (entity.properties.localRotation !== undefined) { + entity.properties.localRotation = Quat.safeEulerAngles(entity.properties.localRotation); + } + if (entity.properties.emitOrientation !== undefined) { + entity.properties.emitOrientation = Quat.safeEulerAngles(entity.properties.emitOrientation); + } if (entity.properties.keyLight !== undefined && entity.properties.keyLight.direction !== undefined) { - entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, - Vec3.toPolar(entity.properties.keyLight.direction)); + entity.properties.keyLight.direction = Vec3.toPolar(entity.properties.keyLight.direction); entity.properties.keyLight.direction.z = 0.0; } selections.push(entity); @@ -2252,27 +2340,36 @@ var PropertiesTool = function (opts) { } else if (data.properties) { if (data.properties.dynamic === false) { // this object is leaving dynamic, so we zero its velocities - data.properties.velocity = Vec3.ZERO; - data.properties.angularVelocity = Vec3.ZERO; + data.properties.localVelocity = Vec3.ZERO; + data.properties.localAngularVelocity = Vec3.ZERO; } if (data.properties.rotation !== undefined) { - var rotation = data.properties.rotation; - data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); + data.properties.rotation = Quat.fromVec3Degrees(data.properties.rotation); + } + if (data.properties.localRotation !== undefined) { + data.properties.localRotation = Quat.fromVec3Degrees(data.properties.localRotation); + } + if (data.properties.emitOrientation !== undefined) { + data.properties.emitOrientation = Quat.fromVec3Degrees(data.properties.emitOrientation); } if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) { - data.properties.keyLight.direction = Vec3.fromPolar( - data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, - data.properties.keyLight.direction.y * DEGREES_TO_RADIANS - ); + var currentKeyLightDirection = Vec3.toPolar(Entities.getEntityProperties(selectionManager.selections[0], ['keyLight.direction']).keyLight.direction); + if (data.properties.keyLight.direction.x === undefined) { + data.properties.keyLight.direction.x = currentKeyLightDirection.x; + } + if (data.properties.keyLight.direction.y === undefined) { + data.properties.keyLight.direction.y = currentKeyLightDirection.y; + } + data.properties.keyLight.direction = Vec3.fromPolar(data.properties.keyLight.direction.x, data.properties.keyLight.direction.y); } Entities.editEntity(selectionManager.selections[0], data.properties); if (data.properties.name !== undefined || data.properties.modelURL !== undefined || data.properties.materialURL !== undefined || - data.properties.visible !== undefined || data.properties.locked !== undefined) { + data.properties.visible !== undefined || data.properties.locked !== undefined) { entityListTool.sendUpdate(); } } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } else if (data.type === 'parent') { parentSelectedEntities(); } else if (data.type === 'unparent') { @@ -2301,7 +2398,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "moveAllToGrid") { if (selectionManager.hasSelection()) { @@ -2321,7 +2418,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "resetToNaturalDimensions") { if (selectionManager.hasSelection()) { @@ -2342,7 +2439,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "previewCamera") { if (selectionManager.hasSelection()) { @@ -2360,7 +2457,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "reloadClientScripts") { if (selectionManager.hasSelection()) { @@ -2380,9 +2477,22 @@ var PropertiesTool = function (opts) { } } else if (data.type === "propertiesPageReady") { updateSelections(true); + } else if (data.type === "tooltipsRequest") { + emitScriptEvent({ + type: 'tooltipsReply', + tooltips: Script.require('./assets/data/createAppTooltips.json'), + hmdActive: HMD.active, + }); } }; + HMD.displayModeChanged.connect(function() { + emitScriptEvent({ + type: 'hmdActiveChanged', + hmdActive: HMD.active, + }); + }); + createToolsWindow.webEventReceived.addListener(this, onWebEventReceived); webView.webEventReceived.connect(onWebEventReceived); @@ -2390,6 +2500,7 @@ var PropertiesTool = function (opts) { return that; }; + var PopupMenu = function () { var self = this; @@ -2486,7 +2597,7 @@ var PopupMenu = function () { y: event.y }); if (!pressingOverlay) { - if (hoveringOverlay !== null && hoveringOverlay !== null && overlay !== hoveringOverlay) { + if (hoveringOverlay !== null && overlay !== hoveringOverlay) { Overlays.editOverlay(hoveringOverlay, { backgroundColor: upColor }); @@ -2559,6 +2670,46 @@ var PopupMenu = function () { return this; }; +function whenPressed(fn) { + return function(value) { + if (value > 0) { + fn(); + } + }; +} + +function whenReleased(fn) { + return function(value) { + if (value === 0) { + fn(); + } + }; +} + +var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); +mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); +mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); +mapping.from([Controller.Hardware.Keyboard.X]) + .when([Controller.Hardware.Keyboard.Control]) + .to(whenReleased(function() { selectionManager.cutSelectedEntities() })); +mapping.from([Controller.Hardware.Keyboard.C]) + .when([Controller.Hardware.Keyboard.Control]) + .to(whenReleased(function() { selectionManager.copySelectedEntities() })); +mapping.from([Controller.Hardware.Keyboard.V]) + .when([Controller.Hardware.Keyboard.Control]) + .to(whenReleased(function() { selectionManager.pasteEntities() })); +mapping.from([Controller.Hardware.Keyboard.D]) + .when([Controller.Hardware.Keyboard.Control]) + .to(whenReleased(function() { selectionManager.duplicateSelection() })); + +// Bind undo to ctrl-shift-z to maintain backwards-compatibility +mapping.from([Controller.Hardware.Keyboard.Z]) + .when([Controller.Hardware.Keyboard.Control, Controller.Hardware.Keyboard.Shift]) + .to(whenPressed(function() { undoHistory.redo() })); + var propertyMenu = new PopupMenu(); @@ -2572,31 +2723,6 @@ propertyMenu.onSelectMenuItem = function (name) { var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); -var particleExplorerTool = new ParticleExplorerTool(createToolsWindow); -var selectedParticleEntityID = null; - -function selectParticleEntity(entityID) { - selectedParticleEntityID = entityID; - - var properties = Entities.getEntityProperties(entityID); - if (properties.emitOrientation) { - properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); - } - - particleExplorerTool.destroyWebView(); - particleExplorerTool.createWebView(); - - particleExplorerTool.setActiveParticleEntity(entityID); - - // Switch to particle explorer - var selectTabMethod = { method: 'selectTab', params: { id: 'particle' } }; - if (shouldUseEditTabletApp()) { - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.sendToQml(selectTabMethod); - } else { - createToolsWindow.sendToQml(selectTabMethod); - } -} entityListTool.webView.webEventReceived.connect(function(data) { try { @@ -2610,21 +2736,13 @@ entityListTool.webView.webEventReceived.connect(function(data) { parentSelectedEntities(); } else if (data.type === 'unparent') { unparentSelectedEntities(); - } else if (data.type === "selectionUpdate") { - var ids = data.entityIds; - if (ids.length === 1) { - if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { - if (JSON.stringify(selectedParticleEntityID) === JSON.stringify(ids[0])) { - // This particle entity is already selected, so return - return; - } - // Destroy the old particles web view first - } else { - selectedParticleEntityID = 0; - particleExplorerTool.destroyWebView(); - } - } } }); + +selectionDisplay.onSpaceModeChange = function(spaceMode) { + entityListTool.setSpaceMode(spaceMode); + propertiesTool.setSpaceMode(spaceMode); +}; + }()); // END LOCAL_SCOPE diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 6c1931932a..e4b33414ab 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -103,7 +103,7 @@ thead { font-size: 12px; text-transform: uppercase; background-color: #1c1c1c; - padding: 1px 0px; + padding: 1px 0; border-bottom: 1px solid #575757; width: 100%; } @@ -198,7 +198,7 @@ td.url { } -input[type="text"], input[type="number"], textarea { +input[type="text"], input[type="search"], input[type="number"], textarea { margin: 0; padding: 0 12px; color: #afafaf; @@ -222,7 +222,7 @@ input::-webkit-input-placeholder { font-style: italic; } -input:focus, textarea:focus { +input:focus, textarea:focus, button:focus { color: #fff; background-color: #000; outline: 1px solid #00b4ef; @@ -244,7 +244,7 @@ input.search:focus { height: 26px; margin-top: 1px; margin-bottom: 1px; - box-shadow: 0 0 0px 1px #00b4ef; + box-shadow: 0 0 0 1px #00b4ef; } input:disabled, textarea:disabled { @@ -257,12 +257,22 @@ input[type="text"] { width: 100%; } +input[type="search"] { + height: 28px; + width: 100%; +} +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 20px; + width: 20px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goNAQIFbBwsbwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAZfSURBVDgRAVQGq/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9PT0YAwMDBgAAAAD8/Pz5+vr67MrKyv0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4KAgICQgAAAE3///9RAQEBFQAAAAD////pAQEBu/39/ab+/v7BxcXF9gAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAADs7OzMEBASIAQEBRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAACm+/v7cMXFxewAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAPT09OwEBAagBAQEcAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAA2f///2XCwsLDAAAAAAAAAAABAAAAAAAAAAA9PT0KAwMDt////z4AAAAAAAAAAAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAcIBAQFJvr6+9gAAAAACAAAAAAAAAAAAAABg////PgEBAQAAAAAAS0tLADg4OAAAAAAAAAAAAP///wADAwMAQEBAACEhIQD///8A////AP7+/j76+vpWAAAAAAAAAAACAAAAAD09PQ8CAgJkAQEBAP///wD///8ACgoKAFhYWAAyMjIAAAAAAAICAgBGRkYAT09PABEREQAAAAAAAAAAAAAAAAACAgJwOjo6EAAAAAAEAAAAAAICAg8BAQExAAAAAAEBAQABAQEAsrKyAAoKCgBaWloA9/f3ABsbGwBISEgAtra2AM7OzgACAgIA////AP///wABAQEuBQUFDgAAAPAEAAAAAPz8/BkEBAQAAQEBAAAAAAAAAAAA+vr6AKioqAALCwsAZWVlAAcHBwC/v78Au7u7AAEBAQD///8AAAAAAAAAAAAAAAABAAAAAAAAAAACAAAAAAQEBOgBAQEAAQEBAAEBAQABAQEAAQEBAPz8/ADT09MADg4OAP39/QDQ0NAA/v7+AP///wAAAAAAAAAAAAEBAQABAQEAAQEBAAAAAAACAAAAAAAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAACkpKQBQUFAAx8fHAObm5gBfX18AFxcXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAP39/fz+/v7z////AP///wD///8AJycnAGFhYQDc3NwApaWlAJaWlgD29vYAZmZmABQUFAACAgIAAQEBAAEBAQABAQH1AAAA/AAAAAACAAAAAPr6+ukBAQGkAAAAAAAAAAABAQEAQEBAAObm5gCmpqYA+fn5APPz8wCdnZ0A////ACwsLAD///8AAAAAAAAAAAD///+k9vb26QAAAAABAAAAAAAAAAA+Pj4uAgICxgAAAAsAAAAAEBAQAPr6+gD29vYAAAAAAAAAAAABAQEAAgICAP///wD+/v4AAAAAAAAAAPL8/Pw/xMTE0AAAAAACAAAAAAAAAAD5+fnV////nQICAgABAQEA8fHxAPX19QABAQEAAAAAAAAAAAD///8A/v7+AP7+/gAAAAAAAAAAAP7+/p36+vrSAAAAAAAAAAADAAAAAAAAAADl5eX/ICAgwQAAAA////8q////BgEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD1/f39mAEBAXrGxsb7AAAAAAAAAAADAAAAAAAAAAAAAAAA4eHh/BgYGLsBAQHDBAQEHAAAACP///8AAQEBAAAAAAAAAAAAAAAA+////7QBAQFu+fn5m8bGxvoAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPz8/Cv7+/iUBAQFMAgICEQICAgD8/PzdAwMDs/j4+OvHx8f5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8TUnpZ7EwQgAAAABJRU5ErkJggg==') +} + input[type="number"] { position: relative; height: 28px; width: 124px; } - input[type=number] { padding-right: 3px; } @@ -273,7 +283,7 @@ input[type=number]::-webkit-inner-spin-button { width: 10px; height: 90%; overflow: hidden; - font-family: hifi-glyphs; + font-family: HiFi-Glyphs; font-size: 32px; color: #afafaf; cursor: pointer; @@ -300,6 +310,28 @@ input[type=number].hover-down::-webkit-inner-spin-button:after { color: #ffffff; } +input[type=range] { + -webkit-appearance: none; + background: #2e2e2e; + height: 1.8rem; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb { + -webkit-appearance:none; + width: 0.6rem; + height: 1.8rem; + padding:0; + margin: 0; + background-color: #696969; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb:hover { + background-color: white; +} +input[type=range]:focus { /*#252525*/ + outline: none; +} + input.no-spin::-webkit-outer-spin-button, input.no-spin::-webkit-inner-spin-button { display: none; @@ -308,14 +340,14 @@ input.no-spin::-webkit-inner-spin-button { padding-right: 12px; } -input[type=button] { +input[type=button], button.hifi-edit-button { font-family: Raleway-Bold; font-size: 13px; text-transform: uppercase; vertical-align: top; height: 28px; min-width: 120px; - padding: 0px 18px; + padding: 0 18px; margin-right: 6px; border-radius: 5px; border: none; @@ -325,7 +357,7 @@ input[type=button] { cursor: pointer; } -input[type=button].glyph { +input[type=button].glyph, button.hifi-edit-button.glyph { font-family: HiFi-Glyphs; font-size: 20px; text-transform: none; @@ -333,58 +365,58 @@ input[type=button].glyph { padding: 0; } -input[type=button].red { +input[type=button].red, button.hifi-edit-button.red { color: #fff; background-color: #94132e; background: linear-gradient(#d42043 20%, #94132e 100%); } -input[type=button].blue { +input[type=button].blue, button.hifi-edit-button.blue { color: #fff; background-color: #1080b8; background: linear-gradient(#00b4ef 20%, #1080b8 100%); } -input[type=button].white { +input[type=button].white, button.hifi-edit-button.white { color: #121212; background-color: #afafaf; background: linear-gradient(#fff 20%, #afafaf 100%); } -input[type=button]:enabled:hover { +input[type=button]:enabled:hover, button.hifi-edit-button:enabled:hover { background: linear-gradient(#000, #000); border: none; } -input[type=button].red:enabled:hover { +input[type=button].red:enabled:hover, button.hifi-edit-button.red:enabled:hover { background: linear-gradient(#d42043, #d42043); border: none; } -input[type=button].blue:enabled:hover { +input[type=button].blue:enabled:hover, button.hifi-edit-button.blue:enabled:hover { background: linear-gradient(#00b4ef, #00b4ef); border: none; } -input[type=button].white:enabled:hover { +input[type=button].white:enabled:hover, button.hifi-edit-button.white:enabled:hover { background: linear-gradient(#fff, #fff); border: none; } -input[type=button]:active { +input[type=button]:active, button.hifi-edit-button:active { background: linear-gradient(#343434, #343434); } -input[type=button].red:active { +input[type=button].red:active, button.hifi-edit-button.red:active { background: linear-gradient(#94132e, #94132e); } -input[type=button].blue:active { +input[type=button].blue:active, button.hifi-edit-button.blue:active { background: linear-gradient(#1080b8, #1080b8); } -input[type=button].white:active { +input[type=button].white:active, button.hifi-edit-button.white:active { background: linear-gradient(#afafaf, #afafaf); } -input[type=button]:disabled { +input[type=button]:disabled, button.hifi-edit-button:disabled { color: #252525; background: linear-gradient(#575757 20%, #252525 100%); } -input[type=button][pressed=pressed] { +input[type=button][pressed=pressed], button.hifi-edit-button[pressed=pressed] { color: #00b4ef; } @@ -415,7 +447,7 @@ input[type=checkbox]:checked + label:hover { position: absolute; left: 6px; top: -2px; - font-family: hifi-glyphs; + font-family: HiFi-Glyphs; font-size: 30px; color: #afafaf; } @@ -448,18 +480,19 @@ input[type=checkbox]:checked + label:hover { border: 1.5pt solid black; } -.shape-section, .light-section, .model-section, .web-section, .image-section, .hyperlink-section, .text-section, .zone-section, .material-section { - display: table; +#properties-list { + display: flex; + flex-direction: column; } #properties-list fieldset { position: relative; /* 0.1px on the top is to prevent margin collapsing between this and it's first child */ - margin: 21px -21px 0px -21px; - padding: 0.1px 21px 0px 21px; + margin: 0 -21px 21px -21px; + padding: 0.1px 21px 0 21px; border: none; border-top: 1px rgb(90,90,90) solid; - box-shadow: 0px -1px 0px rgb(37,37,37); + box-shadow: 0 -1px 0 rgb(37,37,37); } #properties-list fieldset.fstuple, #properties-list fieldset.fsrow { @@ -469,7 +502,7 @@ input[type=checkbox]:checked + label:hover { } #properties-list > fieldset[data-collapsed="true"] + fieldset { - margin-top: 0px; + margin-top: 0; } #properties-list > fieldset[data-collapsed="true"] > *:not(legend) { @@ -477,18 +510,11 @@ input[type=checkbox]:checked + label:hover { } #properties-list legend + fieldset { - margin-top: 0px; + margin-top: 0; border: none; box-shadow: none; } -#properties-list > fieldset#properties-header { - margin-top: 0px; - padding-bottom: 0px; -} - - - #properties-list > fieldset > legend { position: relative; display: table; @@ -507,7 +533,7 @@ input[type=checkbox]:checked + label:hover { box-shadow: 0 -1px 0 rgb(37,37,37), 0 4px 4px 0 rgba(0,0,0,0.75); } -div.section-header, .sub-section-header, hr { +div.section-header, hr { display: table; width: 100%; margin: 21px -21px 0 -21px; @@ -520,17 +546,6 @@ div.section-header, .sub-section-header, hr { outline: none; } - - -.column .sub-section-header { - background-image: none; - padding-top: 0; -} - -.sub-section-header, .no-collapse, hr { - background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAjSURBVBhXY1RVVf3PgARYjIyMoEwIYHRwcEBRwQSloYCBAQCwjgPMiI7W2QAAAABJRU5ErkJggg==) repeat-x top left; -} - div.section-header:first-child { margin-top: -2px; padding-top: 0; @@ -538,10 +553,6 @@ div.section-header:first-child { height: auto; } -.sub-section-header { - margin-bottom: -10px; -} - #properties-list > fieldset > legend span, .section-header span { font-family: HiFi-Glyphs; font-size: 30px; @@ -555,26 +566,21 @@ div.section-header:first-child { margin-bottom: -21px; } +#properties-list .sub-section-header { + border-top: none; + box-shadow: none; + margin-top: 8px; +} + +.sub-section-header + .property { + margin-top: 0; +} + hr { border: none; padding-top: 2px; } -.text-group[collapsed="true"] ~ .text-group, -.zone-group[collapsed="true"] ~ .zone-group, -.image-group[collapsed="true"] ~ .image-group, -.web-group[collapsed="true"] ~ .web-group, -.hyperlink-group[collapsed="true"] ~ .hyperlink-group, -.spatial-group[collapsed="true"] ~ .spatial-group, -.physical-group[collapsed="true"] ~ .physical-group, -.behavior-group[collapsed="true"] ~ .behavior-group, -.model-group[collapsed="true"] ~ .model-group, -.material-group[collapsed="true"] ~ .material-group, -.light-group[collapsed="true"] ~ .light-group { - display: none !important; -} - - .property { display: table; width: 100%; @@ -644,7 +650,16 @@ hr { margin-left: 10px; } -.text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label,.pyr label, .dropdown label, .gen label { +.property.range label{ + padding-bottom: 3px; +} +.property.range input[type=number]{ + margin-left: 0.8rem; + width: 5.4rem; + height: 1.8rem; +} + +.text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label { float: left; margin-left: 1px; margin-bottom: 3px; @@ -658,6 +673,10 @@ hr { margin-top: -2px; } +.xy > div, .wh > div, .xyz > div, .pyr > div, .gen > div { + clear: both; +} + .number > input { clear: both; float: left; @@ -666,9 +685,6 @@ hr { clear: both; float: left; } -.xy > div, .wh > div, .xyz > div, .pyr > div, .gen > div { - clear: both; -} .dropdown { position: relative; @@ -761,6 +777,29 @@ hr { color: #252525; } +.multiselect { + position: relative; +} +.select-box { + position: absolute; +} +.select-box select { + font-family: FiraSans-SemiBold; + font-size: 15px; + color: #afafaf; + background-color: #252525; + border: none; + height: 28px; + width: 107px; + text-align-last: center; +} +.over-select { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; +} div.refresh { box-sizing: border-box; @@ -800,15 +839,6 @@ div.refresh input[type="button"] { display: none !important; } -#property-color-control1 { - display: table-cell; - float: none; -} - -#property-color-control1 + label { - border-left: 20px transparent solid; -} - .rgb label { float: left; margin-top: 10px; @@ -873,23 +903,23 @@ div.refresh input[type="button"] { font-family: FiraSans-SemiBold; font-size: 12px; } -.tuple .red + label, .tuple .x + label, .tuple .pitch + label { +.tuple .red + label, .tuple .x + label, .tuple .pitch + label, .tuple .width + label { color: #e2334d; } -.tuple .green + label, .tuple .y + label, .tuple .yaw + label { +.tuple .green + label, .tuple .y + label, .tuple .yaw + label, .tuple .height + label { color: #1ac567; } .tuple .blue + label, .tuple .z + label, .tuple .roll + label { color: #1080b8; } -.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus { +.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus, .tuple .width:focus { outline-color: #e2334d; } -.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus { +.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus, .tuple .height:focus { outline-color: #1ac567; } -tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { +.tuple .blue:focus, .tuple .z:focus, .tuple .roll:focus { outline-color: #1080b8; } @@ -914,6 +944,37 @@ tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { float: left; } +.property.texture { + display: block; +} +.property.texture input{ + margin: 0.4rem 0; +} +.texture-image img{ + padding: 0; + margin: 0; + width: 100%; + height: 100%; + display: none; +} +.texture-image { + display: block; + position: relative; + background-repeat: no-repeat; + background-position: center; + background-size: 100% 100%; + margin-top: 0.4rem; + height:128px; + width: 128px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABhNJREFUeNrsnVFy4joQRVsSCwAqBMwqsrRsIavMEkICoeAf2+8j1R5ZGDBgpLzoUDVVmTT2dc8It/paOpi3t7faOSciImVZyn6/l6qqRETEWivj8VistYPFd7ud1HUtIiLGGBmPx5JKX0RkMplIzvmPnHNijBERaS7Ef1lrB40bY1oXgH5a/ZH+8P7+LlVVycfHR/MGa60URdGcYOi4MUaKomhGaGx9EZHlcplMP2X+Ly8vPwOgLEtxzklVVVJVVfOznqAsy9YFXhuvqqq5AF/Lj+srtr7+LpV+yvz1mNF+vxcRkdVqJdZaeXp6ap1ws9m0TjibzVoj6lJ8vV6fjJdlKev1ujViU+j7t8tc8p9Op1KWpYw06L9JL0Av0r9l+jXl3nhd11JV1VE8tn5YM3PI3xjzoxVOGvyDU7zQj6s/0tGmI++amtNV087F9Wf/FnVPzRtCXz8RdV1nlb/efUbaJy4Wi0FqzjU1yRgjs9ls0Jp3jb6IyPPzczL9lPkvFot/dwCtB/om/x9oyJoXxps65NW8mPpdNTeX/JtBEtYE/+AUL/Tj6g/qA3TVnD41a6g++Bp9rYOp9FPnH80HOBcvy1I2m81D++BL+o/2AX5r/vgA+AD4AOif8AH8EdpVcy71sX3jWp/8W2AKff/TkUv+Oufr9AF0YuKc66xJ18T7eNP3nP9WfZ0EzufzJPqp8y+KQuq67vYBdETqCDpVU/rEw5oUnr+rD46h73/qUuinzh8fAP22D6AjxznXcqq6akrf+KmaFB6vf4+t7/sAelfIJf/GB9jtdmKMkdVq1dQM3zg4VVNU/NY+1Bgjh8Oh6YM1+dj6X19fzXwgp/wbH0DFtS7oyf0RdKqmhPFr+1RdseKfP7a+Px/IKX98APTbPoDOJrv60L417d54TH3V8lfS5pT/yfUA6/X6qOZcqkm3xrUm6X9CTH3fB0ihnzr/Ix9A/3T1qbfWpGvjMfX9T0UK/dT54wOg/88H8EfGPTVr6D740frhLDmn/Hv5AH1qku9t31KTzh3/aP1LPsBfzr+XDxCO0K6ack/N6qp5MfUv+QB/Of/ePsCQfWmfc6EfV3/kjzZrrRwOh9YtKHSm/LjOH3yrMTzej4c1y//51PHoP0a/tR7AOSdFURw9rz5VU049zw7jl2qWrosP++BY+iI/+wJS6afMv9kXoA6gvimsieHzZr/m6MTp3PPuc3G9SP95OPpx9JtOgT4cHwA+QCJ9+ADwAeADsC+AfQHo/4b1APAB4APAB4APAB8APgB9OD4AfAD4AFFqEnwA+AD4APgA6P86HwA+AHyAZhIBHwA+AHwA+AD04X/eB4APAB8APgB8APgA8AHow/P0AeADwAeADwAfAD4AfAD68Px8APgA8AHgA8AHgA8AH0DO70/v6lHvjaOfVn8U/iLcXx5OUML96X49vRTX3/nPw9FPo9+sB5hMJuKck+VyeVRTrLWtdfNdcf95eldNCuOfn5+tSYy/Pz+2voi0fICc8p/P5z93gJAPEN4+wufN4evaePj99eH+ePTj6p/1Abp60kt9Ksf/v46HDwAfAD6A/6gUPgD7AtgXwPP4DNcDwAeADwAfAD4AfAD4ADyPz289AHyA+Pqp84cPIPAB8AHwAfAB8AHgA7Q+HfAB4APAB4APAB+APjw3HwA+AHwA+ADwAeADwAegD8/TB4APAB8APgB8APgA8AHow/PzAeADwAeADwAfAD4AfACJ//316KfVH/mjLeb31+vx/kWhH0+/tR7AOSdFUUT9/nq9oK4+OJa+iLT25+eUf7MvIOQDxPr+en2F++PRj6PfdAr04fgA8AES6cMHgA8AH4B9AewLQP83rAeADwAfAD4AfAD4APAB6MPxAeADwAeIUpPgA8AHwAfAB0D/1/kA8AHgAzSTCPgA8AHgA8AHoA//8z4AfAD4APAB4APAB4APQB+epw8AHwA+AHwA+ADwAeAD0Ifn5wPAB4APAB8APgB8gBz5AOb19bX2TYLpdNpqQ7bbbctJGjJeVZVst9vWLSu2/vf3t+Sc/yicFIRr0C7Fu76f/lw8XBePflr9/wYAqWwWUSLcO54AAAAASUVORK5CYII='); +} +.texture-image.no-texture { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAB81JREFUeNrsnTGPm0oXht97FWm2Ch2pTEeHpUihsyvTuXO67Ta/IPkr+Qfp3MWdO7Zad0SKZDo6XIWOrTzV9xVXZ8SygGHXG4/t96lW68GGw8vMmZlzDv98+/btfyBXy780wXXzTv74/fs3rXFFfPz4kT0AoQAoAJqAAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQCgAQgEQCoBQAIQCIBQAoQAIBUAoAHLmvDv3C7i7u4PjOMiyDOv1+mC75XKJoiga2wRBAN/34TgOHMdBWZYoigJpmiLPcwrARhzHAQD4vg/P81pvlLRrwvM8zGYz00ZrbY5xHAe+7yPPc9zf36MsSwrAVmazGX78+DHoGM/zsFgsAAB5nmOz2ZgeQimF8XiMMAxNu+VyaQRCH8Ai8jyH4zgIw7D3MUopzOdzAECaplitVk+GB601kiTBz58/obWG4ziIoohOoI38+vULABCGYWd3X2U6nUIphbIsEcdxa7uiKPDw8GCGGtd1KQDbKMsSWZZBKYXJZNLrGN/3zdN/iDRNTdcvx1EAFqGUwmazeeIQduG6LpRSAIAsy3r9hrRjD2BxL5AkiXEI+8wetNa9PXtp13eIoQBOQJIkxmHrcgjlJkov8JKpJwVgIVpr47CFYdh6g/f7/ZM5/9CehgKwmDRNURQFlFKYTqeNN/rx8dH0AH2faBn7KYAzQKZ1QRCYZd0qf/78MX+PRqNe3ymO5W63owBsR9bwZShoGirEq++zeBQEweBZAwVwYh4eHqC1RhAErQ6jOHVdK3yu65qhJE1TDgHn5BDKTW6auxdFYdYOgiDAYrF40k4phTAM8fnzZyilUBRF54rhOfIOF06SJMYPaPt8v99jOp3C8zx4nget9bPZQ5ZlF3fzL0IAZVke9OLv7+/Njl/brCHLMozHY4xGI3z48MH0EEVRIMuyi40H+EdqBbNS6HXBSqGEAiAUAAVAE1AAhAIgFAChAAgFQCgAQgGQq+Eom0GLxeJgGHYVSdCUhM02yrI0qV5hGGIymaAsy9b0LNd1cXt7CwDYbDa98wOA/zKLVquVSQGr/nYTbe2iKDIh53JtZVmiLEvsdjtst9tn5z7EDmfXA3QFXdaTMbvYbrdm568tgkdueJ7njbt3QwJA+8YJ1tsFQQDXdXFzc2N2E0Uwk8kEX758eXbMEDtY2QOsVqtn//v69SsAYL1eH9xK7dNGgjuiKMJ4PH4WmSN7+QBMFu/3798bn1oAzz47NvVrqmYgz2azRpv1scNV+wDVaN969y6JIEmSWBmyJenlIgZbcgvOzgmUqJxqkmY18ldCvGwkz/MntQcogBcgETrVMV98Aptvfh1JTKEAXsBms4HWGp7nYT6fw3Ec5Hlufbi253lQSkFr3VqmhgLoQVmW2G63ZigQx8/2my/FKCR17WLWAV7LfD5vzOFLkqS1W0/T1HT9RVFY5/jNZjMz3ouvorVGHMet9QheYoer7AGq478Y2LaiDTc3N3Bd90megSwG2YQVPcDQ+a/ccK01ttutWSWsetl/i7bfq16TzP1lGFgul0exw9X2AJLGJV3joRXCl3rnXbUDhmQKl2WJ9XoNrbV1vdXZCUCWWqvVQGR8HFIgqmuaKUiCSJcA+nrzWmvzdA/ZN6EAKlTz/eXmA3iSuXOoNEzfBRsA+PTpU+PnUjxSfnvo9/ZNR6cAakjFj2rqd3VtQJ6u1z5h1e+SdYbqdK5aWHLImC0OoFQgpRN4YPoD/LfRVC8C2TQlkhVC3/dfVDG0/l1xHCOKIvi+b572atJoURSdtYnbfAHxV0aj0TP/oY8dzqYH6OscHXK26tO+rqcujmNTIKqtJkDfc0vTFMvl8smu436/R57niOO4NSbh0HfLkFHtpYbY4dgwOfRKYXIooQAIBUAB0AQUAKEACAVAKABCARAKgFAA5Gp4s93AKIrw/v17ExsnFEWB/X6P3W6HLMtaN0+GJkwOad+W2FlPLq3GHFSRdq85h2PYyGoByG6cvJOnHiEryZJSg7e+s1ZNmOyzSza0ffWYJsIwbMzk7Tp+6Dm81kZWC0BoCnSU7dowDE2K12q1alT60EDJYwVWKqUQRdHgPf9jnfMQG52dDyA5fLKnLlGztiB5Bn1eP3fuNvr31IaWZM9jhHIdEwk5G1Jk4hxtdPJZQJZlJrLWlnBpx3FMmrnrup3RReduIyumgXJxtryRUxw4mQXIO4Yv0UZWCMDWN3I2vX7u0mxk1RtDmp6yoQmTbe27kjK7iOMYt7e3CIIA2+22VyLIWyZ5Hrsnsmol0Jac+fo51QtSXJKNrOgBuvLsTrUOUO8FxAP3ff/gTXiLc3irt5aevAdQSpmpja0vZqq+fm4ymfz18i5vaaOTC0DSvapv8rQRmRY6joPxeHwxNjqpAGSpUwx8ikKJQ5AyNFKb4BJsdBIfwPM8BEFgFjXSNG3debMJSUv7GyuWf8tGby6Aaq2c+qvaJce/a3p2ioTJQ73A3d3di6aBbef8WhtZKQDJ6K1fTJ7neHx8PFjWTcbbvvPePm8QbVtc6ft/+UwKUdfbDT3n19roGDA59EphciihAAgFQAHQBBQAoQAIBUAoAEIBEAqAUACEAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQC4CkxgiceKEPQC5Iv4/APgB2O7x8IXXAAAAAElFTkSuQmCC'); +} +.texture-image.no-preview { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA8sSURBVHhe7Z3rbxXFG8d7B9SWthRabLmIYlHkIEXKJdXYBEXxHtEXprwxxsR3/jG+8PLCaDDGeAkmKsTEoCUVKoVCA6WNtLS2UEUKBSy0tKW/D+eZM9nu7tmz55z+mC2Zz4tl9tk5c2bnO/PMM2dnS+6nn36aYzFH7vvvv6+SFhMoAY4fPy7nljvG448/zjFPTiymsAIYxgpgGCuAYawAhrECGMYKYBgrgGGsAIaxAhjGCmAYK4BhrACGsQIYxgpgGCuAYawAhrECGMYKYBgrgGGsAIaxAhjmLhQgPz+/pKRk3rx56jzaRHFf0ObNmxctWkTi7Nmzp0+fFqNm+/btRUVFP/30kzp3UFtbu27duqVLl+bl3e5Y169f7+rqam1tvXnzpmSIFNHdF1RTU7M6TkNDQ0FBgbImWLVqFZfUSQKyvfzyy88991x1dfXU1NSFCxdGRkbuueeeurq6pqam0tJSlS96RNcFSQvSo9V5IC+88MIDDzwwOjr6448/fvTRR19++eVnn322Z8+ev//+u7i4+M0331ywYIHKGjGiK8Aff/zBMRaL5ebmiiUZjz322MqVK/Ez33333ZkzZxgBYh8eHt67d++lS5do/W3btokxakRXANxIf38/3mPNmjXKlARxpkeOHKGtxaIZHx9vaWkhwfTg9WZRILoCgIQG0r7JKC8vlxm7s7NTLC6YyW/cuFFYWIiPUqYoEWkB+vr6cOJLlizBwyiTB2l9vA0xj1hcTE9PDw4OkiA6EkukiLQAcOzYMY4bN26UUy8LFy7k+O+//8qpL1euXOF43333yWmkiLoATKqEQwSmlZWVyjQTIiWOwZG+npYjSNQFwIG0tbWRqK+vF4sL1r0qlZzJyUmOYXLeeaIuAHR3d+PfmQbE27hgguUY3LgS/0RzHMwBAei/R48ezcvL8x0EOCiOxEJy6osoJ1JFjTkgAHR0dExMTBDLexe0EvsTKQUMgsWLF3OUWChqzA0BGARoQBN7wyHWa6Ojo1x6+OGHlWkmaEOoeuvWrXPnzilTlJgbAgBeiEEQi8W8Pf3kyZMct27d6v0JGsf15JNPkmA5lmyhYJY5IwAenNmYBW1RUZEyJSBMYiYoLi7etWtXWVmZsubkkHPHjh2EsCjX3NysrBFjzggANDSeRJ04wEF9//33rLYqKip27979yiuvNDY2Pvvss2+//TZ+ieBn//79V69eVbkjRv6WLVv4hxW/nEcB+iyuo6ura3x8XJnicIqToV8zGpgSlDXO2NhYZ2cnV+WnIVZtTLxEn+fPn9+3b180p9+qqiqOd9ub8ihH67M8xuPT65mf1YXocXe+KY+PGhoa6unp4Rjl1tfcbQLMOawAhrECGMYKYBgrgGGsAIaxAhjGCmAYK4BhrACGyfy3oNdff72mpkadJLh27Vpvb29LS8vExIRYdu7c6dpLOz09ffPmTXLypadOnVLWnJzGxsZYLKZOPHR0dDQ3N7/33nv5+fkff/yx7/PFBQsWvPPOO5T/4YcfLly4sKmpaXBw8Ntvv5Wr7777bsAOUbINDw+Th5IpX1kTyGcPHz7c2tqqTHG4NW7wzz//9N2tHczs/BY0NjZ2PQFVLy4uXr9+/UsvvaQuJxgfH1eZ4tkKCwsrKiq2b9/u3XbozOkEzaamps6ePUueZHvcsOfl5ZFHtkH4oorzQOFU7MqVKzS0S6fy8nKxeDvckiVLOGbza2u22yW/+eYbOo46ie9Te/XVV5ctW7Z8+fK//vpLWXNyfvjhB2ctaaaGhoYNGzZs3bq1q6tLWeP88ssvdCh14oFLDz30EA3tuxFRhBGRkvHJJ5+olB8XLlxg6NCs/f39ypRo93/++Wfp0qWMP+fuCnna7N2TGp5ZngMQ48iRIyQefPBBsfhy69atgwcPjo6OlpSU+G42SQaicv80tPfBJBbslBwsQDBDQ0McpVk1CMBAx2HyFa79jUhFfeRTmTH7k7DsEky5DxBPffHiRRKytS0kNMTAwAAN4d0tigX7+fPnfaeHkEjlxbFoEIAvlTFRXV0tRhBnNTIy4hwT6TL7Asgz2zBvBUlO/K+chkQc1IoVK+RUI5YzZ87IaWZIX3buMpIJAP+Jroxv5zQgOmW52WL2BZDtyv/995+cJkMeHHJX6T42wcPgZ5gJ1HkCsWTjf4C+TCuXlpZqFyctLl6etpZpIH5F6eScAjNglgVg+n3iiSdIuHoiI/f2S19xamtrN23a9NprrzEVt7W1uSKWtWvXPu2HuhzfHkF/pFfef//9ypSTQxoLPi3lw3dV3Ez4UnU5/nicJpZuBAigvTzfyyU9DWQfAkG2UdCLL76oPeC99947f/58Et3d3cQMYhTk0b8TejGhfXt7uzpPgCfxuhf49ddfVSonp6enhyhr1apVeHyxkOYYxv8QJauUA9yaXpEQCKEH8zAJThGA1pd7lLamM0mCPNhl73vGZDsCGK10FgGffvnyZZYqP//8s7qcgCY7EUemMvz+F198ceDAAaZiyaA5duwYixov6nIcaWhpdEHSfIucBqCKm4m8hSDIBhHp3URoMgHEr9wefHoaYChw71qbjMlWgK+//pp1o/DBBx98/vnnLBfp3epyAmI4ujDs3bv3t99+I/J5/vnnfd++4/7pj17U5TjohzsuKysTL8yRNM5HwqpgVHEzce7KoYlpUynZO83qaYAOxzGbFYCQrQAsXOkXgrc7+4IYuA5WwgHvvaSEVuMoKy859vb23r6QNbQ+zof2Je2cAAQ9DYhCWU4AMPtRUBhko2B9fX1aiwAnEu3IakCOYfxPSFgN4HnwP7h7xHA6GT0NyFScZQgEZgRgimYyKCwsrKurU6Y0weHIbwO0FEfGX5bxuBPp8kR0jAPX22d8EY2Oa6qqqiJt3gVlzKFDhzjGYjFaUCzpgs/BGzQ2NnJkWg7pAMMg8Y/8Wul1Mn19fUiONtl3fzAmAP0XN8IgcM0EGzZs2JkElSOBTAMsLDiGnwBUWR74XpUjvuxiJS/TgK8AdBpUz34CAGMCgPy27hoEdC5Zr3lRORIQ8krYMzExMTAwIMaUqLI8iE/XyCCgj+NnxKLRoWf2/gcyfyBDGDNv3jw6csCP70C0QPvSUq6tzgKelK5EUxJZElazlFMX/PB6efkIJXsD0IKCgsrKSuclmpi1t6S9uBy6lJzMy1My5ae892DExdn/R8wYd+fu6DmHFcAwVgDDWAEMYwUwjBXAMFYAw1gBDGMFMIwVwDBp/xSxZs2aqqqqsbGxw4cPK1PiD2W0t7cne0K9ePHitWvXXr9+Xf4aKFRWVj7yyCMkKIfSxKgpLS1lpT4yMqIrxinGU6dOBf95OGH16tXV1dWuSmrkmbs6iTM5OXnjxo2enh7560Oap+O7MZz7AVzIF6kTPwI+m+FPEbT1+vXrN2/eXFJSokzxfXAYH330UXXuYd26dWRw/uoZi8WwgPPZukYKdO5vJI0FDdR5IL6V1KxYseL2FzvYuHFjQ0NDU1OTa7uRXFUnftTU1EieZKh8yUlPALott3T58mXSiC9GkJ/mA/aDyo1JNsjPz6fdr169OjU15SxnVqioqCgrK/NW0oXefrF///4DBw5QN2r1zDPPFBcXqxyhOXnypBTlReVITnoCyP20tLS4Gq6/v58hvGjRIudfi9HIrqnR0VG9jWfZsmXz58/nnoeGhiQt9llBVxIXFCCA3n7R3d3d0dFBY3EXRUVF4hjTAq8oRXlROZKTtgATExN9fX0DAwMyGsQ+PT0te3V8b1iMztqIpbe3l6JkNIh9VtCVpEGdlUyJPOjnI3J6Z0hDALkZbozuL63pbG6vReMSQFqcEcOACPhUZoj/kUrKPonwhcvTlTDbimeRNASQt1mkp9N5uUPn+y2Dg4M4Ge7f1eOQTR4taf+zcuVKfI6UI5sbli9f7pyfs0GaWwpnmLoqGYxswwr/dHNWSEMA7o37kfdecK+4b+luchUv5NudnS0iiEU/Rmfg5+XlBb/QEZ7gSjoh0CpPwOy1adMmQrVz58653tgJAz1MFTQT79+w8xJWACZSvobeoWN2r9MXAWSfmkb8u8v/UIjuaOk6igCkrYMrqXnqqad2JyAA3bZtG8N037593n2VKamvr1cFzaS2tlblSE5YAeQenLvPpJc57w0ng0thYaL3u0mLcGN6Bwf+p7CwkOmRfiqWixcv4rsIqLP3QmEqqRkeHqZWQK8njMH1U+233nor5FLDCcs3KcpFypckIOz2dLkHhiqrG7EAlZYmlqAb6Oksaoj65W+6iWOhG+pdU1IOGjjLQSGGF5nlD1BmTMhKCq2trXpcAkOT5RuV37Fjx1dffaWs4Whvb3f9DbvwhBoBdE8aiASr5y0O5B0j519MlVvSDt21/iooKBCPxFEVEYcGwhhmwAYgrUwiZSV9YUQeOnQI31VVVZXWe4NZEkoAqT3tyIrRibwQ6Ww4Qho6mvgTmoNG4ZZ0/EO70/cZ7+rzDojc+VTGe3VBur+3kvq/MInnCgINqD+JDLxQxqQWIDc3VzoyHYSB5uT333/HfUtDS2agCYhqWN8CpxKwyiVpI/XhmUhQJBkyQz7rrWRbWxvu3lXJZMhw0RW+A6QWQLoz9+DyoYI3hmFlzxHN+CAJp/+RAMk5SWqyjIXE/ySrJOsyjikLp+OzaiEKohxl+v+TWgCpt2+rgTfOu3TpEoENrQ/OcBP/w0RHyMGUKxYnrAbod84IyheCa/K4YH4KrqSvAK6i6urq3njjDcbu6dOnXTVUOWZCf1KX48opqweZOwNIEQVp/6PXTS7w77SyDHC9C5NeT0RBorOz0+V/5PcWL5OTk0hFkEq2EydOKKsHJlWVcoCjl8KTVVJUd1XStyjmp4MHD6qTBLt27VIpB3v27NEDZUMcSbugbrhBdeJHij9dTDyAvFQrWaMQXyLS+Pj4tWvX9PAn/kV5hgJhJXYxMgLIQDm+u3SBeZgOKJM2/YuhwJSoN+SWlJTQiJTphTZlzRlQSXBWkjUwsan6cBy+iLD9+PHjzc3Nzv22RLQqhwfEphBukx6mTH6wEEn2kOru/NPFc4gMn4hZZhcrgGGsAIaxAhjGCmAYK4BhrACGsQIYxgpgGCuAYawAhrECGMYKYBgrgGGsAIaxAhjGCmAYK4BhrACGsQIYxgpgGCuAYdS2FIsp7AgwSk7O/wCqCi/+JioQYgAAAABJRU5ErkJggg=='); +} + .two-column { display: table; width: 100%; @@ -924,12 +985,11 @@ tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { } #properties-list fieldset .two-column { - padding-top:21px; + padding-top: 10px; display: flex; } -#properties-list .two-column fieldset { - /*display: table-cell;*/ +#properties-list .two-column fieldset { width: 50%; margin: 0; padding: 0; @@ -937,22 +997,30 @@ tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { box-shadow: none; } +#properties-list .two-column .column { + position: relative; + top: -10px; +} + #properties-list .two-column fieldset legend { - display: table; width: 100%; - margin: 21px -21px 0px -21px; - padding: 0px 0px 0px 21px; + margin: 21px -21px 0 -21px; + padding: 16px 0 0 21px; font-family: Raleway-Regular; font-size: 12px; color: #afafaf; - height: 28px; + height: 10px; text-transform: uppercase; outline: none; } +#properties-list .two-column + .property { + margin-top: 6px; +} + fieldset .checkbox-sub-props { margin-top: 0; - } +} fieldset .checkbox-sub-props .property:first-child { margin-top: 0; @@ -973,6 +1041,10 @@ fieldset .checkbox-sub-props .property:first-child { ::-webkit-scrollbar-track { background-color: #2e2e2e; } +#entity-table-scroll::-webkit-scrollbar-track { + border-bottom-right-radius: 7px; +} + ::-webkit-scrollbar-thumb { background-color: #696969; border: 2px solid #2e2e2e; @@ -995,6 +1067,10 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { } +body#entity-list-body { + padding-bottom: 0; +} + #entity-list-header { margin-bottom: 36px; } @@ -1024,38 +1100,131 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { float: right; margin-right: 0; background-color: #ff0000; - min-width: 90px; } #entity-list { position: relative; /* New positioning context. */ } -#search-area { +#filter-area { padding-right: 168px; padding-bottom: 24px; } -#filter { - width: 98%; +#filter-type-select-box select { + border-radius: 14.5px; +} +#filter-type-checkboxes { + position: absolute; + z-index: 2; + top: 48px; + display: none; + border: none; +} +#filter-type-checkboxes div { + position: relative; + height: 22px; +} +#filter-type-checkboxes span { + position: relative; + top: 3px; + font-family: HiFi-Glyphs; + font-size: 13px; + color: #000000; + padding-left: 6px; + padding-right: 4px; +} +#filter-type-checkboxes label { + position: absolute; + top: -20px; + z-index: 2; + display: block; + font-family: FiraSans-SemiBold; + font-size: 11px; + color: #000000; + background-color: #afafaf; + width: 200px; + height: 22px; + padding-top: 1px; +} +#filter-type-checkboxes label:hover { + background-color: #1e90ff; +} +#filter-type-checkboxes input[type=checkbox] + label { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); + background-size: 11px 11px; + background-position: top 5px left 14px; +} +#filter-type-checkboxes input[type=checkbox]:checked + label { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); + background-size: 11px 11px; + background-position: top 5px left 14px; +} +#filter-type-checkboxes input[type=checkbox]:hover + label { + background-color: #1e90ff; } -#in-view { +#filter-search-and-icon { + position: relative; + left: 118px; + width: calc(100% - 126px); +} + +#filter-in-view { position: absolute; + top: 0; right: 126px; } -#radius-and-unit { +#filter-radius-and-unit { + position: relative; float: right; margin-right: -168px; - position: relative; - top: -17px; + top: -45px; } -#radius-and-unit label { +#filter-radius-and-unit label { margin-left: 2px; } -#radius-and-unit input { +#filter-radius-and-unit span { + position: relative; + top: 25px; + right: 9px; + z-index: 2; + font-style: italic; +} +#filter-radius-and-unit input { width: 120px; + border-radius: 14.5px; + font-style: italic; +} +#filter-radius-and-unit input[type=number]::-webkit-inner-spin-button { + display: none; +} + +#entity-list-footer { + padding-top: 9px; +} + +#footer-text { + float: right; + padding-top: 12px; + padding-right: 22px; +} + +input[type=button]#export { + height: 38px; + width: 180px; +} + +#no-entities { + display: none; + position: absolute; + top: 80px; + padding: 12px; + font-family: FiraSans-SemiBold; + font-size: 15px; + font-style: italic; + color: #afafaf; } #entity-table-scroll { @@ -1068,6 +1237,9 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { margin-top: 28px; border-left: 2px solid #575757; border-right: 2px solid #575757; + border-bottom: 2px solid #575757; + border-bottom-left-radius: 7px; + border-bottom-right-radius: 7px; background-color: #1c1c1c; } @@ -1084,8 +1256,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { background-color: #1c1c1c; } -#entity-table thead tr, #entity-table thead tr th, -#entity-table tfoot tr, #entity-table tfoot tr td { +#entity-table thead tr, #entity-table thead tr th { background: none; } @@ -1196,19 +1367,6 @@ th#entity-hasTransparent .sort-order { top: -1px; } -#entity-table tfoot { - box-sizing: border-box; - border: 2px solid #575757; - border-bottom-left-radius: 7px; - border-bottom-right-radius: 7px; - border-top: 1px solid #575757; - position: absolute; - bottom: -21px; - left: 0; - width: 100%; -} - - #col-type { width: 16%; } @@ -1286,63 +1444,51 @@ th#entity-hasScript { border-right: 1px solid #575757; } - -#no-entities { - display: none; - position: absolute; - top: 80px; - padding: 12px; - font-family: FiraSans-SemiBold; - font-size: 15px; - font-style: italic; - color: #afafaf; +#properties-base { + border-top: none !important; + box-shadow: none !important; + margin-bottom: 5px !important; + top: -15px; } - -#properties-list #properties-header { - display: table-row; - height: 28px; - border-top: none; - box-shadow: none; -} - -#properties-header .property { - display: table-cell; - vertical-align: middle; -} -#properties-header .checkbox { - position: relative; - top: -1px; -} - -#properties-header #type-icon { - font-family: hifi-glyphs; +#properties-base #property-type-icon { + font-family: HiFi-Glyphs; font-size: 31px; color: #00b4ef; margin: -4px 12px -4px -2px; width: auto; display: none; - vertical-align: middle; } -#properties-header #property-type { +#properties-base #property-type { padding: 5px 24px 5px 0; border-right: 1px solid #808080; - height: 100%; width: auto; display: inline-block; +} + +#properties-base #div-property-locked { + position: absolute; + top: 6px; + right: 140px; + display: table-cell; vertical-align: middle; } -#properties-header .checkbox:last-child { - padding-left: 24px; +#properties-base #div-property-visible { + position: absolute; + top: 26px; + right: 20px; + display: table-cell; + vertical-align: middle; } -#properties-header .checkbox label { +#properties-base #div-property-locked label, +#properties-base #div-property-visible label { background-position-y: 1px; } -#properties-header .checkbox label span { +#properties-base .checkbox label span { font-family: HiFi-Glyphs; font-size: 20px; padding-right: 6px; @@ -1351,15 +1497,10 @@ th#entity-hasScript { top: -4px; } -#properties-header input[type=checkbox]:checked + label span { +#properties-base input[type=checkbox]:checked + label span { color: #ffffff; } -#properties-header + hr { - margin-top: 12px; -} - - #id label { width: 24px; } @@ -1371,421 +1512,128 @@ th#entity-hasScript { background-color: #00b4ef; } -input#property-parent-id { - width: 340px; -} - -input#dimension-rescale-button { +input#property-scale-button-rescale { min-width: 50px; margin-left: 6px; } -input#reset-to-natural-dimensions { +input#property-scale-button-reset { margin-right: 0; } -#animation-fps { - margin-top: 48px; -} - -#userdata-clear, -#materialdata-clear { +#property-userData-button-clear, +#property-materialData-button-clear { margin-bottom: 10px; } - -#static-userdata, -#static-materialData { +#property-userData-static, +#property-materialData-static { display: none; z-index: 99; position: absolute; width: 96%; - padding-left:1%; - margin-top:5px; - margin-bottom:10px; + padding-left: 1%; + margin-top: 5px; + margin-bottom: 10px; background-color: #2e2e2e; } -#userdata-saved, -#materialData-saved { - margin-top:5px; - font-size:16px; - display:none; +#property-userData-saved, +#property-materialData-saved { + margin-top: 5px; + font-size: 16px; + display: none; } -#properties-list #collision-info > fieldset:first-of-type { - border-top: none !important; - box-shadow: none; +#div-property-serverScripts-status label { + position: relative; + top: 8px; +} +#property-serverScripts-status { + position: relative; + top: 5px; + right: -20px; +} + +#div-property-collisionSoundURL[style*="display: none"] + .property { margin-top: 0; } -#properties-list { - display: flex; - flex-direction: column; -} - -/* ----- Order of Menu items for Primitive ----- */ -/* Entity Menu classes are specified by selected entity - within entityProperties.js -*/ -#properties-list.ShapeMenu #general, -#properties-list.BoxMenu #general, -#properties-list.SphereMenu #general { - order: 1; -} - -#properties-list.ShapeMenu #collision-info, -#properties-list.BoxMenu #collision-info, -#properties-list.SphereMenu #collision-info { - order: 2; -} - -#properties-list.ShapeMenu #physical, -#properties-list.BoxMenu #physical, -#properties-list.SphereMenu #physical { - order: 3; -} - -#properties-list.ShapeMenu #spatial, -#properties-list.BoxMenu #spatial, -#properties-list.SphereMenu #spatial { - order: 4; -} - -#properties-list.ShapeMenu #behavior, -#properties-list.BoxMenu #behavior, -#properties-list.SphereMenu #behavior { - order: 5; -} - -#properties-list.ShapeMenu #hyperlink, -#properties-list.BoxMenu #hyperlink, -#properties-list.SphereMenu #hyperlink { - order: 6; -} - -#properties-list.ShapeMenu #material, -#properties-list.BoxMenu #material, -#properties-list.SphereMenu #material, -#properties-list.ShapeMenu #light, -#properties-list.BoxMenu #light, -#properties-list.SphereMenu #light, -#properties-list.ShapeMenu #model, -#properties-list.BoxMenu #model, -#properties-list.SphereMenu #model, -#properties-list.ShapeMenu #zone, -#properties-list.BoxMenu #zone, -#properties-list.SphereMenu #zone, -#properties-list.ShapeMenu #text, -#properties-list.BoxMenu #text, -#properties-list.SphereMenu #text, -#properties-list.ShapeMenu #image, -#properties-list.BoxMenu #image, -#properties-list.SphereMenu #image, -#properties-list.ShapeMenu #web, -#properties-list.BoxMenu #web, -#properties-list.SphereMenu #web { +.context-menu { display: none; + position: fixed; + color: #000000; + background-color: #afafaf; + padding: 5px 0 5px 0; + cursor: default; +} +.context-menu li { + list-style-type: none; + padding: 4px 18px 4px 18px; + margin: 0; + white-space: nowrap; +} +.context-menu li:hover { + background-color: #e3e3e3; +} +.context-menu li.separator { + border-top: 1px solid #333333; + margin: 5px 5px; + padding: 0 0; +} +.context-menu li.disabled { + color: #333333; +} +.context-menu li.separator:hover, .context-menu li.disabled:hover { + background-color: #afafaf; } -/* ----- ParticleEffectMenu ----- */ -#properties-list.ParticleEffectMenu #general { - order: 1; -} -#properties-list.ParticleEffectMenu #collision-info { - order: 2; -} -#properties-list.ParticleEffectMenu #physical { - order: 3; -} -#properties-list.ParticleEffectMenu #spatial { - order: 4; -} -#properties-list.ParticleEffectMenu #behavior { - order: 5; +input.rename-entity { + height: 100%; + width: 100%; + border: none; + font-family: FiraSans-SemiBold; + font-size: 15px; + /* need this to show the text cursor when the input field is empty */ + padding-left: 2px; } -/* items to hide */ -#properties-list.ParticleEffectMenu #material, -#properties-list.ParticleEffectMenu #base-color-section, -#properties-list.ParticleEffectMenu #hyperlink, -#properties-list.ParticleEffectMenu #light, -#properties-list.ParticleEffectMenu #model, -#properties-list.ParticleEffectMenu #shape-list, -#properties-list.ParticleEffectMenu #text, -#properties-list.ParticleEffectMenu #web, -#properties-list.ParticleEffectMenu #image, -#properties-list.ParticleEffectMenu #zone { - display: none; +.create-app-tooltip { + position: absolute; + background: #6a6a6a; + border: 1px solid black; + width: 258px; + min-height: 20px; + padding: 5px; } -/* ----- Order of Menu items for Light ----- */ -#properties-list.LightMenu #general { - order: 1; -} -#properties-list.LightMenu #light { - order: 2; -} -#properties-list.LightMenu #physical { - order: 3; -} -#properties-list.LightMenu #spatial { - order: 4; -} -#properties-list.LightMenu #behavior { - order: 5; -} -#properties-list.LightMenu #collision-info { - order: 6; -} -#properties-list.LightMenu #hyperlink { - order: 7; -} -/* sections to hide */ -#properties-list.LightMenu #material, -#properties-list.LightMenu #model, -#properties-list.LightMenu #zone, -#properties-list.LightMenu #text, -#properties-list.LightMenu #image, -#properties-list.LightMenu #web { - display: none; -} -/* items to hide */ -#properties-list.LightMenu #shape-list, -#properties-list.LightMenu #base-color-section { - display: none +.create-app-tooltip .create-app-tooltip-description { + font-size: 12px; + font-style: italic; + color: #ffffff; } - -/* ----- Order of Menu items for Model ----- */ -#properties-list.ModelMenu #general { - order: 1; -} -#properties-list.ModelMenu #model { - order: 2; -} -#properties-list.ModelMenu #collision-info { - order: 3; -} -#properties-list.ModelMenu #physical { - order: 4; -} -#properties-list.ModelMenu #spatial { - order: 5; -} -#properties-list.ModelMenu #behavior { - order: 6; -} -#properties-list.ModelMenu #hyperlink { - order: 7; -} -/* sections to hide */ -#properties-list.ModelMenu #material, -#properties-list.ModelMenu #light, -#properties-list.ModelMenu #zone, -#properties-list.ModelMenu #text, -#properties-list.ModelMenu #image, -#properties-list.ModelMenu #web { - display: none; -} -/* items to hide */ -#properties-list.ModelMenu #shape-list, -#properties-list.ModelMenu #base-color-section { - display: none +.create-app-tooltip .create-app-tooltip-js-attribute { + font-family: Raleway-SemiBold; + font-size: 11px; + color: #000000; + bottom: 0; + margin-top: 5px; } - -/* ----- Order of Menu items for Zone ----- */ -#properties-list.ZoneMenu #general { - order: 1; -} -#properties-list.ZoneMenu #zone { - order: 2; -} -#properties-list.ZoneMenu #physical { - order: 3; -} -#properties-list.ZoneMenu #spatial { - order: 4; -} -#properties-list.ZoneMenu #behavior { - order: 5; -} -#properties-list.ZoneMenu #collision-info { - order: 6; -} -#properties-list.ZoneMenu #hyperlink { - order: 7; -} -/* sections to hide */ -#properties-list.ZoneMenu #material, -#properties-list.ZoneMenu #light, -#properties-list.ZoneMenu #model, -#properties-list.ZoneMenu #text, -#properties-list.ZoneMenu #image, -#properties-list.ZoneMenu #web { - display: none; -} -/* items to hide */ -#properties-list.ZoneMenu #shape-list, -#properties-list.ZoneMenu #base-color-section { - display: none +#toggle-space-mode::before { + font-family: HiFi-Glyphs; + font-size: 20px; + text-transform: none; + min-width: 32px; + padding-right: 4px; + vertical-align: middle; } - -/* ----- Order of Menu items for Image ----- */ -#properties-list.ImageMenu #general { - order: 1; -} -#properties-list.ImageMenu #image { - order: 2; -} -#properties-list.ImageMenu #collision-info { - order: 3; -} -#properties-list.ImageMenu #physical { - order: 4; -} -#properties-list.ImageMenu #spatial { - order: 5; -} -#properties-list.ImageMenu #behavior { - order: 6; -} -#properties-list.ImageMenu #hyperlink { - order: 7; -} -/* sections to hide */ -#properties-list.ImageMenu #material, -#properties-list.ImageMenu #light, -#properties-list.ImageMenu #model, -#properties-list.ImageMenu #zone, -#properties-list.ImageMenu #web, -#properties-list.ImageMenu #text { - display: none; -} -/* items to hide */ -#properties-list.ImageMenu #shape-list, -#properties-list.ImageMenu #base-color-section { - display: none; +#toggle-space-mode.space-mode-local::before { + content: "m"; } - -/* ----- Order of Menu items for Web ----- */ -#properties-list.WebMenu #general { - order: 1; -} -#properties-list.WebMenu #web { - order: 2; -} -#properties-list.WebMenu #collision-info { - order: 3; -} -#properties-list.WebMenu #physical { - order: 4; -} -#properties-list.WebMenu #spatial { - order: 5; -} -#properties-list.WebMenu #behavior { - order: 6; -} -#properties-list.WebMenu #hyperlink { - order: 7; -} -/* sections to hide */ -#properties-list.WebMenu #material, -#properties-list.WebMenu #light, -#properties-list.WebMenu #model, -#properties-list.WebMenu #zone, -#properties-list.WebMenu #image, -#properties-list.WebMenu #text { - display: none; -} -/* items to hide */ -#properties-list.WebMenu #shape-list, -#properties-list.WebMenu #base-color-section { - display: none; +#toggle-space-mode.space-mode-world::before { + content: "\e02c"; } - - -/* ----- Order of Menu items for Text ----- */ -#properties-list.TextMenu #general { - order: 1; -} -#properties-list.TextMenu #text { - order: 2; -} -#properties-list.TextMenu #collision-info { - order: 3; -} -#properties-list.TextMenu #physical { - order: 4; -} -#properties-list.TextMenu #spatial { - order: 5; -} -#properties-list.TextMenu #behavior { - order: 6; -} -#properties-list.TextMenu #hyperlink { - order: 7; -} -/* sections to hide */ -#properties-list.TextMenu #material, -#properties-list.TextMenu #light, -#properties-list.TextMenu #model, -#properties-list.TextMenu #zone, -#properties-list.TextMenu #image, -#properties-list.TextMenu #web { - display: none; -} -/* items to hide */ -#properties-list.TextMenu #shape-list, -#properties-list.TextMenu #base-color-section { - display: none -} - -/* ----- Order of Menu items for Material ----- */ -#properties-list.MaterialMenu #general { - order: 1; -} -#properties-list.MaterialMenu #material { - order: 2; -} -#properties-list.MaterialMenu #spatial { - order: 3; -} -#properties-list.MaterialMenu #hyperlink { - order: 4; -} -#properties-list.MaterialMenu #behavior { - order: 5; -} - -/* sections to hide */ -#properties-list.MaterialMenu #physical, -#properties-list.MaterialMenu #collision-info, -#properties-list.MaterialMenu #model, -#properties-list.MaterialMenu #light, -#properties-list.MaterialMenu #zone, -#properties-list.MaterialMenu #text, -#properties-list.MaterialMenu #web, -#properties-list.MaterialMenu #image { - display: none; -} -/* items to hide */ -#properties-list.MaterialMenu #shape-list, -#properties-list.MaterialMenu #base-color-section { - display: none -} - - -/* Currently always hidden */ -#properties-list #polyvox { - display: none; -} - -.skybox-section { - display: none; -} \ No newline at end of file diff --git a/scripts/system/html/css/hifi-style.css b/scripts/system/html/css/hifi-style.css index e1e4f67723..90a5b366c2 100644 --- a/scripts/system/html/css/hifi-style.css +++ b/scripts/system/html/css/hifi-style.css @@ -139,9 +139,9 @@ input[type=radio]:active + label > span > span{ font-family: Raleway-Bold; font-size: 13px; color: black; - padding: 0px 10px; + padding: 0 10px; border-radius: 3px; - border-width: 0px; + border-width: 0; background-image: linear-gradient(#FFFFFF, #AFAFAF); min-height: 30px; } @@ -158,9 +158,9 @@ input[type=radio]:active + label > span > span{ font-family: Raleway-Bold; font-size: 13px; color: white; - padding: 0px 10px; + padding: 0 10px; border-radius: 3px; - border-width: 0px; + border-width: 0; background-image: linear-gradient(#00B4EF, #1080B8); min-height: 30px; } diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 7eed78ecf3..062847bcb6 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -1,4 +1,4 @@ - + Entity List @@ -16,26 +17,40 @@ + - +
- - + +
-
- Y - -
+
+
+
+ +
+
+
+ +
+
+
+ Y +
+ +
- +
@@ -87,15 +102,17 @@ - - - - - -
- No entities found in view within a 100 meter radius. Try moving to a different location and refreshing. + There are no entities to display. Please check your filters or create an entity to begin. +
+
+
+ diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 744150253d..b56ad346e2 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -1,4 +1,4 @@ - - -
- - Bloom - -
- Inherit - Off - On -
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
- -
- - TextM - -
- - -
-
- - -
-
- - -
-
-
- -
-
-
-
-
-
-
-
- Background color -
-
-
-
-
-
-
- -
- - ImageM - -
- - -
-
- -
- - WebM - -
- - -
-
- - -
-
- -
- - Voxel volume size m - -
-
-
-
-
- -
- - -
-
- - -
-
- - -
-
- -
- - MaterialM - -
-
- - -
- -
- -

-
- - - - Saved! -
-
-
- -
-
-
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
-
-
-
- -
- -
-
-
-
- -
- - -
-
- -
-
- +
diff --git a/scripts/system/html/js/createAppTooltip.js b/scripts/system/html/js/createAppTooltip.js new file mode 100644 index 0000000000..a5c961a7e2 --- /dev/null +++ b/scripts/system/html/js/createAppTooltip.js @@ -0,0 +1,116 @@ +// createAppTooltip.js +// +// Created by Thijs Wenker on 17 Oct 2018 +// 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 + +const CREATE_APP_TOOLTIP_OFFSET = 20; +const TOOLTIP_DELAY = 500; // ms +const TOOLTIP_DEBUG = false; + +function CreateAppTooltip() { + this._tooltipData = null; + this._tooltipDiv = null; + this._delayTimeout = null; + this._isEnabled = false; +} + +CreateAppTooltip.prototype = { + _tooltipData: null, + _tooltipDiv: null, + _delayTimeout: null, + _isEnabled: null, + + _removeTooltipIfExists: function() { + if (this._delayTimeout !== null) { + window.clearTimeout(this._delayTimeout); + this._delayTimeout = null; + } + + if (this._tooltipDiv !== null) { + this._tooltipDiv.remove(); + this._tooltipDiv = null; + } + }, + + setIsEnabled: function(isEnabled) { + this._isEnabled = isEnabled; + }, + + setTooltipData: function(tooltipData) { + this._tooltipData = tooltipData; + }, + + registerTooltipElement: function(element, tooltipID) { + element.addEventListener("mouseover", function() { + if (!this._isEnabled) { + return; + } + + this._removeTooltipIfExists(); + + this._delayTimeout = window.setTimeout(function() { + let tooltipData = this._tooltipData[tooltipID]; + + if (!tooltipData || tooltipData.tooltip === "") { + if (!TOOLTIP_DEBUG) { + return; + } + tooltipData = { tooltip: 'PLEASE SET THIS TOOLTIP' }; + } + + let elementRect = element.getBoundingClientRect(); + let elTip = document.createElement("div"); + elTip.className = "create-app-tooltip"; + + let elTipDescription = document.createElement("div"); + elTipDescription.className = "create-app-tooltip-description"; + elTipDescription.innerText = tooltipData.tooltip; + elTip.appendChild(elTipDescription); + + let jsAttribute = tooltipID; + if (tooltipData.jsPropertyName) { + jsAttribute = tooltipData.jsPropertyName; + } + + if (!tooltipData.skipJSProperty) { + let elTipJSAttribute = document.createElement("div"); + elTipJSAttribute.className = "create-app-tooltip-js-attribute"; + elTipJSAttribute.innerText = `JS Attribute: ${jsAttribute}`; + elTip.appendChild(elTipJSAttribute); + } + + document.body.appendChild(elTip); + + let elementTop = window.pageYOffset + elementRect.top; + + let desiredTooltipTop = elementTop + element.clientHeight + CREATE_APP_TOOLTIP_OFFSET; + let desiredTooltipLeft = window.pageXOffset + elementRect.left; + + if ((window.innerHeight + window.pageYOffset) < (desiredTooltipTop + elTip.clientHeight)) { + // show above when otherwise out of bounds + elTip.style.top = elementTop - CREATE_APP_TOOLTIP_OFFSET - elTip.clientHeight; + } else { + // show tooltip below by default + elTip.style.top = desiredTooltipTop; + } + if ((window.innerWidth + window.pageXOffset) < (desiredTooltipLeft + elTip.clientWidth)) { + elTip.style.left = document.body.clientWidth + window.pageXOffset - elTip.offsetWidth; + } else { + elTip.style.left = desiredTooltipLeft; + } + + this._tooltipDiv = elTip; + }.bind(this), TOOLTIP_DELAY); + }.bind(this), false); + element.addEventListener("mouseout", function() { + if (!this._isEnabled) { + return; + } + + this._removeTooltipIfExists(); + }.bind(this), false); + } +}; diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index fed4dfb632..0204f0c349 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -13,14 +13,14 @@ const DESCENDING_STRING = '▾'; const LOCKED_GLYPH = ""; const VISIBLE_GLYPH = ""; const TRANSPARENCY_GLYPH = ""; -const BAKED_GLYPH = "" +const BAKED_GLYPH = ""; const SCRIPT_GLYPH = "k"; const BYTES_PER_MEGABYTE = 1024 * 1024; const IMAGE_MODEL_NAME = 'default-image-model.fbx'; const COLLAPSE_EXTRA_INFO = "E"; const EXPAND_EXTRA_INFO = "D"; const FILTER_IN_VIEW_ATTRIBUTE = "pressed"; -const WINDOW_NONVARIABLE_HEIGHT = 207; +const WINDOW_NONVARIABLE_HEIGHT = 227; const NUM_COLUMNS = 12; const EMPTY_ENTITY_ID = "0"; const DELETE = 46; // Key code for the delete key. @@ -54,29 +54,82 @@ const COMPARE_ASCENDING = function(a, b) { } return 1; -} +}; const COMPARE_DESCENDING = function(a, b) { return COMPARE_ASCENDING(b, a); -} +}; + +const FILTER_TYPES = [ + "Shape", + "Model", + "Image", + "Light", + "Zone", + "Web", + "Material", + "ParticleEffect", + "Text", +]; + +const ICON_FOR_TYPE = { + Shape: "n", + Model: "", + Image: "", + Light: "p", + Zone: "o", + Web: "q", + Material: "", + ParticleEffect: "", + Text: "l", +}; // List of all entities -var entities = [] +let entities = []; // List of all entities, indexed by Entity ID -var entitiesByID = {}; +let entitiesByID = {}; // The filtered and sorted list of entities passed to ListView -var visibleEntities = []; +let visibleEntities = []; // List of all entities that are currently selected -var selectedEntities = []; +let selectedEntities = []; -var entityList = null; // The ListView +let entityList = null; // The ListView -var currentSortColumn = 'type'; -var currentSortOrder = ASCENDING_SORT; -var isFilterInView = false; -var showExtraInfo = false; +/** + * @type EntityListContextMenu + */ +let entityListContextMenu = null; + +let currentSortColumn = 'type'; +let currentSortOrder = ASCENDING_SORT; +let typeFilters = []; +let isFilterInView = false; +let showExtraInfo = false; + +let elEntityTable, + elEntityTableBody, + elEntityTableScroll, + elEntityTableHeaderRow, + elRefresh, + elToggleLocked, + elToggleVisible, + elDelete, + elFilterTypeSelectBox, + elFilterTypeText, + elFilterTypeCheckboxes, + elFilterSearch, + elFilterInView, + elFilterRadius, + elExport, + elPal, + elInfoToggle, + elInfoToggleGlyph, + elSelectedEntitiesCount, + elVisibleEntitiesCount, + elNoEntitiesMessage, + elToggleSpaceMode; const ENABLE_PROFILING = false; -var profileIndent = ''; +let profileIndent = ''; const PROFILE_NOOP = function(_name, fn, args) { fn.apply(this, args); } ; @@ -105,18 +158,22 @@ function loaded() { elToggleLocked = document.getElementById("locked"); elToggleVisible = document.getElementById("visible"); elDelete = document.getElementById("delete"); - elFilter = document.getElementById("filter"); - elInView = document.getElementById("in-view") - elRadius = document.getElementById("radius"); + elFilterTypeSelectBox = document.getElementById("filter-type-select-box"); + elFilterTypeText = document.getElementById("filter-type-text"); + elFilterTypeCheckboxes = document.getElementById("filter-type-checkboxes"); + elFilterSearch = document.getElementById("filter-search"); + elFilterInView = document.getElementById("filter-in-view"); + elFilterRadius = document.getElementById("filter-radius"); elExport = document.getElementById("export"); elPal = document.getElementById("pal"); elInfoToggle = document.getElementById("info-toggle"); elInfoToggleGlyph = elInfoToggle.firstChild; - elFooter = document.getElementById("footer-text"); + elSelectedEntitiesCount = document.getElementById("selected-entities-count"); + elVisibleEntitiesCount = document.getElementById("visible-entities-count"); elNoEntitiesMessage = document.getElementById("no-entities"); - elNoEntitiesInView = document.getElementById("no-entities-in-view"); - elNoEntitiesRadius = document.getElementById("no-entities-radius"); + elToggleSpaceMode = document.getElementById('toggle-space-mode'); + document.body.onclick = onBodyClick; document.getElementById("entity-name").onclick = function() { setSortColumn('name'); }; @@ -126,74 +183,189 @@ function loaded() { document.getElementById("entity-url").onclick = function() { setSortColumn('url'); }; - document.getElementById("entity-locked").onclick = function () { + document.getElementById("entity-locked").onclick = function() { setSortColumn('locked'); }; - document.getElementById("entity-visible").onclick = function () { + document.getElementById("entity-visible").onclick = function() { setSortColumn('visible'); }; - document.getElementById("entity-verticesCount").onclick = function () { + document.getElementById("entity-verticesCount").onclick = function() { setSortColumn('verticesCount'); }; - document.getElementById("entity-texturesCount").onclick = function () { + document.getElementById("entity-texturesCount").onclick = function() { setSortColumn('texturesCount'); }; - document.getElementById("entity-texturesSize").onclick = function () { + document.getElementById("entity-texturesSize").onclick = function() { setSortColumn('texturesSize'); }; - document.getElementById("entity-hasTransparent").onclick = function () { + document.getElementById("entity-hasTransparent").onclick = function() { setSortColumn('hasTransparent'); }; - document.getElementById("entity-isBaked").onclick = function () { + document.getElementById("entity-isBaked").onclick = function() { setSortColumn('isBaked'); }; - document.getElementById("entity-drawCalls").onclick = function () { + document.getElementById("entity-drawCalls").onclick = function() { setSortColumn('drawCalls'); }; - document.getElementById("entity-hasScript").onclick = function () { + document.getElementById("entity-hasScript").onclick = function() { setSortColumn('hasScript'); }; elRefresh.onclick = function() { refreshEntities(); - } + }; elToggleLocked.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' })); - } + }; elToggleVisible.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleVisible' })); - } + }; elExport.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'export'})); - } + }; elPal.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'pal' })); - } + }; elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); - } - elFilter.onkeyup = refreshEntityList; - elFilter.onpaste = refreshEntityList; - elFilter.onchange = onFilterChange; - elFilter.onblur = refreshFooter; - elInView.onclick = toggleFilterInView; - elRadius.onchange = onRadiusChange; + }; + elToggleSpaceMode.onclick = function() { + EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleSpaceMode' })); + }; + + elFilterTypeSelectBox.onclick = onToggleTypeDropdown; + elFilterSearch.onkeyup = refreshEntityList; + elFilterSearch.onsearch = refreshEntityList; + elFilterInView.onclick = toggleFilterInView; + elFilterRadius.onkeyup = onRadiusChange; + elFilterRadius.onchange = onRadiusChange; + elFilterRadius.onclick = onRadiusChange; elInfoToggle.onclick = toggleInfo; - elNoEntitiesInView.style.display = "none"; + // create filter type dropdown checkboxes with label and icon for each type + for (let i = 0; i < FILTER_TYPES.length; ++i) { + let type = FILTER_TYPES[i]; + let typeFilterID = "filter-type-" + type; + let elDiv = document.createElement('div'); + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", typeFilterID); + elLabel.innerText = type; + let elSpan = document.createElement('span'); + elSpan.setAttribute("class", "typeIcon"); + elSpan.innerHTML = ICON_FOR_TYPE[type]; + let elInput = document.createElement('input'); + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", typeFilterID); + elInput.setAttribute("filterType", type); + elInput.checked = true; // all types are checked initially + toggleTypeFilter(elInput, false); // add all types to the initial types filter + elDiv.appendChild(elInput); + elLabel.insertBefore(elSpan, elLabel.childNodes[0]); + elDiv.appendChild(elLabel); + elFilterTypeCheckboxes.appendChild(elDiv); + elDiv.onclick = onToggleTypeFilter; + } entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); - + + entityListContextMenu = new EntityListContextMenu(); + + + function startRenamingEntity(entityID) { + let entity = entitiesByID[entityID]; + if (!entity || entity.locked || !entity.elRow) { + return; + } + + let elCell = entity.elRow.childNodes[COLUMN_INDEX.NAME]; + let elRenameInput = document.createElement("input"); + elRenameInput.setAttribute('class', 'rename-entity'); + elRenameInput.value = entity.name; + let ignoreClicks = function(event) { + event.stopPropagation(); + }; + elRenameInput.onclick = ignoreClicks; + elRenameInput.ondblclick = ignoreClicks; + elRenameInput.onkeyup = function(keyEvent) { + if (keyEvent.key === "Enter") { + elRenameInput.blur(); + } + }; + + elRenameInput.onblur = function(event) { + let value = elRenameInput.value; + EventBridge.emitWebEvent(JSON.stringify({ + type: 'rename', + entityID: entityID, + name: value + })); + entity.name = value; + elCell.innerText = value; + }; + + elCell.innerHTML = ""; + elCell.appendChild(elRenameInput); + + elRenameInput.select(); + } + + entityListContextMenu.setOnSelectedCallback(function(optionName, selectedEntityID) { + switch (optionName) { + case "Cut": + EventBridge.emitWebEvent(JSON.stringify({ type: 'cut' })); + break; + case "Copy": + EventBridge.emitWebEvent(JSON.stringify({ type: 'copy' })); + break; + case "Paste": + EventBridge.emitWebEvent(JSON.stringify({ type: 'paste' })); + break; + case "Rename": + startRenamingEntity(selectedEntityID); + break; + case "Duplicate": + EventBridge.emitWebEvent(JSON.stringify({ type: 'duplicate' })); + break; + case "Delete": + EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); + break; + } + }); + + function onRowContextMenu(clickEvent) { + let entityID = this.dataset.entityID; + + if (!selectedEntities.includes(entityID)) { + let selection = [entityID]; + updateSelectedEntities(selection); + + EventBridge.emitWebEvent(JSON.stringify({ + type: "selectionUpdate", + focus: false, + entityIds: selection, + })); + } + + let enabledContextMenuItems = ['Copy', 'Paste', 'Duplicate']; + if (entitiesByID[entityID] && !entitiesByID[entityID].locked) { + enabledContextMenuItems.push('Cut'); + enabledContextMenuItems.push('Rename'); + enabledContextMenuItems.push('Delete'); + } + + entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems); + } + function onRowClicked(clickEvent) { let entityID = this.dataset.entityID; let selection = [entityID]; - + if (clickEvent.ctrlKey) { let selectedIndex = selectedEntities.indexOf(entityID); if (selectedIndex >= 0) { selection = []; selection = selection.concat(selectedEntities); - selection.splice(selectedIndex, 1) + selection.splice(selectedIndex, 1); } else { selection = selection.concat(selectedEntities); } @@ -220,28 +392,29 @@ function loaded() { } } } else if (!clickEvent.ctrlKey && !clickEvent.shiftKey && selectedEntities.length === 1) { - // if reselecting the same entity then deselect it + // if reselecting the same entity then start renaming it if (selectedEntities[0] === entityID) { - selection = []; + startRenamingEntity(entityID); } } - updateSelectedEntities(selection); + updateSelectedEntities(selection, false); EventBridge.emitWebEvent(JSON.stringify({ type: "selectionUpdate", focus: false, entityIds: selection, })); - - refreshFooter(); } function onRowDoubleClicked() { + let selection = [this.dataset.entityID]; + updateSelectedEntities(selection, false); + EventBridge.emitWebEvent(JSON.stringify({ type: "selectionUpdate", focus: true, - entityIds: [this.dataset.entityID], + entityIds: selection, })); } @@ -288,7 +461,7 @@ function loaded() { hasScript: entity.hasScript, elRow: null, // if this entity has a visible row element assigned to it selected: false // if this entity is selected for edit regardless of having a visible row - } + }; entities.push(entityData); entitiesByID[entityData.id] = entityData; @@ -301,17 +474,16 @@ function loaded() { function refreshEntityList() { PROFILE("refresh-entity-list", function() { PROFILE("filter", function() { - let searchTerm = elFilter.value.toLowerCase(); - if (searchTerm === '') { - visibleEntities = entities.slice(0); - } else { - visibleEntities = entities.filter(function(e) { - return e.name.toLowerCase().indexOf(searchTerm) > -1 - || e.type.toLowerCase().indexOf(searchTerm) > -1 - || e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 - || e.id.toLowerCase().indexOf(searchTerm) > -1; - }); - } + let searchTerm = elFilterSearch.value.toLowerCase(); + visibleEntities = entities.filter(function(e) { + let type = e.type === "Box" || e.type === "Sphere" ? "Shape" : e.type; + let typeFilter = typeFilters.indexOf(type) > -1; + let searchFilter = searchTerm === '' || (e.name.toLowerCase().indexOf(searchTerm) > -1 || + e.type.toLowerCase().indexOf(searchTerm) > -1 || + e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 || + e.id.toLowerCase().indexOf(searchTerm) > -1); + return typeFilter && searchFilter; + }); }); PROFILE("sort", function() { @@ -417,7 +589,7 @@ function loaded() { isBaked: document.querySelector('#entity-isBaked .sort-order'), drawCalls: document.querySelector('#entity-drawCalls .sort-order'), hasScript: document.querySelector('#entity-hasScript .sort-order'), - } + }; function setSortColumn(column) { PROFILE("set-sort-column", function() { if (currentSortColumn === column) { @@ -440,15 +612,8 @@ function loaded() { } function refreshFooter() { - if (selectedEntities.length > 1) { - elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected"; - } else if (selectedEntities.length === 1) { - elFooter.firstChild.nodeValue = "1 entity selected"; - } else if (visibleEntities.length === 1) { - elFooter.firstChild.nodeValue = "1 entity found"; - } else { - elFooter.firstChild.nodeValue = visibleEntities.length + " entities found"; - } + elSelectedEntitiesCount.innerText = selectedEntities.length; + elVisibleEntitiesCount.innerText = visibleEntities.length; } function refreshNoEntitiesMessage() { @@ -459,7 +624,7 @@ function loaded() { } } - function updateSelectedEntities(selectedIDs) { + function updateSelectedEntities(selectedIDs, autoScroll) { let notFound = false; // reset all currently selected entities and their rows first @@ -488,6 +653,28 @@ function loaded() { } }); + if (autoScroll && selectedIDs.length > 0) { + let firstItem = Number.MAX_VALUE; + let lastItem = -1; + let itemFound = false; + visibleEntities.forEach(function(entity, index) { + if (selectedIDs.indexOf(entity.id) !== -1) { + if (firstItem > index) { + firstItem = index; + } + if (lastItem < index) { + lastItem = index; + } + itemFound = true; + } + }); + if (itemFound) { + entityList.scrollToRow(firstItem, lastItem); + } + } + + elToggleSpaceMode.disabled = selectedIDs.length > 1; + refreshFooter(); return notFound; @@ -508,6 +695,7 @@ function loaded() { } row.appendChild(column); } + row.oncontextmenu = onRowContextMenu; row.onclick = onRowClicked; row.ondblclick = onRowDoubleClicked; return row; @@ -588,29 +776,74 @@ function loaded() { function toggleFilterInView() { isFilterInView = !isFilterInView; if (isFilterInView) { - elInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE); - elNoEntitiesInView.style.display = "inline"; + elFilterInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE); } else { - elInView.removeAttribute(FILTER_IN_VIEW_ATTRIBUTE); - elNoEntitiesInView.style.display = "none"; + elFilterInView.removeAttribute(FILTER_IN_VIEW_ATTRIBUTE); } EventBridge.emitWebEvent(JSON.stringify({ type: "filterInView", filterInView: isFilterInView })); refreshEntities(); } - function onFilterChange() { - refreshEntityList(); - entityList.resize(); - } - function onRadiusChange() { - elRadius.value = Math.max(elRadius.value, 0); - elNoEntitiesRadius.firstChild.nodeValue = elRadius.value; - elNoEntitiesMessage.style.display = "none"; - EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value })); + elFilterRadius.value = elFilterRadius.value.replace(/[^0-9]/g, ''); + elFilterRadius.value = Math.max(elFilterRadius.value, 0); + EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); } - + + function isTypeDropdownVisible() { + return elFilterTypeCheckboxes.style.display === "block"; + } + + function toggleTypeDropdown() { + elFilterTypeCheckboxes.style.display = isTypeDropdownVisible() ? "none" : "block"; + } + + function onToggleTypeDropdown(event) { + toggleTypeDropdown(); + event.stopPropagation(); + } + + function toggleTypeFilter(elInput, refresh) { + let type = elInput.getAttribute("filterType"); + let typeChecked = elInput.checked; + + let typeFilterIndex = typeFilters.indexOf(type); + if (!typeChecked && typeFilterIndex > -1) { + typeFilters.splice(typeFilterIndex, 1); + } else if (typeChecked && typeFilterIndex === -1) { + typeFilters.push(type); + } + + if (typeFilters.length === 0) { + elFilterTypeText.innerText = "No Types"; + } else if (typeFilters.length === FILTER_TYPES.length) { + elFilterTypeText.innerText = "All Types"; + } else { + elFilterTypeText.innerText = "Types..."; + } + + if (refresh) { + refreshEntityList(); + } + } + + function onToggleTypeFilter(event) { + let elTarget = event.target; + if (elTarget instanceof HTMLInputElement) { + toggleTypeFilter(elTarget, true); + } + event.stopPropagation(); + } + + function onBodyClick(event) { + // if clicking anywhere outside of the type filter dropdown (since click event bubbled up to onBodyClick and + // propagation wasn't stopped by onToggleTypeFilter or onToggleTypeDropdown) and the dropdown is open then close it + if (isTypeDropdownVisible()) { + toggleTypeDropdown(); + } + } + function toggleInfo(event) { showExtraInfo = !showExtraInfo; if (showExtraInfo) { @@ -624,6 +857,16 @@ function loaded() { event.stopPropagation(); } + function setSpaceMode(spaceMode) { + if (spaceMode === "local") { + elToggleSpaceMode.className = "space-mode-local hifi-edit-button"; + elToggleSpaceMode.innerText = "Local"; + } else { + elToggleSpaceMode.className = "space-mode-world hifi-edit-button"; + elToggleSpaceMode.innerText = "World"; + } + } + document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { return; @@ -646,8 +889,8 @@ function loaded() { data = JSON.parse(data); if (data.type === "clearEntityList") { clearEntities(); - } else if (data.type == "selectionUpdate") { - let notFound = updateSelectedEntities(data.selectedIDs); + } else if (data.type === "selectionUpdate") { + let notFound = updateSelectedEntities(data.selectedIDs, true); if (notFound) { refreshEntities(); } @@ -659,15 +902,18 @@ function loaded() { clearEntities(); } else { updateEntityData(newEntities); - updateSelectedEntities(data.selectedIDs); + updateSelectedEntities(data.selectedIDs, true); } } + setSpaceMode(data.spaceMode); }); } else if (data.type === "removeEntities" && data.deletedIDs !== undefined && data.selectedIDs !== undefined) { removeEntities(data.deletedIDs); - updateSelectedEntities(data.selectedIDs); + updateSelectedEntities(data.selectedIDs, true); } else if (data.type === "deleted" && data.ids) { removeEntities(data.ids); + } else if (data.type === "setSpaceMode") { + setSpaceMode(data.spaceMode); } }); } @@ -678,8 +924,15 @@ function loaded() { augmentSpinButtons(); - // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function (event) { + entityListContextMenu.close(); + + // Disable default right-click context menu which is not visible in the HMD and makes it seem like the app has locked event.preventDefault(); }, false); + + // close context menu when switching focus to another window + $(window).blur(function() { + entityListContextMenu.close(); + }); } diff --git a/scripts/system/html/js/entityListContextMenu.js b/scripts/system/html/js/entityListContextMenu.js new file mode 100644 index 0000000000..d71719f252 --- /dev/null +++ b/scripts/system/html/js/entityListContextMenu.js @@ -0,0 +1,163 @@ +// +// entityListContextMenu.js +// +// exampleContextMenus.js was originally created by David Rowe on 22 Aug 2018. +// Modified to entityListContextMenu.js by Thijs Wenker on 10 Oct 2018 +// 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 +// + +/* eslint-env browser */ +const CONTEXT_MENU_CLASS = "context-menu"; + +/** + * ContextMenu class for EntityList + * @constructor + */ +function EntityListContextMenu() { + this._elContextMenu = null; + this._onSelectedCallback = null; + this._listItems = []; + this._initialize(); +} + +EntityListContextMenu.prototype = { + + /** + * @private + */ + _elContextMenu: null, + + /** + * @private + */ + _onSelectedCallback: null, + + /** + * @private + */ + _selectedEntityID: null, + + /** + * @private + */ + _listItems: null, + + /** + * Close the context menu + */ + close: function() { + if (this.isContextMenuOpen()) { + this._elContextMenu.style.display = "none"; + } + }, + + isContextMenuOpen: function() { + return this._elContextMenu.style.display === "block"; + }, + + /** + * Open the context menu + * @param clickEvent + * @param selectedEntityID + * @param enabledOptions + */ + open: function(clickEvent, selectedEntityID, enabledOptions) { + this._selectedEntityID = selectedEntityID; + + this._listItems.forEach(function(listItem) { + let enabled = enabledOptions.includes(listItem.label); + listItem.enabled = enabled; + listItem.element.setAttribute('class', enabled ? '' : 'disabled'); + }); + + this._elContextMenu.style.display = "block"; + this._elContextMenu.style.left + = Math.min(clickEvent.pageX, document.body.offsetWidth - this._elContextMenu.offsetWidth).toString() + "px"; + this._elContextMenu.style.top + = Math.min(clickEvent.pageY, document.body.clientHeight - this._elContextMenu.offsetHeight).toString() + "px"; + clickEvent.stopPropagation(); + }, + + /** + * Set the callback for when a menu item is selected + * @param onSelectedCallback + */ + setOnSelectedCallback: function(onSelectedCallback) { + this._onSelectedCallback = onSelectedCallback; + }, + + /** + * Add a labeled item to the context menu + * @param itemLabel + * @private + */ + _addListItem: function(itemLabel) { + let elListItem = document.createElement("li"); + elListItem.innerText = itemLabel; + + let listItem = { + label: itemLabel, + element: elListItem, + enabled: false + }; + + elListItem.addEventListener("click", function () { + if (listItem.enabled && this._onSelectedCallback) { + this._onSelectedCallback.call(this, itemLabel, this._selectedEntityID); + } + }.bind(this), false); + + elListItem.setAttribute('class', 'disabled'); + + this._listItems.push(listItem); + this._elContextMenu.appendChild(elListItem); + }, + + /** + * Add a separator item to the context menu + * @private + */ + _addListSeparator: function() { + let elListItem = document.createElement("li"); + elListItem.setAttribute('class', 'separator'); + this._elContextMenu.appendChild(elListItem); + }, + + /** + * Initialize the context menu. + * @private + */ + _initialize: function() { + this._elContextMenu = document.createElement("ul"); + this._elContextMenu.setAttribute("class", CONTEXT_MENU_CLASS); + document.body.appendChild(this._elContextMenu); + + this._addListItem("Cut"); + this._addListItem("Copy"); + this._addListItem("Paste"); + this._addListSeparator(); + this._addListItem("Rename"); + this._addListItem("Duplicate"); + this._addListItem("Delete"); + + // Ignore clicks on context menu background or separator. + this._elContextMenu.addEventListener("click", function(event) { + // Sink clicks on context menu background or separator but let context menu item clicks through. + if (event.target.classList.contains(CONTEXT_MENU_CLASS)) { + event.stopPropagation(); + } + }); + + // Provide means to close context menu without clicking menu item. + document.body.addEventListener("click", this.close.bind(this)); + document.body.addEventListener("keydown", function(event) { + // Close context menu with Esc key. + if (this.isContextMenuOpen() && event.key === "Escape") { + this.close(); + } + }.bind(this)); + } +}; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index de9027586e..f2c84d2f36 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1,18 +1,16 @@ // entityProperties.js // // Created by Ryan Huffman on 13 Nov 2014 +// Modified by David Back on 19 Oct 2018 // Copyright 2014 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 -/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge, - HifiEntityUI, JSONEditor, openEventBridge, setTimeout, window, _ $ */ - -var PI = 3.14159265358979; -var DEGREES_TO_RADIANS = PI / 180.0; -var RADIANS_TO_DEGREES = 180.0 / PI; -var ICON_FOR_TYPE = { +/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, + EventBridge, JSONEditor, openEventBridge, setTimeout, window, _ $ */ + +const ICON_FOR_TYPE = { Box: "V", Sphere: "n", Shape: "n", @@ -27,16 +25,1341 @@ var ICON_FOR_TYPE = { Multiple: "", PolyLine: "", Material: "" +}; + +const DEGREES_TO_RADIANS = Math.PI / 180.0; + +const NO_SELECTION = "No selection"; + +const PROPERTY_SPACE_MODE = { + ALL: 0, + LOCAL: 1, + WORLD: 2 }; -var EDITOR_TIMEOUT_DURATION = 1500; -var KEY_P = 80; // Key code for letter p used for Parenting hotkey. +const GROUPS = [ + { + id: "base", + properties: [ + { + label: NO_SELECTION, + type: "icon", + icons: ICON_FOR_TYPE, + propertyID: "type", + }, + { + label: "Name", + type: "string", + propertyID: "name", + }, + { + label: "ID", + type: "string", + propertyID: "id", + readOnly: true, + }, + { + label: "Description", + type: "string", + propertyID: "description", + }, + { + label: "Parent", + type: "string", + propertyID: "parentID", + }, + { + label: "Parent Joint Index", + type: "number", + propertyID: "parentJointIndex", + }, + { + label: "Locked", + glyph: "", + type: "bool", + propertyID: "locked", + }, + { + label: "Visible", + glyph: "", + type: "bool", + propertyID: "visible", + }, + ] + }, + { + id: "shape", + addToGroup: "base", + properties: [ + { + label: "Shape", + type: "dropdown", + options: { Cube: "Box", Sphere: "Sphere", Tetrahedron: "Tetrahedron", Octahedron: "Octahedron", + Icosahedron: "Icosahedron", Dodecahedron: "Dodecahedron", Hexagon: "Hexagon", + Triangle: "Triangle", Octagon: "Octagon", Cylinder: "Cylinder", Cone: "Cone", + Circle: "Circle", Quad: "Quad" }, + propertyID: "shape", + }, + { + label: "Color", + type: "color", + propertyID: "color", + }, + ] + }, + { + id: "text", + addToGroup: "base", + properties: [ + { + label: "Text", + type: "string", + propertyID: "text", + }, + { + label: "Text Color", + type: "color", + propertyID: "textColor", + }, + { + label: "Background Color", + type: "color", + propertyID: "backgroundColor", + }, + { + label: "Line Height", + type: "number", + min: 0, + step: 0.005, + decimals: 4, + unit: "m", + propertyID: "lineHeight" + }, + { + label: "Face Camera", + type: "bool", + propertyID: "faceCamera" + }, + ] + }, + { + id: "zone", + addToGroup: "base", + properties: [ + { + label: "Flying Allowed", + type: "bool", + propertyID: "flyingAllowed", + }, + { + label: "Ghosting Allowed", + type: "bool", + propertyID: "ghostingAllowed", + }, + { + label: "Filter", + type: "string", + propertyID: "filterURL", + }, + { + label: "Key Light", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "keyLightMode", + + }, + { + label: "Key Light Color", + type: "color", + propertyID: "keyLight.color", + showPropertyRule: { "keyLightMode": "enabled" }, + }, + { + label: "Light Intensity", + type: "number", + min: 0, + max: 10, + step: 0.1, + decimals: 2, + propertyID: "keyLight.intensity", + showPropertyRule: { "keyLightMode": "enabled" }, + }, + { + label: "Light Horizontal Angle", + type: "number", + multiplier: DEGREES_TO_RADIANS, + decimals: 2, + unit: "deg", + propertyID: "keyLight.direction.x", + showPropertyRule: { "keyLightMode": "enabled" }, + }, + { + label: "Light Vertical Angle", + type: "number", + multiplier: DEGREES_TO_RADIANS, + decimals: 2, + unit: "deg", + propertyID: "keyLight.direction.y", + showPropertyRule: { "keyLightMode": "enabled" }, + }, + { + label: "Cast Shadows", + type: "bool", + propertyID: "keyLight.castShadows", + showPropertyRule: { "keyLightMode": "enabled" }, + }, + { + label: "Skybox", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "skyboxMode", + }, + { + label: "Skybox Color", + type: "color", + propertyID: "skybox.color", + showPropertyRule: { "skyboxMode": "enabled" }, + }, + { + label: "Skybox Source", + type: "string", + propertyID: "skybox.url", + showPropertyRule: { "skyboxMode": "enabled" }, + }, + { + label: "Ambient Light", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "ambientLightMode", + }, + { + label: "Ambient Intensity", + type: "number", + min: 0, + max: 10, + step: 0.1, + decimals: 2, + propertyID: "ambientLight.ambientIntensity", + showPropertyRule: { "ambientLightMode": "enabled" }, + }, + { + label: "Ambient Source", + type: "string", + propertyID: "ambientLight.ambientURL", + showPropertyRule: { "ambientLightMode": "enabled" }, + }, + { + type: "buttons", + buttons: [ { id: "copy", label: "Copy from Skybox", + className: "black", onClick: copySkyboxURLToAmbientURL } ], + propertyID: "copyURLToAmbient", + showPropertyRule: { "skyboxMode": "enabled" }, + }, + { + label: "Haze", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "hazeMode", + }, + { + label: "Range", + type: "number", + min: 5, + max: 10000, + step: 5, + decimals: 0, + unit: "m", + propertyID: "haze.hazeRange", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Use Altitude", + type: "bool", + propertyID: "haze.hazeAltitudeEffect", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Base", + type: "number", + min: -1000, + max: 1000, + step: 10, + decimals: 0, + unit: "m", + propertyID: "haze.hazeBaseRef", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Ceiling", + type: "number", + min: -1000, + max: 5000, + step: 10, + decimals: 0, + unit: "m", + propertyID: "haze.hazeCeiling", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Haze Color", + type: "color", + propertyID: "haze.hazeColor", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Background Blend", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "haze.hazeBackgroundBlend", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Enable Glare", + type: "bool", + propertyID: "haze.hazeEnableGlare", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Glare Color", + type: "color", + propertyID: "haze.hazeGlareColor", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Glare Angle", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + propertyID: "haze.hazeGlareAngle", + showPropertyRule: { "hazeMode": "enabled" }, + }, + { + label: "Bloom", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "bloomMode", + }, + { + label: "Bloom Intensity", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "bloom.bloomIntensity", + showPropertyRule: { "bloomMode": "enabled" }, + }, + { + label: "Bloom Threshold", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "bloom.bloomThreshold", + showPropertyRule: { "bloomMode": "enabled" }, + }, + { + label: "Bloom Size", + type: "slider", + min: 0, + max: 2, + step: 0.01, + decimals: 2, + propertyID: "bloom.bloomSize", + showPropertyRule: { "bloomMode": "enabled" }, + }, + ] + }, + { + id: "model", + addToGroup: "base", + properties: [ + { + label: "Model", + type: "string", + propertyID: "modelURL", + }, + { + label: "Collision Shape", + type: "dropdown", + options: { "none": "No Collision", "box": "Box", "sphere": "Sphere", "compound": "Compound" , + "simple-hull": "Basic - Whole model", "simple-compound": "Good - Sub-meshes" , + "static-mesh": "Exact - All polygons (non-dynamic only)" }, + propertyID: "shapeType", + }, + { + label: "Compound Shape", + type: "string", + propertyID: "compoundShapeURL", + }, + { + label: "Animation", + type: "string", + propertyID: "animation.url", + }, + { + label: "Play Automatically", + type: "bool", + propertyID: "animation.running", + }, + { + label: "Loop", + type: "bool", + propertyID: "animation.loop", + }, + { + label: "Allow Transition", + type: "bool", + propertyID: "animation.allowTranslation", + }, + { + label: "Hold", + type: "bool", + propertyID: "animation.hold", + }, + { + label: "Animation Frame", + type: "number", + propertyID: "animation.currentFrame", + }, + { + label: "First Frame", + type: "number", + propertyID: "animation.firstFrame", + }, + { + label: "Last Frame", + type: "number", + propertyID: "animation.lastFrame", + }, + { + label: "Animation FPS", + type: "number", + propertyID: "animation.fps", + }, + { + label: "Texture", + type: "textarea", + propertyID: "textures", + }, + { + label: "Original Texture", + type: "textarea", + propertyID: "originalTextures", + readOnly: true, + }, + ] + }, + { + id: "image", + addToGroup: "base", + properties: [ + { + label: "Image", + type: "string", + propertyID: "image", + }, + ] + }, + { + id: "web", + addToGroup: "base", + properties: [ + { + label: "Source", + type: "string", + propertyID: "sourceUrl", + }, + { + label: "Source Resolution", + type: "number", + propertyID: "dpi", + }, + ] + }, + { + id: "light", + addToGroup: "base", + properties: [ + { + label: "Light Color", + type: "color", + propertyID: "lightColor", + propertyName: "color", // actual entity property name + }, + { + label: "Intensity", + type: "number", + min: 0, + step: 0.1, + decimals: 1, + propertyID: "intensity", + }, + { + label: "Fall-Off Radius", + type: "number", + min: 0, + step: 0.1, + decimals: 1, + unit: "m", + propertyID: "falloffRadius", + }, + { + label: "Spotlight", + type: "bool", + propertyID: "isSpotlight", + }, + { + label: "Spotlight Exponent", + type: "number", + step: 0.01, + decimals: 2, + propertyID: "exponent", + }, + { + label: "Spotlight Cut-Off", + type: "number", + step: 0.01, + decimals: 2, + propertyID: "cutoff", + }, + ] + }, + { + id: "material", + addToGroup: "base", + properties: [ + { + label: "Material URL", + type: "string", + propertyID: "materialURL", + }, + { + label: "Material Data", + type: "textarea", + buttons: [ { id: "clear", label: "Clear Material Data", className: "red", onClick: clearMaterialData }, + { id: "edit", label: "Edit as JSON", className: "blue", onClick: newJSONMaterialEditor }, + { id: "save", label: "Save Material Data", className: "black", onClick: saveMaterialData } ], + propertyID: "materialData", + }, + { + label: "Submesh to Replace", + type: "number", + min: 0, + step: 1, + propertyID: "submeshToReplace", + }, + { + label: "Material Name to Replace", + type: "string", + propertyID: "materialNameToReplace", + }, + { + label: "Select Submesh", + type: "bool", + propertyID: "selectSubmesh", + }, + { + label: "Priority", + type: "number", + min: 0, + propertyID: "priority", + }, + { + label: "Material Position", + type: "vec2", + vec2Type: "xy", + min: 0, + max: 1, + step: 0.1, + decimals: 4, + subLabels: [ "x", "y" ], + propertyID: "materialMappingPos", + }, + { + label: "Material Scale", + type: "vec2", + vec2Type: "wh", + min: 0, + step: 0.1, + decimals: 4, + subLabels: [ "width", "height" ], + propertyID: "materialMappingScale", + }, + { + label: "Material Rotation", + type: "number", + step: 0.1, + decimals: 2, + unit: "deg", + propertyID: "materialMappingRot", + }, + ] + }, + { + id: "particles", + addToGroup: "base", + properties: [ + { + label: "Emit", + type: "bool", + propertyID: "isEmitting", + }, + { + label: "Lifespan", + type: "slider", + unit: "s", + min: 0.01, + max: 10, + step: 0.01, + decimals: 2, + propertyID: "lifespan", + }, + { + label: "Max Particles", + type: "slider", + min: 1, + max: 10000, + step: 1, + propertyID: "maxParticles", + }, + { + label: "Texture", + type: "texture", + propertyID: "particleTextures", + propertyName: "textures", // actual entity property name + }, + ] + }, + { + id: "particles_emit", + label: "EMIT", + properties: [ + { + label: "Emit Rate", + type: "slider", + min: 1, + max: 1000, + step: 1, + propertyID: "emitRate", + }, + { + label: "Emit Speed", + type: "slider", + min: 0, + max: 5, + step: 0.01, + decimals: 2, + propertyID: "emitSpeed", + }, + { + label: "Speed Spread", + type: "slider", + min: 0, + max: 5, + step: 0.01, + decimals: 2, + propertyID: "speedSpread", + }, + { + label: "Emit Dimensions", + type: "vec3", + vec3Type: "xyz", + min: 0, + step: 0.01, + round: 100, + subLabels: [ "x", "y", "z" ], + propertyID: "emitDimensions", + }, + { + label: "Emit Radius Start", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "emitRadiusStart" + }, + { + label: "Emit Orientation", + type: "vec3", + vec3Type: "pyr", + step: 0.01, + round: 100, + subLabels: [ "pitch", "yaw", "roll" ], + unit: "deg", + propertyID: "emitOrientation", + }, + { + label: "Trails", + type: "bool", + propertyID: "emitterShouldTrail", + }, + ] + }, + { + id: "particles_size", + label: "SIZE", + properties: [ + { + label: "Size", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "particleRadius", + }, + { + label: "Size Start", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusStart", + fallbackProperty: "particleRadius", + }, + { + label: "Size Finish", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusFinish", + fallbackProperty: "particleRadius", + }, + { + label: "Size Spread", + type: "slider", + min: 0, + max: 4, + step: 0.01, + decimals: 2, + propertyID: "radiusSpread", + }, + ] + }, + { + id: "particles_color", + label: "COLOR", + properties: [ + { + label: "Color", + type: "color", + propertyID: "particleColor", + propertyName: "color", // actual entity property name + }, + { + label: "Color Start", + type: "color", + propertyID: "colorStart", + fallbackProperty: "color", + }, + { + label: "Color Finish", + type: "color", + propertyID: "colorFinish", + fallbackProperty: "color", + }, + { + label: "Color Spread", + type: "color", + propertyID: "colorSpread", + }, + ] + }, + { + id: "particles_alpha", + label: "ALPHA", + properties: [ + { + label: "Alpha", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alpha", + }, + { + label: "Alpha Start", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaStart", + fallbackProperty: "alpha", + }, + { + label: "Alpha Finish", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaFinish", + fallbackProperty: "alpha", + }, + { + label: "Alpha Spread", + type: "slider", + min: 0, + max: 1, + step: 0.01, + decimals: 2, + propertyID: "alphaSpread", + }, + ] + }, + { + id: "particles_acceleration", + label: "ACCELERATION", + properties: [ + { + label: "Emit Acceleration", + type: "vec3", + vec3Type: "xyz", + step: 0.01, + round: 100, + subLabels: [ "x", "y", "z" ], + propertyID: "emitAcceleration", + }, + { + label: "Acceleration Spread", + type: "vec3", + vec3Type: "xyz", + step: 0.01, + round: 100, + subLabels: [ "x", "y", "z" ], + propertyID: "accelerationSpread", + }, + ] + }, + { + id: "particles_spin", + label: "SPIN", + properties: [ + { + label: "Spin", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "particleSpin", + }, + { + label: "Spin Start", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinStart", + fallbackProperty: "particleSpin", + }, + { + label: "Spin Finish", + type: "slider", + min: -360, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinFinish", + fallbackProperty: "particleSpin", + }, + { + label: "Spin Spread", + type: "slider", + min: 0, + max: 360, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinSpread", + }, + { + label: "Rotate with Entity", + type: "bool", + propertyID: "rotateWithEntity", + }, + ] + }, + { + id: "particles_constraints", + label: "CONSTRAINTS", + properties: [ + { + label: "Horizontal Angle Start", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarStart", + }, + { + label: "Horizontal Angle Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarFinish", + }, + { + label: "Vertical Angle Start", + type: "slider", + min: -180, + max: 0, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthStart", + }, + { + label: "Vertical Angle Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + decimals: 0, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthFinish", + }, + ] + }, + { + id: "spatial", + label: "SPATIAL", + properties: [ + { + label: "Position", + type: "vec3", + vec3Type: "xyz", + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m", + propertyID: "position", + spaceMode: PROPERTY_SPACE_MODE.WORLD, + }, + { + label: "Local Position", + type: "vec3", + vec3Type: "xyz", + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m", + propertyID: "localPosition", + spaceMode: PROPERTY_SPACE_MODE.LOCAL, + }, + { + label: "Rotation", + type: "vec3", + vec3Type: "pyr", + step: 0.1, + decimals: 4, + subLabels: [ "pitch", "yaw", "roll" ], + unit: "deg", + propertyID: "rotation", + spaceMode: PROPERTY_SPACE_MODE.WORLD, + }, + { + label: "Local Rotation", + type: "vec3", + vec3Type: "pyr", + step: 0.1, + decimals: 4, + subLabels: [ "pitch", "yaw", "roll" ], + unit: "deg", + propertyID: "localRotation", + spaceMode: PROPERTY_SPACE_MODE.LOCAL, + }, + { + label: "Dimensions", + type: "vec3", + vec3Type: "xyz", + min: 0, + step: 0.1, + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m", + propertyID: "dimensions", + spaceMode: PROPERTY_SPACE_MODE.WORLD, + }, + { + label: "Local Dimensions", + type: "vec3", + vec3Type: "xyz", + min: 0, + step: 0.1, + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m", + propertyID: "localDimensions", + spaceMode: PROPERTY_SPACE_MODE.LOCAL, + }, + { + label: "Scale", + type: "number", + defaultValue: 100, + unit: "%", + buttons: [ { id: "rescale", label: "Rescale", className: "blue", onClick: rescaleDimensions }, + { id: "reset", label: "Reset Dimensions", className: "red", onClick: resetToNaturalDimensions } ], + propertyID: "scale", + }, + { + label: "Pivot", + type: "vec3", + vec3Type: "xyz", + step: 0.1, + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "(ratio of dimension)", + propertyID: "registrationPoint", + }, + { + label: "Align", + type: "buttons", + buttons: [ { id: "selection", label: "Selection to Grid", className: "black", onClick: moveSelectionToGrid }, + { id: "all", label: "All to Grid", className: "black", onClick: moveAllToGrid } ], + propertyID: "alignToGrid", + }, + ] + }, + { + id: "behavior", + label: "BEHAVIOR", + properties: [ + { + label: "Grabbable", + type: "bool", + propertyID: "grab.grabbable", + }, + { + label: "Cloneable", + type: "bool", + propertyID: "cloneable", + }, + { + label: "Clone Lifetime", + type: "number", + unit: "s", + propertyID: "cloneLifetime", + showPropertyRule: { "cloneable": "true" }, + }, + { + label: "Clone Limit", + type: "number", + propertyID: "cloneLimit", + showPropertyRule: { "cloneable": "true" }, + }, + { + label: "Clone Dynamic", + type: "bool", + propertyID: "cloneDynamic", + showPropertyRule: { "cloneable": "true" }, + }, + { + label: "Clone Avatar Entity", + type: "bool", + propertyID: "cloneAvatarEntity", + showPropertyRule: { "cloneable": "true" }, + }, + { + label: "Triggerable", + type: "bool", + propertyID: "grab.triggerable", + }, + { + label: "Follow Controller", + type: "bool", + propertyID: "grab.grabFollowsController", + }, + { + label: "Cast shadows", + type: "bool", + propertyID: "canCastShadow", + }, + { + label: "Link", + type: "string", + propertyID: "href", + }, + { + label: "Script", + type: "string", + buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadScripts } ], + propertyID: "script", + }, + { + label: "Server Script", + type: "string", + buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ], + propertyID: "serverScripts", + }, + { + label: "Lifetime", + type: "number", + unit: "s", + propertyID: "lifetime", + }, + { + label: "User Data", + type: "textarea", + buttons: [ { id: "clear", label: "Clear User Data", className: "red", onClick: clearUserData }, + { id: "edit", label: "Edit as JSON", className: "blue", onClick: newJSONEditor }, + { id: "save", label: "Save User Data", className: "black", onClick: saveUserData } ], + propertyID: "userData", + }, + ] + }, + { + id: "collision", + label: "COLLISION", + properties: [ + { + label: "Collides", + type: "bool", + inverse: true, + propertyID: "collisionless", + }, + { + label: "Collides With", + type: "sub-header", + propertyID: "collidesWithHeader", // not actually a property but used for naming/storing this element + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "Static Entities", + type: "bool", + propertyID: "collidesWithStatic", + propertyName: "static", // actual subProperty name + subPropertyOf: "collidesWith", + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "Kinematic Entities", + type: "bool", + propertyID: "collidesWithKinematic", + propertyName: "kinematic", // actual subProperty name + subPropertyOf: "collidesWith", + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "Dynamic Entities", + type: "bool", + propertyID: "collidesWithDynamic", + propertyName: "dynamic", // actual subProperty name + subPropertyOf: "collidesWith", + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "My Avatar", + type: "bool", + propertyID: "collidesWithMyAvatar", + propertyName: "myAvatar", // actual subProperty name + subPropertyOf: "collidesWith", + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "Other Avatars", + type: "bool", + propertyID: "collidesWithOtherAvatar", + propertyName: "otherAvatar", // actual subProperty name + subPropertyOf: "collidesWith", + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "Collision sound URL", + type: "string", + propertyID: "collisionSoundURL", + showPropertyRule: { "collisionless": "false" }, + }, + { + label: "Dynamic", + type: "bool", + propertyID: "dynamic", + }, + ] + }, + { + id: "physics", + label: "PHYSICS", + properties: [ + { + label: "Linear Velocity", + type: "vec3", + vec3Type: "xyz", + decimals: 4, + subLabels: [ "x", "y", "z" ], + unit: "m/s", + propertyID: "localVelocity", + }, + { + label: "Linear Damping", + type: "number", + decimals: 2, + propertyID: "damping", + }, + { + label: "Angular Velocity", + type: "vec3", + vec3Type: "pyr", + multiplier: DEGREES_TO_RADIANS, + decimals: 4, + subLabels: [ "pitch", "yaw", "roll" ], + unit: "deg/s", + propertyID: "localAngularVelocity", + }, + { + label: "Angular Damping", + type: "number", + decimals: 4, + propertyID: "angularDamping", + }, + { + label: "Bounciness", + type: "number", + decimals: 4, + propertyID: "restitution", + }, + { + label: "Friction", + type: "number", + decimals: 4, + propertyID: "friction", + }, + { + label: "Density", + type: "number", + decimals: 4, + propertyID: "density", + }, + { + label: "Gravity", + type: "vec3", + vec3Type: "xyz", + subLabels: [ "x", "y", "z" ], + unit: "m/s2", + propertyID: "gravity", + }, + { + label: "Acceleration", + type: "vec3", + vec3Type: "xyz", + subLabels: [ "x", "y", "z" ], + decimals: 4, + unit: "m/s2", + propertyID: "acceleration", + }, + ] + }, +]; + +const GROUPS_PER_TYPE = { + None: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], + Shape: [ 'base', 'shape', 'spatial', 'behavior', 'collision', 'physics' ], + Text: [ 'base', 'text', 'spatial', 'behavior', 'collision', 'physics' ], + Zone: [ 'base', 'zone', 'spatial', 'behavior', 'collision', 'physics' ], + Model: [ 'base', 'model', 'spatial', 'behavior', 'collision', 'physics' ], + Image: [ 'base', 'image', 'spatial', 'behavior', 'collision', 'physics' ], + Web: [ 'base', 'web', 'spatial', 'behavior', 'collision', 'physics' ], + Light: [ 'base', 'light', 'spatial', 'behavior', 'collision', 'physics' ], + Material: [ 'base', 'material', 'spatial', 'behavior' ], + ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', 'particles_alpha', + 'particles_acceleration', 'particles_spin', 'particles_constraints', 'spatial', 'behavior', 'physics' ], + Multiple: [ 'base', 'spatial', 'behavior', 'collision', 'physics' ], +}; + +const EDITOR_TIMEOUT_DURATION = 1500; +const DEBOUNCE_TIMEOUT = 125; + +const COLOR_MIN = 0; +const COLOR_MAX = 255; +const COLOR_STEP = 1; + +const KEY_P = 80; // Key code for letter p used for Parenting hotkey. + +const MATERIAL_PREFIX_STRING = "mat::"; + +const PENDING_SCRIPT_STATUS = "[ Fetching status ]"; +const NOT_RUNNING_SCRIPT_STATUS = "Not running"; +const ENTITY_SCRIPT_STATUS = { + pending: "Pending", + loading: "Loading", + error_loading_script: "Error loading script", // eslint-disable-line camelcase + error_running_script: "Error running script", // eslint-disable-line camelcase + running: "Running", + unloaded: "Unloaded" +}; + +const PROPERTY_NAME_DIVISION = { + GROUP: 0, + PROPERTY: 1, + SUBPROPERTY: 2, +}; + +const VECTOR_ELEMENTS = { + X_INPUT: 0, + Y_INPUT: 1, + Z_INPUT: 2, +}; + +const COLOR_ELEMENTS = { + COLOR_PICKER: 0, + RED_INPUT: 1, + GREEN_INPUT: 2, + BLUE_INPUT: 3, +}; + +const SLIDER_ELEMENTS = { + SLIDER: 0, + NUMBER_INPUT: 1, +}; + +const ICON_ELEMENTS = { + ICON: 0, + LABEL: 1, +}; + +const TEXTURE_ELEMENTS = { + IMAGE: 0, + TEXT_INPUT: 1, +}; + +const JSON_EDITOR_ROW_DIV_INDEX = 2; + +var elGroups = {}; +var properties = {}; var colorPickers = {}; +var particlePropertyUpdates = {}; +var selectedEntityProperties; var lastEntityID = null; - -var MATERIAL_PREFIX_STRING = "mat::"; - -var PENDING_SCRIPT_STATUS = "[ Fetching status ]"; +var createAppTooltip = new CreateAppTooltip(); +let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; function debugPrint(message) { EventBridge.emitWebEvent( @@ -47,16 +1370,44 @@ function debugPrint(message) { ); } + +/** + * GENERAL PROPERTY/GROUP FUNCTIONS + */ + +function getPropertyInputElement(propertyID) { + let property = properties[propertyID]; + switch (property.data.type) { + case 'string': + case 'bool': + case 'number': + case 'slider': + case 'dropdown': + case 'textarea': + case 'texture': + return property.elInput; + case 'vec3': + case 'vec2': + return { x: property.elInputX, y: property.elInputY, z: property.elInputZ }; + case 'color': + return { red: property.elInputR, green: property.elInputG, blue: property.elInputB }; + case 'icon': + return property.elLabel; + default: + return undefined; + } +} + function enableChildren(el, selector) { - var elSelectors = el.querySelectorAll(selector); - for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { + let elSelectors = el.querySelectorAll(selector); + for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { elSelectors[selectorIndex].removeAttribute('disabled'); } } function disableChildren(el, selector) { - var elSelectors = el.querySelectorAll(selector); - for (var selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { + let elSelectors = el.querySelectorAll(selector); + for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { elSelectors[selectorIndex].setAttribute('disabled', 'disabled'); } } @@ -64,7 +1415,7 @@ function disableChildren(el, selector) { function enableProperties() { enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); enableChildren(document, ".colpick"); - var elLocked = document.getElementById("property-locked"); + let elLocked = getPropertyInputElement("locked"); if (elLocked.checked === false) { removeStaticUserData(); @@ -72,205 +1423,261 @@ function enableProperties() { } } - function disableProperties() { disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); disableChildren(document, ".colpick"); - for (var pickKey in colorPickers) { + for (let pickKey in colorPickers) { colorPickers[pickKey].colpickHide(); } - var elLocked = document.getElementById("property-locked"); + let elLocked = getPropertyInputElement("locked"); if (elLocked.checked === true) { - if ($('#userdata-editor').css('display') === "block") { + if ($('#property-userData-editor').css('display') === "block") { showStaticUserData(); } - if ($('#materialdata-editor').css('display') === "block") { + if ($('#property-materialData-editor').css('display') === "block") { showStaticMaterialData(); } } } -function showElements(els, show) { - for (var i = 0; i < els.length; i++) { - els[i].style.display = (show) ? 'table' : 'none'; +function showPropertyElement(propertyID, show) { + let elProperty = properties[propertyID].elProperty; + elProperty.style.display = show ? "table" : "none"; +} + +function resetProperties() { + for (let propertyID in properties) { + let property = properties[propertyID]; + let propertyData = property.data; + + switch (propertyData.type) { + case 'string': { + property.elInput.value = ""; + break; + } + case 'bool': { + property.elInput.checked = false; + break; + } + case 'number': + case 'slider': { + if (propertyData.defaultValue !== undefined) { + property.elInput.value = propertyData.defaultValue; + } else { + property.elInput.value = ""; + } + if (property.elSlider !== undefined) { + property.elSlider.value = property.elInput.value; + } + break; + } + case 'vec3': + case 'vec2': { + property.elInputX.value = ""; + property.elInputY.value = ""; + if (property.elInputZ !== undefined) { + property.elInputZ.value = ""; + } + break; + } + case 'color': { + property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; + property.elInputR.value = ""; + property.elInputG.value = ""; + property.elInputB.value = ""; + break; + } + case 'dropdown': { + property.elInput.value = ""; + setDropdownText(property.elInput); + break; + } + case 'textarea': { + property.elInput.value = ""; + setTextareaScrolling(property.elInput); + break; + } + case 'icon': { + property.elSpan.style.display = "none"; + property.elLabel.innerHTML = propertyData.label; + break; + } + case 'texture': { + property.elInput.value = ""; + property.elInput.imageLoad(property.elInput.value); + break; + } + } + + let showPropertyRules = properties[propertyID].showPropertyRules; + if (showPropertyRules !== undefined) { + for (let propertyToHide in showPropertyRules) { + showPropertyElement(propertyToHide, false); + } + } + } + + let elServerScriptError = document.getElementById("property-serverScripts-error"); + let elServerScriptStatus = document.getElementById("property-serverScripts-status"); + elServerScriptError.parentElement.style.display = "none"; + elServerScriptStatus.innerText = NOT_RUNNING_SCRIPT_STATUS; +} + +function showGroupsForType(type) { + if (type === "Box" || type === "Sphere") { + type = "Shape"; + } + + let typeGroups = GROUPS_PER_TYPE[type]; + + for (let groupKey in elGroups) { + let elGroup = elGroups[groupKey]; + if (typeGroups && typeGroups.indexOf(groupKey) > -1) { + elGroup.style.display = "block"; + } else { + elGroup.style.display = "none"; + } } } -function updateProperty(propertyName, propertyValue) { - var properties = {}; - properties[propertyName] = propertyValue; - updateProperties(properties); +function getPropertyValue(originalPropertyName) { + // if this is a compound property name (i.e. animation.running) + // then split it by . up to 3 times to find property value + let propertyValue; + let splitPropertyName = originalPropertyName.split('.'); + if (splitPropertyName.length > 1) { + let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; + let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; + let groupProperties = selectedEntityProperties[propertyGroupName]; + if (groupProperties === undefined || groupProperties[propertyName] === undefined) { + return undefined; + } + if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUBPROPERTY + 1) { + let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUBPROPERTY]; + propertyValue = groupProperties[propertyName][subPropertyName]; + } else { + propertyValue = groupProperties[propertyName]; + } + } else { + propertyValue = selectedEntityProperties[originalPropertyName]; + } + return propertyValue; } -function updateProperties(properties) { + +/** + * PROPERTY UPDATE FUNCTIONS + */ + +function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { + let propertyUpdate = {}; + // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times + let splitPropertyName = originalPropertyName.split('.'); + if (splitPropertyName.length > 1) { + let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; + let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; + propertyUpdate[propertyGroupName] = {}; + if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUBPROPERTY + 1) { + let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUBPROPERTY]; + propertyUpdate[propertyGroupName][propertyName] = {}; + propertyUpdate[propertyGroupName][propertyName][subPropertyName] = propertyValue; + } else { + propertyUpdate[propertyGroupName][propertyName] = propertyValue; + } + } else { + propertyUpdate[originalPropertyName] = propertyValue; + } + // queue up particle property changes with the debounced sync to avoid + // causing particle emitting to reset excessively with each value change + if (isParticleProperty) { + Object.keys(propertyUpdate).forEach(function (propertyUpdateKey) { + particlePropertyUpdates[propertyUpdateKey] = propertyUpdate[propertyUpdateKey]; + }); + particleSyncDebounce(); + } else { + updateProperties(propertyUpdate); + } +} + +var particleSyncDebounce = _.debounce(function () { + updateProperties(particlePropertyUpdates); + particlePropertyUpdates = {}; +}, DEBOUNCE_TIMEOUT); + +function updateProperties(propertiesToUpdate) { EventBridge.emitWebEvent(JSON.stringify({ id: lastEntityID, type: "update", - properties: properties + properties: propertiesToUpdate })); } -function createEmitCheckedPropertyUpdateFunction(propertyName) { +function createEmitTextPropertyUpdateFunction(propertyName, isParticleProperty) { return function() { - updateProperty(propertyName, this.checked); + updateProperty(propertyName, this.value, isParticleProperty); }; } -function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { +function createEmitCheckedPropertyUpdateFunction(propertyName, inverse, isParticleProperty) { return function() { - var properties = {}; - properties[group] = {}; - properties[group][propertyName] = this.checked; - updateProperties(properties); + updateProperty(propertyName, inverse ? !this.checked : this.checked, isParticleProperty); }; } -function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { - decimals = ((decimals === undefined) ? 4 : decimals); +function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, isParticleProperty) { return function() { - var value = parseFloat(this.value).toFixed(decimals); - updateProperty(propertyName, value); - }; -} - -function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { - return function() { - var properties = {}; - properties[group] = {}; - properties[group][propertyName] = this.value; - updateProperties(properties); - }; -} - -function createImageURLUpdateFunction(propertyName) { - return function () { - var newTextures = JSON.stringify({ "tex.picture": this.value }); - updateProperty(propertyName, newTextures); - }; -} - -function createEmitTextPropertyUpdateFunction(propertyName) { - return function() { - updateProperty(propertyName, this.value); - }; -} - -function createZoneComponentModeChangedFunction(zoneComponent, zoneComponentModeInherit, - zoneComponentModeDisabled, zoneComponentModeEnabled) { - - return function() { - var zoneComponentMode; - - if (zoneComponentModeInherit.checked) { - zoneComponentMode = 'inherit'; - } else if (zoneComponentModeDisabled.checked) { - zoneComponentMode = 'disabled'; - } else if (zoneComponentModeEnabled.checked) { - zoneComponentMode = 'enabled'; + if (multiplier === undefined) { + multiplier = 1; } - - updateProperty(zoneComponent, zoneComponentMode); + let value = parseFloat(this.value) * multiplier; + updateProperty(propertyName, value, isParticleProperty); }; } -function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { - return function() { - var properties = {}; - properties[group] = {}; - properties[group][propertyName] = this.value; - updateProperties(properties); - }; -} - -function createEmitVec2PropertyUpdateFunction(property, elX, elY) { +function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier, isParticleProperty) { return function () { - var properties = {}; - properties[property] = { - x: elX.value, - y: elY.value + if (multiplier === undefined) { + multiplier = 1; + } + let newValue = { + x: elX.value * multiplier, + y: elY.value * multiplier }; - updateProperties(properties); + updateProperty(propertyName, newValue, isParticleProperty); }; } -function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { +function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier, isParticleProperty) { return function() { - var properties = {}; - properties[property] = { - x: elX.value, - y: elY.value, - z: elZ.value - }; - updateProperties(properties); - }; -} - -function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { - return function() { - var properties = {}; - properties[group] = {}; - properties[group][property] = { - x: elX.value, - y: elY.value, - z: elZ ? elZ.value : 0 - }; - updateProperties(properties); - }; -} - -function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { - return function() { - var properties = {}; - properties[property] = { + if (multiplier === undefined) { + multiplier = 1; + } + let newValue = { x: elX.value * multiplier, y: elY.value * multiplier, z: elZ.value * multiplier }; - updateProperties(properties); + updateProperty(propertyName, newValue, isParticleProperty); }; } -function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) { +function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue, isParticleProperty) { return function() { - emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value); + emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value, isParticleProperty); }; } -function emitColorPropertyUpdate(property, red, green, blue, group) { - var properties = {}; - if (group) { - properties[group] = {}; - properties[group][property] = { - red: red, - green: green, - blue: blue - }; - } else { - properties[property] = { - red: red, - green: green, - blue: blue - }; - } - updateProperties(properties); -} - - -function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { - return function() { - var properties = {}; - properties[group] = {}; - properties[group][property] = { - red: elRed.value, - green: elGreen.value, - blue: elBlue.value - }; - updateProperties(properties); +function emitColorPropertyUpdate(propertyName, red, green, blue, isParticleProperty) { + let newValue = { + red: red, + green: green, + blue: blue }; + updateProperty(propertyName, newValue, isParticleProperty); } -function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) { +function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString, isParticleProperty) { if (subPropertyElement.checked) { if (propertyValue.indexOf(subPropertyString)) { propertyValue += subPropertyString + ','; @@ -279,11 +1686,597 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen // We've unchecked, so remove propertyValue = propertyValue.replace(subPropertyString + ",", ""); } - updateProperty(propertyName, propertyValue); + updateProperty(propertyName, propertyValue, isParticleProperty); +} + +function createImageURLUpdateFunction(propertyName, isParticleProperty) { + return function () { + let newTextures = JSON.stringify({ "tex.picture": this.value }); + updateProperty(propertyName, newTextures, isParticleProperty); + }; +} + + +/** + * PROPERTY ELEMENT CREATION FUNCTIONS + */ + +function createStringProperty(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property text"; + + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "text"); + if (propertyData.readOnly) { + elInput.readOnly = true; + } + + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + + elProperty.appendChild(elLabel); + elProperty.appendChild(elInput); + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, false); + } + + return elInput; +} + +function createBoolProperty(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property checkbox"; + + if (propertyData.glyph !== undefined) { + elLabel.innerText = " " + propertyData.label; + let elSpan = document.createElement('span'); + elSpan.innerHTML = propertyData.glyph; + elLabel.insertBefore(elSpan, elLabel.firstChild); + } + + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "checkbox"); + + elProperty.appendChild(elInput); + elProperty.appendChild(elLabel); + + let subPropertyOf = propertyData.subPropertyOf; + if (subPropertyOf !== undefined) { + elInput.addEventListener('change', function() { + updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], elInput, propertyName, property.isParticleProperty); + }); + } else { + elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, property.isParticleProperty)); + } + + return elInput; +} + +function createNumberProperty(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property number"; + + addUnit(propertyData.unit, elLabel); + + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "number"); + if (propertyData.min !== undefined) { + elInput.setAttribute("min", propertyData.min); + } + if (propertyData.max !== undefined) { + elInput.setAttribute("max", propertyData.max); + } + if (propertyData.step !== undefined) { + elInput.setAttribute("step", propertyData.step); + } + + let defaultValue = propertyData.defaultValue; + if (defaultValue !== undefined) { + elInput.value = defaultValue; + } + + elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, propertyData.decimals, property.isParticleProperty)); + + elProperty.appendChild(elLabel); + elProperty.appendChild(elInput); + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, true); + } + + return elInput; +} + +function createSliderProperty(property, elProperty, elLabel) { + let propertyData = property.data; + + elProperty.className = "property range"; + + let elDiv = document.createElement("div"); + elDiv.className = "slider-wrapper"; + + let elSlider = document.createElement("input"); + elSlider.setAttribute("type", "range"); + + let elInput = document.createElement("input"); + elInput.setAttribute("type", "number"); + + if (propertyData.min !== undefined) { + elInput.setAttribute("min", propertyData.min); + elSlider.setAttribute("min", propertyData.min); + } + if (propertyData.max !== undefined) { + elInput.setAttribute("max", propertyData.max); + elSlider.setAttribute("max", propertyData.max); + elSlider.setAttribute("data-max", propertyData.max); + } + if (propertyData.step !== undefined) { + elInput.setAttribute("step", propertyData.step); + elSlider.setAttribute("step", propertyData.step); + } + + elInput.onchange = function (event) { + let inputValue = event.target.value; + elSlider.value = inputValue; + if (propertyData.multiplier !== undefined) { + inputValue *= propertyData.multiplier; + } + updateProperty(property.name, inputValue, property.isParticleProperty); + }; + elSlider.oninput = function (event) { + let sliderValue = event.target.value; + if (propertyData.step === 1) { + if (sliderValue > 0) { + elInput.value = Math.floor(sliderValue); + } else { + elInput.value = Math.ceil(sliderValue); + } + } else { + elInput.value = sliderValue; + } + if (propertyData.multiplier !== undefined) { + sliderValue *= propertyData.multiplier; + } + updateProperty(property.name, sliderValue, property.isParticleProperty); + }; + + elDiv.appendChild(elLabel); + elDiv.appendChild(elSlider); + elDiv.appendChild(elInput); + elProperty.appendChild(elDiv); + + let elResult = []; + elResult[SLIDER_ELEMENTS.SLIDER] = elSlider; + elResult[SLIDER_ELEMENTS.NUMBER_INPUT] = elInput; + return elResult; +} + +function createVec3Property(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property " + propertyData.vec3Type + " fstuple"; + + let elTuple = document.createElement('div'); + elTuple.className = "tuple"; + + addUnit(propertyData.unit, elLabel); + + elProperty.appendChild(elLabel); + elProperty.appendChild(elTuple); + + let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + propertyData.min, propertyData.max, propertyData.step); + let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + propertyData.min, propertyData.max, propertyData.step); + let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_INPUT], + propertyData.min, propertyData.max, propertyData.step); + + let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ, + propertyData.multiplier, property.isParticleProperty); + elInputX.addEventListener('change', inputChangeFunction); + elInputY.addEventListener('change', inputChangeFunction); + elInputZ.addEventListener('change', inputChangeFunction); + + let elResult = []; + elResult[VECTOR_ELEMENTS.X_INPUT] = elInputX; + elResult[VECTOR_ELEMENTS.Y_INPUT] = elInputY; + elResult[VECTOR_ELEMENTS.Z_INPUT] = elInputZ; + return elResult; +} + +function createVec2Property(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property " + propertyData.vec2Type + " fstuple"; + + let elTuple = document.createElement('div'); + elTuple.className = "tuple"; + + addUnit(propertyData.unit, elLabel); + + elProperty.appendChild(elLabel); + elProperty.appendChild(elTuple); + + let elInputX = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_INPUT], + propertyData.min, propertyData.max, propertyData.step); + let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_INPUT], + propertyData.min, propertyData.max, propertyData.step); + + let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, elInputY, + propertyData.multiplier, property.isParticleProperty); + elInputX.addEventListener('change', inputChangeFunction); + elInputY.addEventListener('change', inputChangeFunction); + + let elResult = []; + elResult[VECTOR_ELEMENTS.X_INPUT] = elInputX; + elResult[VECTOR_ELEMENTS.Y_INPUT] = elInputY; + return elResult; +} + +function createColorProperty(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + + elProperty.className = "property rgb fstuple"; + + let elColorPicker = document.createElement('div'); + elColorPicker.className = "color-picker"; + elColorPicker.setAttribute("id", elementID); + + let elTuple = document.createElement('div'); + elTuple.className = "tuple"; + + elProperty.appendChild(elColorPicker); + elProperty.appendChild(elLabel); + elProperty.appendChild(elTuple); + + let elInputR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elInputG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); + let elInputB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); + + let inputChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elInputR, elInputG, elInputB, + property.isParticleProperty); + elInputR.addEventListener('change', inputChangeFunction); + elInputG.addEventListener('change', inputChangeFunction); + elInputB.addEventListener('change', inputChangeFunction); + + let colorPickerID = "#" + elementID; + colorPickers[colorPickerID] = $(colorPickerID).colpick({ + colorScheme: 'dark', + layout: 'hex', + color: '000000', + submit: false, // We don't want to have a submission button + onShow: function(colpick) { + $(colorPickerID).attr('active', 'true'); + // The original color preview within the picker needs to be updated on show because + // prior to the picker being shown we don't have access to the selections' starting color. + colorPickers[colorPickerID].colpickSetColor({ + "r": elInputR.value, + "g": elInputG.value, + "b": elInputB.value + }); + }, + onHide: function(colpick) { + $(colorPickerID).attr('active', 'false'); + }, + onChange: function(hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + emitColorPropertyUpdate(propertyName, rgb.r, rgb.g, rgb.b); + } + }); + + let elResult = []; + elResult[COLOR_ELEMENTS.COLOR_PICKER] = elColorPicker; + elResult[COLOR_ELEMENTS.RED_INPUT] = elInputR; + elResult[COLOR_ELEMENTS.GREEN_INPUT] = elInputG; + elResult[COLOR_ELEMENTS.BLUE_INPUT] = elInputB; + return elResult; +} + +function createDropdownProperty(property, propertyID, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property dropdown"; + + let elInput = document.createElement('select'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("propertyID", propertyID); + + for (let optionKey in propertyData.options) { + let option = document.createElement('option'); + option.value = optionKey; + option.text = propertyData.options[optionKey]; + elInput.add(option); + } + + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + + elProperty.appendChild(elLabel); + elProperty.appendChild(elInput); + + return elInput; +} + +function createTextareaProperty(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property textarea"; + + elProperty.appendChild(elLabel); + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, true); + } + + let elInput = document.createElement('textarea'); + elInput.setAttribute("id", elementID); + if (propertyData.readOnly) { + elInput.readOnly = true; + } + + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + + elProperty.appendChild(elInput); + + return elInput; +} + +function createIconProperty(property, elProperty, elLabel) { + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property value"; + + elLabel.setAttribute("id", elementID); + elLabel.innerHTML = " " + propertyData.label; + + let elSpan = document.createElement('span'); + elSpan.setAttribute("id", elementID + "-icon"); + + elProperty.appendChild(elSpan); + elProperty.appendChild(elLabel); + + let elResult = []; + elResult[ICON_ELEMENTS.ICON] = elSpan; + elResult[ICON_ELEMENTS.LABEL] = elLabel; + return elResult; +} + +function createTextureProperty(property, elProperty, elLabel) { + let elementID = property.elementID; + + elProperty.className = "property texture"; + + let elDiv = document.createElement("div"); + let elImage = document.createElement("img"); + elDiv.className = "texture-image no-texture"; + elDiv.appendChild(elImage); + + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "text"); + + let imageLoad = _.debounce(function (url) { + if (url.slice(0, 5).toLowerCase() === "atp:/") { + elImage.src = ""; + elImage.style.display = "none"; + elDiv.classList.remove("with-texture"); + elDiv.classList.remove("no-texture"); + elDiv.classList.add("no-preview"); + } else if (url.length > 0) { + elDiv.classList.remove("no-texture"); + elDiv.classList.remove("no-preview"); + elDiv.classList.add("with-texture"); + elImage.src = url; + elImage.style.display = "block"; + } else { + elImage.src = ""; + elImage.style.display = "none"; + elDiv.classList.remove("with-texture"); + elDiv.classList.remove("no-preview"); + elDiv.classList.add("no-texture"); + } + }, DEBOUNCE_TIMEOUT * 2); + elInput.imageLoad = imageLoad; + elInput.oninput = function (event) { + // Add throttle + let url = event.target.value; + imageLoad(url); + updateProperty(property.name, url, property.isParticleProperty) + }; + elInput.onchange = elInput.oninput; + + elProperty.appendChild(elLabel); + elProperty.appendChild(elDiv); + elProperty.appendChild(elInput); + + let elResult = []; + elResult[TEXTURE_ELEMENTS.IMAGE] = elImage; + elResult[TEXTURE_ELEMENTS.TEXT_INPUT] = elInput; + return elResult; +} + +function createButtonsProperty(property, elProperty, elLabel) { + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property text"; + + let hasLabel = propertyData.label !== undefined; + if (hasLabel) { + elProperty.appendChild(elLabel); + } + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, hasLabel); + } + + return elProperty; +} + +function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step) { + let elementID = propertyElementID + "-" + subLabel.toLowerCase(); + + let elDiv = document.createElement('div'); + let elLabel = document.createElement('label'); + elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1) + ":"; + elLabel.setAttribute("for", elementID); + + let elInput = document.createElement('input'); + elInput.className = subLabel; + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "number"); + elInput.setAttribute("min", min); + elInput.setAttribute("max", max); + elInput.setAttribute("step", step); + + elDiv.appendChild(elInput); + elDiv.appendChild(elLabel); + elTuple.appendChild(elDiv); + + return elInput; +} + +function addUnit(unit, elLabel) { + if (unit !== undefined) { + let elSpan = document.createElement('span'); + elSpan.className = "unit"; + elSpan.innerHTML = unit; + elLabel.appendChild(elSpan); + } +} + +function addButtons(elProperty, propertyID, buttons, newRow) { + let elDiv = document.createElement('div'); + elDiv.className = "row"; + + buttons.forEach(function(button) { + let elButton = document.createElement('input'); + elButton.className = button.className; + elButton.setAttribute("type", "button"); + elButton.setAttribute("id", propertyID + "-button-" + button.id); + elButton.setAttribute("value", button.label); + elButton.addEventListener("click", button.onClick); + if (newRow) { + elDiv.appendChild(elButton); + } else { + elProperty.appendChild(elButton); + } + }); + + if (newRow) { + elProperty.appendChild(document.createElement('br')); + elProperty.appendChild(elDiv); + } +} + + +/** + * BUTTON CALLBACKS + */ + +function rescaleDimensions() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "rescaleDimensions", + percentage: parseFloat(document.getElementById("property-scale").value) + })); +} + +function moveSelectionToGrid() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveSelectionToGrid" + })); +} + +function moveAllToGrid() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveAllToGrid" + })); +} + +function resetToNaturalDimensions() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "resetToNaturalDimensions" + })); +} + +function reloadScripts() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadClientScripts" + })); +} + +function reloadServerScripts() { + // invalidate the current status (so that same-same updates can still be observed visually) + document.getElementById("property-serverScripts-status").innerText = PENDING_SCRIPT_STATUS; + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadServerScripts" + })); +} + +function copySkyboxURLToAmbientURL() { + let skyboxURL = getPropertyInputElement("skybox.url").value; + getPropertyInputElement("ambientLight.ambientURL").value = skyboxURL; + updateProperty("ambientLight.ambientURL", skyboxURL, false); +} + + +/** + * USER DATA FUNCTIONS + */ + +function clearUserData() { + let elUserData = getPropertyInputElement("userData"); + deleteJSONEditor(); + elUserData.value = ""; + showUserDataTextArea(); + showNewJSONEditorButton(); + hideSaveUserDataButton(); + updateProperty('userData', elUserData.value, false); +} + +function newJSONEditor() { + deleteJSONEditor(); + createJSONEditor(); + let data = {}; + setEditorJSON(data); + hideUserDataTextArea(); + hideNewJSONEditorButton(); + showSaveUserDataButton(); +} + +function saveUserData() { + saveJSONUserData(true); } function setUserDataFromEditor(noUpdate) { - var json = null; + let json = null; try { json = editor.get(); } catch (e) { @@ -292,7 +2285,7 @@ function setUserDataFromEditor(noUpdate) { if (json === null) { return; } else { - var text = editor.getText(); + let text = editor.getText(); if (noUpdate === true) { EventBridge.emitWebEvent( JSON.stringify({ @@ -305,17 +2298,17 @@ function setUserDataFromEditor(noUpdate) { ); return; } else { - updateProperty('userData', text); + updateProperty('userData', text, false); } } } function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, removeKeys) { - var properties = {}; - var parsedData = {}; - var keysToBeRemoved = removeKeys ? removeKeys : []; + let propertyUpdate = {}; + let parsedData = {}; + let keysToBeRemoved = removeKeys ? removeKeys : []; try { - if ($('#userdata-editor').css('height') !== "0px") { + if ($('#property-userData-editor').css('height') !== "0px") { // if there is an expanded, we want to use its json. parsedData = getEditorJSON(); } else { @@ -328,14 +2321,14 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r if (!(groupName in parsedData)) { parsedData[groupName] = {}; } - var keys = Object.keys(updateKeyPair); + let keys = Object.keys(updateKeyPair); keys.forEach(function (key) { if (updateKeyPair[key] !== null && updateKeyPair[key] !== "null") { if (updateKeyPair[key] instanceof Element) { if (updateKeyPair[key].type === "checkbox") { parsedData[groupName][key] = updateKeyPair[key].checked; } else { - var val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value); + let val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value); parsedData[groupName][key] = val; } } else { @@ -355,159 +2348,21 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r delete parsedData[groupName]; } if (Object.keys(parsedData).length > 0) { - properties.userData = JSON.stringify(parsedData); + propertyUpdate.userData = JSON.stringify(parsedData); } else { - properties.userData = ''; + propertyUpdate.userData = ''; } - userDataElement.value = properties.userData; + userDataElement.value = propertyUpdate.userData; - updateProperties(properties); -} -function userDataChanger(groupName, keyName, values, userDataElement, defaultValue, removeKeys) { - var val = {}, def = {}; - val[keyName] = values; - def[keyName] = defaultValue; - multiDataUpdater(groupName, val, userDataElement, def, removeKeys); -} - -function setMaterialDataFromEditor(noUpdate) { - var json = null; - try { - json = materialEditor.get(); - } catch (e) { - alert('Invalid JSON code - look for red X in your code ', +e); - } - if (json === null) { - return; - } else { - var text = materialEditor.getText(); - if (noUpdate === true) { - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "saveMaterialData", - properties: { - materialData: text - } - }) - ); - return; - } else { - updateProperty('materialData', text); - } - } -} - -function setTextareaScrolling(element) { - var isScrolling = element.scrollHeight > element.offsetHeight; - element.setAttribute("scrolling", isScrolling ? "true" : "false"); -} - - -var materialEditor = null; - -function createJSONMaterialEditor() { - var container = document.getElementById("materialdata-editor"); - var options = { - search: false, - mode: 'tree', - modes: ['code', 'tree'], - name: 'materialData', - onModeChange: function() { - $('.jsoneditor-poweredBy').remove(); - }, - onError: function(e) { - alert('JSON editor:' + e); - }, - onChange: function() { - var currentJSONString = materialEditor.getText(); - - if (currentJSONString === '{"":""}') { - return; - } - $('#materialdata-save').attr('disabled', false); - - - } - }; - materialEditor = new JSONEditor(container, options); -} - -function hideNewJSONMaterialEditorButton() { - $('#materialdata-new-editor').hide(); -} - -function showSaveMaterialDataButton() { - $('#materialdata-save').show(); -} - -function hideSaveMaterialDataButton() { - $('#materialdata-save').hide(); -} - -function showNewJSONMaterialEditorButton() { - $('#materialdata-new-editor').show(); -} - -function showMaterialDataTextArea() { - $('#property-material-data').show(); -} - -function hideMaterialDataTextArea() { - $('#property-material-data').hide(); -} - -function showStaticMaterialData() { - if (materialEditor !== null) { - $('#static-materialdata').show(); - $('#static-materialdata').css('height', $('#materialdata-editor').height()); - $('#static-materialdata').text(materialEditor.getText()); - } -} - -function removeStaticMaterialData() { - $('#static-materialdata').hide(); -} - -function setMaterialEditorJSON(json) { - materialEditor.set(json); - if (materialEditor.hasOwnProperty('expandAll')) { - materialEditor.expandAll(); - } -} - -function getMaterialEditorJSON() { - return materialEditor.get(); -} - -function deleteJSONMaterialEditor() { - if (materialEditor !== null) { - materialEditor.destroy(); - materialEditor = null; - } -} - -var savedMaterialJSONTimer = null; - -function saveJSONMaterialData(noUpdate) { - setMaterialDataFromEditor(noUpdate); - $('#materialdata-saved').show(); - $('#materialdata-save').attr('disabled', true); - if (savedMaterialJSONTimer !== null) { - clearTimeout(savedMaterialJSONTimer); - } - savedMaterialJSONTimer = setTimeout(function() { - $('#materialdata-saved').hide(); - - }, EDITOR_TIMEOUT_DURATION); + updateProperties(propertyUpdate); } var editor = null; function createJSONEditor() { - var container = document.getElementById("userdata-editor"); - var options = { + let container = document.getElementById("property-userData-editor"); + let options = { search: false, mode: 'tree', modes: ['code', 'tree'], @@ -519,12 +2374,12 @@ function createJSONEditor() { alert('JSON editor:' + e); }, onChange: function() { - var currentJSONString = editor.getText(); + let currentJSONString = editor.getText(); if (currentJSONString === '{"":""}') { return; } - $('#userdata-save').attr('disabled', false); + $('#property-userData-button-save').attr('disabled', false); } @@ -532,40 +2387,48 @@ function createJSONEditor() { editor = new JSONEditor(container, options); } -function hideNewJSONEditorButton() { - $('#userdata-new-editor').hide(); -} - function showSaveUserDataButton() { - $('#userdata-save').show(); + $('#property-userData-button-save').show(); } function hideSaveUserDataButton() { - $('#userdata-save').hide(); + $('#property-userData-button-save').hide(); +} + +function disableSaveUserDataButton() { + $('#property-userData-button-save').attr('disabled', true); } function showNewJSONEditorButton() { - $('#userdata-new-editor').show(); + $('#property-userData-button-edit').show(); +} + +function hideNewJSONEditorButton() { + $('#property-userData-button-edit').hide(); } function showUserDataTextArea() { - $('#property-user-data').show(); + $('#property-userData').show(); } function hideUserDataTextArea() { - $('#property-user-data').hide(); + $('#property-userData').hide(); +} + +function hideUserDataSaved() { + $('#property-userData-saved').hide(); } function showStaticUserData() { if (editor !== null) { - $('#static-userdata').show(); - $('#static-userdata').css('height', $('#userdata-editor').height()); - $('#static-userdata').text(editor.getText()); + $('#property-userData-static').show(); + $('#property-userData-static').css('height', $('#property-userData-editor').height()); + $('#property-userData-static').text(editor.getText()); } } function removeStaticUserData() { - $('#static-userdata').hide(); + $('#property-userData-static').hide(); } function setEditorJSON(json) { @@ -590,308 +2453,457 @@ var savedJSONTimer = null; function saveJSONUserData(noUpdate) { setUserDataFromEditor(noUpdate); - $('#userdata-saved').show(); - $('#userdata-save').attr('disabled', true); + $('#property-userData-saved').show(); + $('#property-userData-button-save').attr('disabled', true); if (savedJSONTimer !== null) { clearTimeout(savedJSONTimer); } savedJSONTimer = setTimeout(function() { - $('#userdata-saved').hide(); + hideUserDataSaved(); + }, EDITOR_TIMEOUT_DURATION); +} + +/** + * MATERIAL DATA FUNCTIONS + */ + +function clearMaterialData() { + let elMaterialData = getPropertyInputElement("materialData"); + deleteJSONMaterialEditor(); + elMaterialData.value = ""; + showMaterialDataTextArea(); + showNewJSONMaterialEditorButton(); + hideSaveMaterialDataButton(); + updateProperty('materialData', elMaterialData.value, false); +} + +function newJSONMaterialEditor() { + deleteJSONMaterialEditor(); + createJSONMaterialEditor(); + let data = {}; + setMaterialEditorJSON(data); + hideMaterialDataTextArea(); + hideNewJSONMaterialEditorButton(); + showSaveMaterialDataButton(); +} + +function saveMaterialData() { + saveJSONMaterialData(true); +} + +function setMaterialDataFromEditor(noUpdate) { + let json = null; + try { + json = materialEditor.get(); + } catch (e) { + alert('Invalid JSON code - look for red X in your code ', +e); + } + if (json === null) { + return; + } else { + let text = materialEditor.getText(); + if (noUpdate === true) { + EventBridge.emitWebEvent( + JSON.stringify({ + id: lastEntityID, + type: "saveMaterialData", + properties: { + materialData: text + } + }) + ); + return; + } else { + updateProperty('materialData', text, false); + } + } +} + +var materialEditor = null; + +function createJSONMaterialEditor() { + let container = document.getElementById("property-materialData-editor"); + let options = { + search: false, + mode: 'tree', + modes: ['code', 'tree'], + name: 'materialData', + onModeChange: function() { + $('.jsoneditor-poweredBy').remove(); + }, + onError: function(e) { + alert('JSON editor:' + e); + }, + onChange: function() { + let currentJSONString = materialEditor.getText(); + + if (currentJSONString === '{"":""}') { + return; + } + $('#property-materialData-button-save').attr('disabled', false); + + + } + }; + materialEditor = new JSONEditor(container, options); +} + +function showSaveMaterialDataButton() { + $('#property-materialData-button-save').show(); +} + +function hideSaveMaterialDataButton() { + $('#property-materialData-button-save').hide(); +} + +function disableSaveMaterialDataButton() { + $('#property-materialData-button-save').attr('disabled', true); +} + +function showNewJSONMaterialEditorButton() { + $('#property-materialData-button-edit').show(); +} + +function hideNewJSONMaterialEditorButton() { + $('#property-materialData-button-edit').hide(); +} + +function showMaterialDataTextArea() { + $('#property-materialData').show(); +} + +function hideMaterialDataTextArea() { + $('#property-materialData').hide(); +} + +function hideMaterialDataSaved() { + $('#property-materialData-saved').hide(); +} + +function showStaticMaterialData() { + if (materialEditor !== null) { + $('#property-materialData-static').show(); + $('#property-materialData-static').css('height', $('#property-materialData-editor').height()); + $('#property-materialData-static').text(materialEditor.getText()); + } +} + +function removeStaticMaterialData() { + $('#property-materialData-static').hide(); +} + +function setMaterialEditorJSON(json) { + materialEditor.set(json); + if (materialEditor.hasOwnProperty('expandAll')) { + materialEditor.expandAll(); + } +} + +function getMaterialEditorJSON() { + return materialEditor.get(); +} + +function deleteJSONMaterialEditor() { + if (materialEditor !== null) { + materialEditor.destroy(); + materialEditor = null; + } +} + +var savedMaterialJSONTimer = null; + +function saveJSONMaterialData(noUpdate) { + setMaterialDataFromEditor(noUpdate); + $('#property-materialData-saved').show(); + $('#property-materialData-button-save').attr('disabled', true); + if (savedMaterialJSONTimer !== null) { + clearTimeout(savedMaterialJSONTimer); + } + savedMaterialJSONTimer = setTimeout(function() { + hideMaterialDataSaved(); }, EDITOR_TIMEOUT_DURATION); } function bindAllNonJSONEditorElements() { - var inputs = $('input'); - var i; - for (i = 0; i < inputs.length; i++) { - var input = inputs[i]; - var field = $(input); + let inputs = $('input'); + let i; + for (i = 0; i < inputs.length; ++i) { + let input = inputs[i]; + let field = $(input); // TODO FIXME: (JSHint) Functions declared within loops referencing // an outer scoped variable may lead to confusing semantics. field.on('focus', function(e) { - if (e.target.id === "userdata-new-editor" || e.target.id === "userdata-clear" || e.target.id === "materialdata-new-editor" || e.target.id === "materialdata-clear") { + if (e.target.id === "property-userData-button-edit" || e.target.id === "property-userData-button-clear" || + e.target.id === "property-materialData-button-edit" || e.target.id === "property-materialData-button-clear") { return; } else { - if ($('#userdata-editor').css('height') !== "0px") { - saveJSONUserData(true); + if ($('#property-userData-editor').css('height') !== "0px") { + saveUserData(); } - if ($('#materialdata-editor').css('height') !== "0px") { - saveJSONMaterialData(true); + if ($('#property-materialData-editor').css('height') !== "0px") { + saveMaterialData(); } } }); } } -function unbindAllInputs() { - var inputs = $('input'); - var i; - for (i = 0; i < inputs.length; i++) { - var input = inputs[i]; - var field = $(input); - field.unbind(); + +/** + * DROPDOWN FUNCTIONS + */ + +function setDropdownText(dropdown) { + let lis = dropdown.parentNode.getElementsByTagName("li"); + let text = ""; + for (let i = 0; i < lis.length; ++i) { + if (String(lis[i].getAttribute("value")) === String(dropdown.value)) { + text = lis[i].textContent; + } } + dropdown.firstChild.textContent = text; +} + +function toggleDropdown(event) { + let element = event.target; + if (element.nodeName !== "DT") { + element = element.parentNode; + } + element = element.parentNode; + let isDropped = element.getAttribute("dropped"); + element.setAttribute("dropped", isDropped !== "true" ? "true" : "false"); +} + +function setDropdownValue(event) { + let dt = event.target.parentNode.parentNode.previousSibling; + dt.value = event.target.getAttribute("value"); + dt.firstChild.textContent = event.target.textContent; + + dt.parentNode.setAttribute("dropped", "false"); + + let evt = document.createEvent("HTMLEvents"); + evt.initEvent("change", true, true); + dt.dispatchEvent(evt); +} + + +/** + * TEXTAREA / PARENT MATERIAL NAME FUNCTIONS + */ + +function setTextareaScrolling(element) { + let isScrolling = element.scrollHeight > element.offsetHeight; + element.setAttribute("scrolling", isScrolling ? "true" : "false"); } function showParentMaterialNameBox(number, elNumber, elString) { if (number) { - $('#property-parent-material-id-number-container').show(); - $('#property-parent-material-id-string-container').hide(); + $('#property-submeshToReplace').parent().show(); + $('#property-materialNameToReplace').parent().hide(); elString.value = ""; } else { - $('#property-parent-material-id-string-container').show(); - $('#property-parent-material-id-number-container').hide(); + $('#property-materialNameToReplace').parent().show(); + $('#property-submeshToReplace').parent().hide(); elNumber.value = 0; } } +function updateVisibleSpaceModeProperties() { + for (let propertyID in properties) { + if (properties.hasOwnProperty(propertyID)) { + let property = properties[propertyID]; + let propertySpaceMode = property.spaceMode; + if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { + showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); + } + } + } +} + function loaded() { openEventBridge(function() { - - var elPropertiesList = document.getElementById("properties-list"); - var elID = document.getElementById("property-id"); - var elType = document.getElementById("property-type"); - var elTypeIcon = document.getElementById("type-icon"); - var elName = document.getElementById("property-name"); - var elLocked = document.getElementById("property-locked"); - var elVisible = document.getElementById("property-visible"); - var elPositionX = document.getElementById("property-pos-x"); - var elPositionY = document.getElementById("property-pos-y"); - var elPositionZ = document.getElementById("property-pos-z"); - var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid"); - var elMoveAllToGrid = document.getElementById("move-all-to-grid"); - - var elDimensionsX = document.getElementById("property-dim-x"); - var elDimensionsY = document.getElementById("property-dim-y"); - var elDimensionsZ = document.getElementById("property-dim-z"); - var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions"); - var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct"); - var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button"); - - var elParentID = document.getElementById("property-parent-id"); - var elParentJointIndex = document.getElementById("property-parent-joint-index"); - - var elRegistrationX = document.getElementById("property-reg-x"); - var elRegistrationY = document.getElementById("property-reg-y"); - var elRegistrationZ = document.getElementById("property-reg-z"); - - var elRotationX = document.getElementById("property-rot-x"); - var elRotationY = document.getElementById("property-rot-y"); - var elRotationZ = document.getElementById("property-rot-z"); - - var elLinearVelocityX = document.getElementById("property-lvel-x"); - var elLinearVelocityY = document.getElementById("property-lvel-y"); - var elLinearVelocityZ = document.getElementById("property-lvel-z"); - var elLinearDamping = document.getElementById("property-ldamping"); - - var elAngularVelocityX = document.getElementById("property-avel-x"); - var elAngularVelocityY = document.getElementById("property-avel-y"); - var elAngularVelocityZ = document.getElementById("property-avel-z"); - var elAngularDamping = document.getElementById("property-adamping"); - - var elRestitution = document.getElementById("property-restitution"); - var elFriction = document.getElementById("property-friction"); - - var elGravityX = document.getElementById("property-grav-x"); - var elGravityY = document.getElementById("property-grav-y"); - var elGravityZ = document.getElementById("property-grav-z"); - - var elAccelerationX = document.getElementById("property-lacc-x"); - var elAccelerationY = document.getElementById("property-lacc-y"); - var elAccelerationZ = document.getElementById("property-lacc-z"); - - var elDensity = document.getElementById("property-density"); - var elCollisionless = document.getElementById("property-collisionless"); - var elDynamic = document.getElementById("property-dynamic"); - var elCollideStatic = document.getElementById("property-collide-static"); - var elCollideDynamic = document.getElementById("property-collide-dynamic"); - var elCollideKinematic = document.getElementById("property-collide-kinematic"); - var elCollideMyAvatar = document.getElementById("property-collide-myAvatar"); - var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar"); - var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); - - var elGrabbable = document.getElementById("property-grabbable"); - - var elCloneable = document.getElementById("property-cloneable"); - var elCloneableDynamic = document.getElementById("property-cloneable-dynamic"); - var elCloneableAvatarEntity = document.getElementById("property-cloneable-avatarEntity"); - var elCloneableGroup = document.getElementById("group-cloneable-group"); - var elCloneableLifetime = document.getElementById("property-cloneable-lifetime"); - var elCloneableLimit = document.getElementById("property-cloneable-limit"); - - var elTriggerable = document.getElementById("property-triggerable"); - var elIgnoreIK = document.getElementById("property-ignore-ik"); - - var elLifetime = document.getElementById("property-lifetime"); - var elScriptURL = document.getElementById("property-script-url"); - var elScriptTimestamp = document.getElementById("property-script-timestamp"); - var elReloadScriptsButton = document.getElementById("reload-script-button"); - var elServerScripts = document.getElementById("property-server-scripts"); - var elReloadServerScriptsButton = document.getElementById("reload-server-scripts-button"); - var elServerScriptStatus = document.getElementById("server-script-status"); - var elServerScriptError = document.getElementById("server-script-error"); - var elUserData = document.getElementById("property-user-data"); - var elClearUserData = document.getElementById("userdata-clear"); - var elSaveUserData = document.getElementById("userdata-save"); - var elNewJSONEditor = document.getElementById('userdata-new-editor'); - var elColorControlVariant2 = document.getElementById("property-color-control2"); - var elColorRed = document.getElementById("property-color-red"); - var elColorGreen = document.getElementById("property-color-green"); - var elColorBlue = document.getElementById("property-color-blue"); - - var elShape = document.getElementById("property-shape"); - - var elCanCastShadow = document.getElementById("property-can-cast-shadow"); - - var elLightSpotLight = document.getElementById("property-light-spot-light"); - var elLightColor = document.getElementById("property-light-color"); - var elLightColorRed = document.getElementById("property-light-color-red"); - var elLightColorGreen = document.getElementById("property-light-color-green"); - var elLightColorBlue = document.getElementById("property-light-color-blue"); - - var elLightIntensity = document.getElementById("property-light-intensity"); - var elLightFalloffRadius = document.getElementById("property-light-falloff-radius"); - var elLightExponent = document.getElementById("property-light-exponent"); - var elLightCutoff = document.getElementById("property-light-cutoff"); - - var elModelURL = document.getElementById("property-model-url"); - var elShapeType = document.getElementById("property-shape-type"); - var elCompoundShapeURL = document.getElementById("property-compound-shape-url"); - var elModelAnimationURL = document.getElementById("property-model-animation-url"); - var elModelAnimationPlaying = document.getElementById("property-model-animation-playing"); - var elModelAnimationFPS = document.getElementById("property-model-animation-fps"); - var elModelAnimationFrame = document.getElementById("property-model-animation-frame"); - var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame"); - var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame"); - var elModelAnimationLoop = document.getElementById("property-model-animation-loop"); - var elModelAnimationHold = document.getElementById("property-model-animation-hold"); - var elModelAnimationAllowTranslation = document.getElementById("property-model-animation-allow-translation"); - var elModelTextures = document.getElementById("property-model-textures"); - var elModelOriginalTextures = document.getElementById("property-model-original-textures"); - - var elMaterialURL = document.getElementById("property-material-url"); - //var elMaterialMappingMode = document.getElementById("property-material-mapping-mode"); - var elPriority = document.getElementById("property-priority"); - var elParentMaterialNameString = document.getElementById("property-parent-material-id-string"); - var elParentMaterialNameNumber = document.getElementById("property-parent-material-id-number"); - var elParentMaterialNameCheckbox = document.getElementById("property-parent-material-id-checkbox"); - var elMaterialMappingPosX = document.getElementById("property-material-mapping-pos-x"); - var elMaterialMappingPosY = document.getElementById("property-material-mapping-pos-y"); - var elMaterialMappingScaleX = document.getElementById("property-material-mapping-scale-x"); - var elMaterialMappingScaleY = document.getElementById("property-material-mapping-scale-y"); - var elMaterialMappingRot = document.getElementById("property-material-mapping-rot"); - var elMaterialData = document.getElementById("property-material-data"); - var elClearMaterialData = document.getElementById("materialdata-clear"); - var elSaveMaterialData = document.getElementById("materialdata-save"); - var elNewJSONMaterialEditor = document.getElementById('materialdata-new-editor'); - - var elImageURL = document.getElementById("property-image-url"); - - var elWebSourceURL = document.getElementById("property-web-source-url"); - var elWebDPI = document.getElementById("property-web-dpi"); - - var elDescription = document.getElementById("property-description"); - - var elHyperlinkHref = document.getElementById("property-hyperlink-href"); - - var elTextText = document.getElementById("property-text-text"); - var elTextLineHeight = document.getElementById("property-text-line-height"); - var elTextTextColor = document.getElementById("property-text-text-color"); - var elTextFaceCamera = document.getElementById("property-text-face-camera"); - var elTextTextColorRed = document.getElementById("property-text-text-color-red"); - var elTextTextColorGreen = document.getElementById("property-text-text-color-green"); - var elTextTextColorBlue = document.getElementById("property-text-text-color-blue"); - var elTextBackgroundColor = document.getElementById("property-text-background-color"); - var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red"); - var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green"); - var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue"); - - // Key light - var elZoneKeyLightModeInherit = document.getElementById("property-zone-key-light-mode-inherit"); - var elZoneKeyLightModeDisabled = document.getElementById("property-zone-key-light-mode-disabled"); - var elZoneKeyLightModeEnabled = document.getElementById("property-zone-key-light-mode-enabled"); - - var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color"); - var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red"); - var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green"); - var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue"); - var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity"); - var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); - var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); - - var elZoneKeyLightCastShadows = document.getElementById("property-zone-key-light-cast-shadows"); - - // Skybox - var elZoneSkyboxModeInherit = document.getElementById("property-zone-skybox-mode-inherit"); - var elZoneSkyboxModeDisabled = document.getElementById("property-zone-skybox-mode-disabled"); - var elZoneSkyboxModeEnabled = document.getElementById("property-zone-skybox-mode-enabled"); - - // Ambient light - var elCopySkyboxURLToAmbientURL = document.getElementById("copy-skybox-url-to-ambient-url"); - - var elZoneAmbientLightModeInherit = document.getElementById("property-zone-ambient-light-mode-inherit"); - var elZoneAmbientLightModeDisabled = document.getElementById("property-zone-ambient-light-mode-disabled"); - var elZoneAmbientLightModeEnabled = document.getElementById("property-zone-ambient-light-mode-enabled"); - - var elZoneAmbientLightIntensity = document.getElementById("property-zone-key-ambient-intensity"); - var elZoneAmbientLightURL = document.getElementById("property-zone-key-ambient-url"); - - // Haze - var elZoneHazeModeInherit = document.getElementById("property-zone-haze-mode-inherit"); - var elZoneHazeModeDisabled = document.getElementById("property-zone-haze-mode-disabled"); - var elZoneHazeModeEnabled = document.getElementById("property-zone-haze-mode-enabled"); - - var elZoneHazeRange = document.getElementById("property-zone-haze-range"); - var elZoneHazeColor = document.getElementById("property-zone-haze-color"); - var elZoneHazeColorRed = document.getElementById("property-zone-haze-color-red"); - var elZoneHazeColorGreen = document.getElementById("property-zone-haze-color-green"); - var elZoneHazeColorBlue = document.getElementById("property-zone-haze-color-blue"); - var elZoneHazeGlareColor = document.getElementById("property-zone-haze-glare-color"); - var elZoneHazeGlareColorRed = document.getElementById("property-zone-haze-glare-color-red"); - var elZoneHazeGlareColorGreen = document.getElementById("property-zone-haze-glare-color-green"); - var elZoneHazeGlareColorBlue = document.getElementById("property-zone-haze-glare-color-blue"); - var elZoneHazeEnableGlare = document.getElementById("property-zone-haze-enable-light-blend"); - var elZoneHazeGlareAngle = document.getElementById("property-zone-haze-blend-angle"); + let elPropertiesList = document.getElementById("properties-list"); - var elZoneHazeAltitudeEffect = document.getElementById("property-zone-haze-altitude-effect"); - var elZoneHazeBaseRef = document.getElementById("property-zone-haze-base"); - var elZoneHazeCeiling = document.getElementById("property-zone-haze-ceiling"); + GROUPS.forEach(function(group) { + let elGroup; + if (group.addToGroup !== undefined) { + let fieldset = document.getElementById("properties-" + group.addToGroup); + elGroup = document.createElement('div'); + fieldset.appendChild(elGroup); + } else { + elGroup = document.createElement('fieldset'); + elGroup.className = "major"; + elGroup.setAttribute("id", "properties-" + group.id); + elPropertiesList.appendChild(elGroup); + } - var elZoneHazeBackgroundBlend = document.getElementById("property-zone-haze-background-blend"); + if (group.label !== undefined) { + let elLegend = document.createElement('legend'); + elLegend.className = "section-header"; + elLegend.innerText = group.label; + let elSpan = document.createElement('span'); + elSpan.className = ".collapse-icon"; + elSpan.innerText = "M"; + elLegend.appendChild(elSpan); + elGroup.appendChild(elLegend); + } + + group.properties.forEach(function(propertyData) { + let propertyType = propertyData.type; + let propertyID = propertyData.propertyID; + let propertyName = propertyData.propertyName !== undefined ? propertyData.propertyName : propertyID; + let propertySpaceMode = propertyData.spaceMode !== undefined ? propertyData.spaceMode : PROPERTY_SPACE_MODE.ALL; + let propertyElementID = "property-" + propertyID; + propertyElementID = propertyElementID.replace('.', '-'); + + let elProperty; + if (propertyType === "sub-header") { + elProperty = document.createElement('legend'); + elProperty.className = "sub-section-header"; + elProperty.innerText = propertyData.label; + } else { + elProperty = document.createElement('div'); + elProperty.setAttribute("id", "div-" + propertyElementID); + } + + if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) { + let columnName = group.id + "column" + propertyData.column; + let elColumn = document.getElementById(columnName); + if (!elColumn) { + let columnDivName = group.id + "columnDiv"; + let elColumnDiv = document.getElementById(columnDivName); + if (!elColumnDiv) { + elColumnDiv = document.createElement('div'); + elColumnDiv.className = "two-column"; + elColumnDiv.setAttribute("id", group.id + "columnDiv"); + elGroup.appendChild(elColumnDiv); + } + elColumn = document.createElement('fieldset'); + elColumn.className = "column"; + elColumn.setAttribute("id", columnName); + elColumnDiv.appendChild(elColumn); + } + elColumn.appendChild(elProperty); + } else { + elGroup.appendChild(elProperty); + } + + let elLabel = document.createElement('label'); + elLabel.innerText = propertyData.label; + elLabel.setAttribute("for", propertyElementID); - // Bloom - var elZoneBloomModeInherit = document.getElementById("property-zone-bloom-mode-inherit"); - var elZoneBloomModeDisabled = document.getElementById("property-zone-bloom-mode-disabled"); - var elZoneBloomModeEnabled = document.getElementById("property-zone-bloom-mode-enabled"); - - var elZoneBloomIntensity = document.getElementById("property-zone-bloom-intensity"); - var elZoneBloomThreshold = document.getElementById("property-zone-bloom-threshold"); - var elZoneBloomSize = document.getElementById("property-zone-bloom-size"); - - var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color"); - var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red"); - var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); - var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue"); - var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url"); - - var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed"); - var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed"); - var elZoneFilterURL = document.getElementById("property-zone-filter-url"); - - var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); - var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); - var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); - var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style"); - var elXTextureURL = document.getElementById("property-x-texture-url"); - var elYTextureURL = document.getElementById("property-y-texture-url"); - var elZTextureURL = document.getElementById("property-z-texture-url"); + createAppTooltip.registerTooltipElement(elLabel, propertyID); + + let property = { + data: propertyData, + elementID: propertyElementID, + name: propertyName, + isParticleProperty: group.id.includes("particles"), + elProperty, + spaceMode: propertySpaceMode, + }; + properties[propertyID] = property; + + switch (propertyType) { + case 'string': { + properties[propertyID].elInput = createStringProperty(property, elProperty, elLabel); + break; + } + case 'bool': { + properties[propertyID].elInput = createBoolProperty(property, elProperty, elLabel); + break; + } + case 'number': { + properties[propertyID].elInput = createNumberProperty(property, elProperty, elLabel); + break; + } + case 'slider': { + let elSlider = createSliderProperty(property, elProperty, elLabel); + properties[propertyID].elSlider = elSlider[SLIDER_ELEMENTS.SLIDER]; + properties[propertyID].elInput = elSlider[SLIDER_ELEMENTS.NUMBER_INPUT]; + break; + } + case 'vec3': { + let elVec3 = createVec3Property(property, elProperty, elLabel); + properties[propertyID].elInputX = elVec3[VECTOR_ELEMENTS.X_INPUT]; + properties[propertyID].elInputY = elVec3[VECTOR_ELEMENTS.Y_INPUT]; + properties[propertyID].elInputZ = elVec3[VECTOR_ELEMENTS.Z_INPUT]; + break; + } + case 'vec2': { + let elVec2 = createVec2Property(property, elProperty, elLabel); + properties[propertyID].elInputX = elVec2[VECTOR_ELEMENTS.X_INPUT]; + properties[propertyID].elInputY = elVec2[VECTOR_ELEMENTS.Y_INPUT]; + break; + } + case 'color': { + let elColor = createColorProperty(property, elProperty, elLabel); + properties[propertyID].elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; + properties[propertyID].elInputR = elColor[COLOR_ELEMENTS.RED_INPUT]; + properties[propertyID].elInputG = elColor[COLOR_ELEMENTS.GREEN_INPUT]; + properties[propertyID].elInputB = elColor[COLOR_ELEMENTS.BLUE_INPUT]; + break; + } + case 'dropdown': { + properties[propertyID].elInput = createDropdownProperty(property, propertyID, elProperty, elLabel); + break; + } + case 'textarea': { + properties[propertyID].elInput = createTextareaProperty(property, elProperty, elLabel); + break; + } + case 'icon': { + let elIcon = createIconProperty(property, elProperty, elLabel); + properties[propertyID].elSpan = elIcon[ICON_ELEMENTS.ICON]; + properties[propertyID].elLabel = elIcon[ICON_ELEMENTS.LABEL]; + break; + } + case 'texture': { + let elTexture = createTextureProperty(property, elProperty, elLabel); + properties[propertyID].elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; + properties[propertyID].elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; + break; + } + case 'buttons': { + properties[propertyID].elProperty = createButtonsProperty(property, elProperty, elLabel); + break; + } + case 'sub-header': { + break; + } + default: { + console.log("EntityProperties - Unknown property type " + + propertyType + " set to property " + propertyID); + break; + } + } + + let showPropertyRule = propertyData.showPropertyRule; + if (showPropertyRule !== undefined) { + let dependentProperty = Object.keys(showPropertyRule)[0]; + let dependentPropertyValue = showPropertyRule[dependentProperty]; + if (properties[dependentProperty] === undefined) { + properties[dependentProperty] = {}; + } + if (properties[dependentProperty].showPropertyRules === undefined) { + properties[dependentProperty].showPropertyRules = {}; + } + properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue; + } + }); + + elGroups[group.id] = elGroup; + }); + updateVisibleSpaceModeProperties(); + if (window.EventBridge !== undefined) { - var properties; EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); if (data.type === "server_script_status") { + let elServerScriptError = document.getElementById("property-serverScripts-error"); + let elServerScriptStatus = document.getElementById("property-serverScripts-status"); elServerScriptError.value = data.errorInfo; // If we just set elServerScriptError's diplay to block or none, we still end up with // it's parent contributing 21px bottom padding even when elServerScriptError is display:none. @@ -900,231 +2912,37 @@ function loaded() { if (data.statusRetrieved === false) { elServerScriptStatus.innerText = "Failed to retrieve status"; } else if (data.isRunning) { - var ENTITY_SCRIPT_STATUS = { - pending: "Pending", - loading: "Loading", - error_loading_script: "Error loading script", // eslint-disable-line camelcase - error_running_script: "Error running script", // eslint-disable-line camelcase - running: "Running", - unloaded: "Unloaded" - }; elServerScriptStatus.innerText = ENTITY_SCRIPT_STATUS[data.status] || data.status; } else { - elServerScriptStatus.innerText = "Not running"; + elServerScriptStatus.innerText = NOT_RUNNING_SCRIPT_STATUS; } } else if (data.type === "update" && data.selections) { - + if (data.spaceMode !== undefined) { + currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; + } if (data.selections.length === 0) { if (lastEntityID !== null) { if (editor !== null) { - saveJSONUserData(true); + saveUserData(); deleteJSONEditor(); } if (materialEditor !== null) { - saveJSONMaterialData(true); + saveMaterialData(); deleteJSONMaterialEditor(); } } - - elTypeIcon.style.display = "none"; - elType.innerHTML = "No selection"; - elPropertiesList.className = ''; - - elID.value = ""; - elName.value = ""; - elLocked.checked = false; - elVisible.checked = false; - - elParentID.value = ""; - elParentJointIndex.value = ""; - - elColorRed.value = ""; - elColorGreen.value = ""; - elColorBlue.value = ""; - elColorControlVariant2.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - - elPositionX.value = ""; - elPositionY.value = ""; - elPositionZ.value = ""; - - elRotationX.value = ""; - elRotationY.value = ""; - elRotationZ.value = ""; - - elDimensionsX.value = ""; - elDimensionsY.value = ""; - elDimensionsZ.value = ""; - - elRegistrationX.value = ""; - elRegistrationY.value = ""; - elRegistrationZ.value = ""; - - elLinearVelocityX.value = ""; - elLinearVelocityY.value = ""; - elLinearVelocityZ.value = ""; - elLinearDamping.value = ""; - - elAngularVelocityX.value = ""; - elAngularVelocityY.value = ""; - elAngularVelocityZ.value = ""; - elAngularDamping.value = ""; - - elGravityX.value = ""; - elGravityY.value = ""; - elGravityZ.value = ""; - - elAccelerationX.value = ""; - elAccelerationY.value = ""; - elAccelerationZ.value = ""; - - elRestitution.value = ""; - elFriction.value = ""; - elDensity.value = ""; - - elCollisionless.checked = false; - elDynamic.checked = false; - - elCollideStatic.checked = false; - elCollideKinematic.checked = false; - elCollideDynamic.checked = false; - elCollideMyAvatar.checked = false; - elCollideOtherAvatar.checked = false; - - elGrabbable.checked = false; - elTriggerable.checked = false; - elIgnoreIK.checked = false; - - elCloneable.checked = false; - elCloneableDynamic.checked = false; - elCloneableAvatarEntity.checked = false; - elCloneableGroup.style.display = "none"; - elCloneableLimit.value = ""; - elCloneableLifetime.value = ""; - - showElements(document.getElementsByClassName('can-cast-shadow-section'), true); - elCanCastShadow.checked = false; - - elCollisionSoundURL.value = ""; - elLifetime.value = ""; - elScriptURL.value = ""; - elServerScripts.value = ""; - elHyperlinkHref.value = ""; - elDescription.value = ""; - + + resetProperties(); + showGroupsForType("None"); + deleteJSONEditor(); - elUserData.value = ""; + getPropertyInputElement("userData").value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); - // Shape Properties - elShape.value = "Cube"; - setDropdownText(elShape); - - // Light Properties - elLightSpotLight.checked = false; - elLightColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elLightColorRed.value = ""; - elLightColorGreen.value = ""; - elLightColorBlue.value = ""; - elLightIntensity.value = ""; - elLightFalloffRadius.value = ""; - elLightExponent.value = ""; - elLightCutoff.value = ""; - - // Model Properties - elModelURL.value = ""; - elCompoundShapeURL.value = ""; - elShapeType.value = "none"; - setDropdownText(elShapeType); - elModelAnimationURL.value = "" - elModelAnimationPlaying.checked = false; - elModelAnimationFPS.value = ""; - elModelAnimationFrame.value = ""; - elModelAnimationFirstFrame.value = ""; - elModelAnimationLastFrame.value = ""; - elModelAnimationLoop.checked = false; - elModelAnimationHold.checked = false; - elModelAnimationAllowTranslation.checked = false; - elModelTextures.value = ""; - elModelOriginalTextures.value = ""; - - // Zone Properties - elZoneFlyingAllowed.checked = false; - elZoneGhostingAllowed.checked = false; - elZoneFilterURL.value = ""; - elZoneKeyLightColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elZoneKeyLightColorRed.value = ""; - elZoneKeyLightColorGreen.value = ""; - elZoneKeyLightColorBlue.value = ""; - elZoneKeyLightIntensity.value = ""; - elZoneKeyLightDirectionX.value = ""; - elZoneKeyLightDirectionY.value = ""; - elZoneKeyLightCastShadows.checked = false; - elZoneAmbientLightIntensity.value = ""; - elZoneAmbientLightURL.value = ""; - elZoneHazeRange.value = ""; - elZoneHazeColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elZoneHazeColorRed.value = ""; - elZoneHazeColorGreen.value = ""; - elZoneHazeColorBlue.value = ""; - elZoneHazeBackgroundBlend.value = 0; - elZoneHazeGlareColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elZoneHazeGlareColorRed.value = ""; - elZoneHazeGlareColorGreen.value = ""; - elZoneHazeGlareColorBlue.value = ""; - elZoneHazeEnableGlare.checked = false; - elZoneHazeGlareAngle.value = ""; - elZoneHazeAltitudeEffect.checked = false; - elZoneHazeBaseRef.value = ""; - elZoneHazeCeiling.value = ""; - elZoneBloomIntensity.value = ""; - elZoneBloomThreshold.value = ""; - elZoneBloomSize.value = ""; - elZoneSkyboxColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elZoneSkyboxColorRed.value = ""; - elZoneSkyboxColorGreen.value = ""; - elZoneSkyboxColorBlue.value = ""; - elZoneSkyboxURL.value = ""; - showElements(document.getElementsByClassName('keylight-section'), true); - showElements(document.getElementsByClassName('skybox-section'), true); - showElements(document.getElementsByClassName('ambient-section'), true); - showElements(document.getElementsByClassName('haze-section'), true); - showElements(document.getElementsByClassName('bloom-section'), true); - - // Text Properties - elTextText.value = ""; - elTextLineHeight.value = ""; - elTextFaceCamera.checked = false; - elTextTextColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elTextTextColorRed.value = ""; - elTextTextColorGreen.value = ""; - elTextTextColorBlue.value = ""; - elTextBackgroundColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elTextBackgroundColorRed.value = ""; - elTextBackgroundColorGreen.value = ""; - elTextBackgroundColorBlue.value = ""; - - // Image Properties - elImageURL.value = ""; - - // Web Properties - elWebSourceURL.value = ""; - elWebDPI.value = ""; - - // Material Properties - elMaterialURL.value = ""; - elParentMaterialNameNumber.value = ""; - elParentMaterialNameCheckbox.checked = false; - elPriority.value = ""; - elMaterialMappingPosX.value = ""; - elMaterialMappingPosY.value = ""; - elMaterialMappingScaleX.value = ""; - elMaterialMappingScaleY.value = ""; - elMaterialMappingRot.value = ""; - deleteJSONMaterialEditor(); - elMaterialData.value = ""; + getPropertyInputElement("materialData").value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); @@ -1133,15 +2951,16 @@ function loaded() { } else if (data.selections.length > 1) { deleteJSONEditor(); deleteJSONMaterialEditor(); - var selections = data.selections; + + let selections = data.selections; - var ids = []; - var types = {}; - var numTypes = 0; + let ids = []; + let types = {}; + let numTypes = 0; - for (var i = 0; i < selections.length; i++) { + for (let i = 0; i < selections.length; ++i) { ids.push(selections[i].id); - var currentSelectedType = selections[i].properties.type; + let currentSelectedType = selections[i].properties.type; if (types[currentSelectedType] === undefined) { types[currentSelectedType] = 0; numTypes += 1; @@ -1149,1047 +2968,464 @@ function loaded() { types[currentSelectedType]++; } - var type = "Multiple"; + let type = "Multiple"; if (numTypes === 1) { type = selections[0].properties.type; } - - elType.innerHTML = type + " (" + data.selections.length + ")"; - elTypeIcon.innerHTML = ICON_FOR_TYPE[type]; - elTypeIcon.style.display = "inline-block"; - elPropertiesList.className = ''; - - elID.value = ""; - + + resetProperties(); + showGroupsForType(type); + + let typeProperty = properties["type"]; + typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; + typeProperty.elSpan.style.display = "inline-block"; + typeProperty.elLabel.innerHTML = type + " (" + data.selections.length + ")"; + disableProperties(); } else { - - properties = data.selections[0].properties; - if (lastEntityID !== '"' + properties.id + '"' && lastEntityID !== null) { + selectedEntityProperties = data.selections[0].properties; + + if (lastEntityID !== '"' + selectedEntityProperties.id + '"' && lastEntityID !== null) { if (editor !== null) { - saveJSONUserData(true); + saveUserData(); } if (materialEditor !== null) { - saveJSONMaterialData(true); + saveMaterialData(); } } - var doSelectElement = lastEntityID === '"' + properties.id + '"'; + let doSelectElement = lastEntityID === '"' + selectedEntityProperties.id + '"'; // the event bridge and json parsing handle our avatar id string differently. - lastEntityID = '"' + properties.id + '"'; - elID.value = properties.id; + lastEntityID = '"' + selectedEntityProperties.id + '"'; // HTML workaround since image is not yet a separate entity type - var IMAGE_MODEL_NAME = 'default-image-model.fbx'; - if (properties.type === "Model") { - var urlParts = properties.modelURL.split('/'); - var propsFilename = urlParts[urlParts.length - 1]; + let IMAGE_MODEL_NAME = 'default-image-model.fbx'; + if (selectedEntityProperties.type === "Model") { + let urlParts = selectedEntityProperties.modelURL.split('/'); + let propsFilename = urlParts[urlParts.length - 1]; if (propsFilename === IMAGE_MODEL_NAME) { - properties.type = "Image"; + selectedEntityProperties.type = "Image"; } } - // Create class name for css ruleset filtering - elPropertiesList.className = properties.type + 'Menu'; - - elType.innerHTML = properties.type; - elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type]; - elTypeIcon.style.display = "inline-block"; - - elLocked.checked = properties.locked; - - elName.value = properties.name; - - elVisible.checked = properties.visible; - - elPositionX.value = properties.position.x.toFixed(4); - elPositionY.value = properties.position.y.toFixed(4); - elPositionZ.value = properties.position.z.toFixed(4); - - elDimensionsX.value = properties.dimensions.x.toFixed(4); - elDimensionsY.value = properties.dimensions.y.toFixed(4); - elDimensionsZ.value = properties.dimensions.z.toFixed(4); - - elParentID.value = properties.parentID; - elParentJointIndex.value = properties.parentJointIndex; - - elRegistrationX.value = properties.registrationPoint.x.toFixed(4); - elRegistrationY.value = properties.registrationPoint.y.toFixed(4); - elRegistrationZ.value = properties.registrationPoint.z.toFixed(4); - - elRotationX.value = properties.rotation.x.toFixed(4); - elRotationY.value = properties.rotation.y.toFixed(4); - elRotationZ.value = properties.rotation.z.toFixed(4); - - elLinearVelocityX.value = properties.velocity.x.toFixed(4); - elLinearVelocityY.value = properties.velocity.y.toFixed(4); - elLinearVelocityZ.value = properties.velocity.z.toFixed(4); - elLinearDamping.value = properties.damping.toFixed(2); - - elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4); - elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4); - elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4); - elAngularDamping.value = properties.angularDamping.toFixed(4); - - elRestitution.value = properties.restitution.toFixed(4); - elFriction.value = properties.friction.toFixed(4); - - elGravityX.value = properties.gravity.x.toFixed(4); - elGravityY.value = properties.gravity.y.toFixed(4); - elGravityZ.value = properties.gravity.z.toFixed(4); - - elAccelerationX.value = properties.acceleration.x.toFixed(4); - elAccelerationY.value = properties.acceleration.y.toFixed(4); - elAccelerationZ.value = properties.acceleration.z.toFixed(4); - - elDensity.value = properties.density.toFixed(4); - elCollisionless.checked = properties.collisionless; - elDynamic.checked = properties.dynamic; - - elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1; - elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1; - elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1; - elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1; - elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1; - - elGrabbable.checked = properties.dynamic; - - elTriggerable.checked = false; - elIgnoreIK.checked = true; - - elCloneable.checked = properties.cloneable; - elCloneableDynamic.checked = properties.cloneDynamic; - elCloneableAvatarEntity.checked = properties.cloneAvatarEntity; - elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; - elCloneableLimit.value = properties.cloneLimit; - elCloneableLifetime.value = properties.cloneLifetime; - - var grabbablesSet = false; - var parsedUserData = {}; - try { - parsedUserData = JSON.parse(properties.userData); - - if ("grabbableKey" in parsedUserData) { - grabbablesSet = true; - var grabbableData = parsedUserData.grabbableKey; - if ("grabbable" in grabbableData) { - elGrabbable.checked = grabbableData.grabbable; - } else { - elGrabbable.checked = true; + showGroupsForType(selectedEntityProperties.type); + + for (let propertyID in properties) { + let property = properties[propertyID]; + let propertyData = property.data; + let propertyName = property.name; + let propertyValue = getPropertyValue(propertyName); + + let isSubProperty = propertyData.subPropertyOf !== undefined; + if (propertyValue === undefined && !isSubProperty) { + continue; + } + + let isPropertyNotNumber = false; + switch (propertyData.type) { + case 'number': + case 'slider': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { + propertyValue = getPropertyValue(propertyData.fallbackProperty); + } + + switch (propertyData.type) { + case 'string': { + property.elInput.value = propertyValue; + break; } - if ("triggerable" in grabbableData) { - elTriggerable.checked = grabbableData.triggerable; - } else if ("wantsTrigger" in grabbableData) { - elTriggerable.checked = grabbableData.wantsTrigger; - } else { - elTriggerable.checked = false; + case 'bool': { + let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; + if (isSubProperty) { + let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; + let subProperties = propertyValue.split(","); + let subPropertyValue = subProperties.indexOf(propertyName) > -1; + property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + } else { + property.elInput.checked = inverse ? !propertyValue : propertyValue; + } + break; } - if ("ignoreIK" in grabbableData) { - elIgnoreIK.checked = grabbableData.ignoreIK; - } else { - elIgnoreIK.checked = true; + case 'number': + case 'slider': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let value = propertyValue / multiplier; + if (propertyData.round !== undefined) { + value = Math.round(value.round) / propertyData.round; + } + if (propertyData.decimals !== undefined) { + property.elInput.value = value.toFixed(propertyData.decimals); + } else { + property.elInput.value = value; + } + if (property.elSlider !== undefined) { + property.elSlider.value = property.elInput.value; + } + break; + } + case 'vec3': + case 'vec2': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let valueX = propertyValue.x / multiplier; + let valueY = propertyValue.y / multiplier; + let valueZ = propertyValue.z / multiplier; + if (propertyData.round !== undefined) { + valueX = Math.round(valueX * propertyData.round) / propertyData.round; + valueY = Math.round(valueY * propertyData.round) / propertyData.round; + valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; + } + if (propertyData.decimals !== undefined) { + property.elInputX.value = valueX.toFixed(propertyData.decimals); + property.elInputY.value = valueY.toFixed(propertyData.decimals); + if (property.elInputZ !== undefined) { + property.elInputZ.value = valueZ.toFixed(propertyData.decimals); + } + } else { + property.elInputX.value = valueX; + property.elInputY.value = valueY; + if (property.elInputZ !== undefined) { + property.elInputZ.value = valueZ; + } + } + break; + } + case 'color': { + property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + + propertyValue.green + "," + + propertyValue.blue + ")"; + property.elInputR.value = propertyValue.red; + property.elInputG.value = propertyValue.green; + property.elInputB.value = propertyValue.blue; + break; + } + case 'dropdown': { + property.elInput.value = propertyValue; + setDropdownText(property.elInput); + break; + } + case 'textarea': { + property.elInput.value = propertyValue; + setTextareaScrolling(property.elInput); + break; + } + case 'icon': { + property.elSpan.innerHTML = propertyData.icons[propertyValue]; + property.elSpan.style.display = "inline-block"; + property.elLabel.innerHTML = propertyValue; + break; + } + case 'texture': { + property.elInput.value = propertyValue; + property.elInput.imageLoad(property.elInput.value); + break; + } + } + + let showPropertyRules = property.showPropertyRules; + if (showPropertyRules !== undefined) { + for (let propertyToShow in showPropertyRules) { + let showIfThisPropertyValue = showPropertyRules[propertyToShow]; + let show = String(propertyValue) === String(showIfThisPropertyValue); + showPropertyElement(propertyToShow, show); } } - } catch (e) { - // TODO: What should go here? - } - if (!grabbablesSet) { - elGrabbable.checked = true; - elTriggerable.checked = false; - elIgnoreIK.checked = true; - elCloneable.checked = false; } - elCollisionSoundURL.value = properties.collisionSoundURL; - elLifetime.value = properties.lifetime; - elScriptURL.value = properties.script; - elScriptTimestamp.value = properties.scriptTimestamp; - elServerScripts.value = properties.serverScripts; + updateVisibleSpaceModeProperties(); - var json = null; + if (selectedEntityProperties.type === "Image") { + let imageLink = JSON.parse(selectedEntityProperties.textures)["tex.picture"]; + getPropertyInputElement("image").value = imageLink; + } else if (selectedEntityProperties.type === "Material") { + let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); + let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); + let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); + let parentMaterialName = selectedEntityProperties.parentMaterialName; + if (parentMaterialName.startsWith(MATERIAL_PREFIX_STRING)) { + elParentMaterialNameString.value = parentMaterialName.replace(MATERIAL_PREFIX_STRING, ""); + showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); + elParentMaterialNameCheckbox.checked = false; + } else { + elParentMaterialNameNumber.value = parseInt(parentMaterialName); + showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); + elParentMaterialNameCheckbox.checked = true; + } + } + + let json = null; try { - json = JSON.parse(properties.userData); + json = JSON.parse(selectedEntityProperties.userData); } catch (e) { // normal text deleteJSONEditor(); - elUserData.value = properties.userData; + getPropertyInputElement("userData").value = selectedEntityProperties.userData; showUserDataTextArea(); showNewJSONEditorButton(); hideSaveUserDataButton(); + hideUserDataSaved(); } if (json !== null) { if (editor === null) { createJSONEditor(); } - setEditorJSON(json); showSaveUserDataButton(); hideUserDataTextArea(); hideNewJSONEditorButton(); + hideUserDataSaved(); } - var materialJson = null; + let materialJson = null; try { - materialJson = JSON.parse(properties.materialData); + materialJson = JSON.parse(selectedEntityProperties.materialData); } catch (e) { // normal text deleteJSONMaterialEditor(); - elMaterialData.value = properties.materialData; + getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; showMaterialDataTextArea(); showNewJSONMaterialEditorButton(); hideSaveMaterialDataButton(); + hideMaterialDataSaved(); } if (materialJson !== null) { if (materialEditor === null) { createJSONMaterialEditor(); } - setMaterialEditorJSON(materialJson); showSaveMaterialDataButton(); hideMaterialDataTextArea(); hideNewJSONMaterialEditorButton(); + hideMaterialDataSaved(); } - - elHyperlinkHref.value = properties.href; - elDescription.value = properties.description; - - - if (properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") { - elShape.value = properties.shape; - setDropdownText(elShape); - } - - if (properties.type === "Shape" || properties.type === "Box" || - properties.type === "Sphere" || properties.type === "ParticleEffect") { - elColorRed.value = properties.color.red; - elColorGreen.value = properties.color.green; - elColorBlue.value = properties.color.blue; - elColorControlVariant2.style.backgroundColor = "rgb(" + properties.color.red + "," + - properties.color.green + "," + properties.color.blue + ")"; - } - - if (properties.type === "Model" || - properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") { - - elCanCastShadow.checked = properties.canCastShadow; - } - - if (properties.type === "Model") { - elModelURL.value = properties.modelURL; - elShapeType.value = properties.shapeType; - setDropdownText(elShapeType); - elCompoundShapeURL.value = properties.compoundShapeURL; - elModelAnimationURL.value = properties.animation.url; - elModelAnimationPlaying.checked = properties.animation.running; - elModelAnimationFPS.value = properties.animation.fps; - elModelAnimationFrame.value = properties.animation.currentFrame; - elModelAnimationFirstFrame.value = properties.animation.firstFrame; - elModelAnimationLastFrame.value = properties.animation.lastFrame; - elModelAnimationLoop.checked = properties.animation.loop; - elModelAnimationHold.checked = properties.animation.hold; - elModelAnimationAllowTranslation.checked = properties.animation.allowTranslation; - elModelTextures.value = properties.textures; - setTextareaScrolling(elModelTextures); - elModelOriginalTextures.value = properties.originalTextures; - setTextareaScrolling(elModelOriginalTextures); - } else if (properties.type === "Web") { - elWebSourceURL.value = properties.sourceUrl; - elWebDPI.value = properties.dpi; - } else if (properties.type === "Image") { - var imageLink = JSON.parse(properties.textures)["tex.picture"]; - elImageURL.value = imageLink; - } else if (properties.type === "Text") { - elTextText.value = properties.text; - elTextLineHeight.value = properties.lineHeight.toFixed(4); - elTextFaceCamera.checked = properties.faceCamera; - elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + - properties.textColor.green + "," + - properties.textColor.blue + ")"; - elTextTextColorRed.value = properties.textColor.red; - elTextTextColorGreen.value = properties.textColor.green; - elTextTextColorBlue.value = properties.textColor.blue; - elTextBackgroundColor.style.backgroundColor = "rgb(" + properties.backgroundColor.red + "," + - properties.backgroundColor.green + "," + - properties.backgroundColor.blue + ")"; - elTextBackgroundColorRed.value = properties.backgroundColor.red; - elTextBackgroundColorGreen.value = properties.backgroundColor.green; - elTextBackgroundColorBlue.value = properties.backgroundColor.blue; - } else if (properties.type === "Light") { - elLightSpotLight.checked = properties.isSpotlight; - - elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," + - properties.color.green + "," + properties.color.blue + ")"; - elLightColorRed.value = properties.color.red; - elLightColorGreen.value = properties.color.green; - elLightColorBlue.value = properties.color.blue; - - elLightIntensity.value = properties.intensity.toFixed(1); - elLightFalloffRadius.value = properties.falloffRadius.toFixed(1); - elLightExponent.value = properties.exponent.toFixed(2); - elLightCutoff.value = properties.cutoff.toFixed(2); - } else if (properties.type === "Zone") { - // Key light - elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit'); - elZoneKeyLightModeDisabled.checked = (properties.keyLightMode === 'disabled'); - elZoneKeyLightModeEnabled.checked = (properties.keyLightMode === 'enabled'); - - elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + - properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; - elZoneKeyLightColorRed.value = properties.keyLight.color.red; - elZoneKeyLightColorGreen.value = properties.keyLight.color.green; - elZoneKeyLightColorBlue.value = properties.keyLight.color.blue; - elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2); - elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2); - elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); - - elZoneKeyLightCastShadows.checked = properties.keyLight.castShadows; - - // Skybox - elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit'); - elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled'); - elZoneSkyboxModeEnabled.checked = (properties.skyboxMode === 'enabled'); - - // Ambient light - elZoneAmbientLightModeInherit.checked = (properties.ambientLightMode === 'inherit'); - elZoneAmbientLightModeDisabled.checked = (properties.ambientLightMode === 'disabled'); - elZoneAmbientLightModeEnabled.checked = (properties.ambientLightMode === 'enabled'); - - elZoneAmbientLightIntensity.value = properties.ambientLight.ambientIntensity.toFixed(2); - elZoneAmbientLightURL.value = properties.ambientLight.ambientURL; - - // Haze - elZoneHazeModeInherit.checked = (properties.hazeMode === 'inherit'); - elZoneHazeModeDisabled.checked = (properties.hazeMode === 'disabled'); - elZoneHazeModeEnabled.checked = (properties.hazeMode === 'enabled'); - - elZoneHazeRange.value = properties.haze.hazeRange.toFixed(0); - elZoneHazeColor.style.backgroundColor = "rgb(" + - properties.haze.hazeColor.red + "," + - properties.haze.hazeColor.green + "," + - properties.haze.hazeColor.blue + ")"; - - elZoneHazeColorRed.value = properties.haze.hazeColor.red; - elZoneHazeColorGreen.value = properties.haze.hazeColor.green; - elZoneHazeColorBlue.value = properties.haze.hazeColor.blue; - elZoneHazeBackgroundBlend.value = properties.haze.hazeBackgroundBlend.toFixed(2); - - elZoneHazeGlareColor.style.backgroundColor = "rgb(" + - properties.haze.hazeGlareColor.red + "," + - properties.haze.hazeGlareColor.green + "," + - properties.haze.hazeGlareColor.blue + ")"; - - elZoneHazeGlareColorRed.value = properties.haze.hazeGlareColor.red; - elZoneHazeGlareColorGreen.value = properties.haze.hazeGlareColor.green; - elZoneHazeGlareColorBlue.value = properties.haze.hazeGlareColor.blue; - - elZoneHazeEnableGlare.checked = properties.haze.hazeEnableGlare; - elZoneHazeGlareAngle.value = properties.haze.hazeGlareAngle.toFixed(0); - - elZoneHazeAltitudeEffect.checked = properties.haze.hazeAltitudeEffect; - elZoneHazeBaseRef.value = properties.haze.hazeBaseRef.toFixed(0); - elZoneHazeCeiling.value = properties.haze.hazeCeiling.toFixed(0); - - elZoneBloomModeInherit.checked = (properties.bloomMode === 'inherit'); - elZoneBloomModeDisabled.checked = (properties.bloomMode === 'disabled'); - elZoneBloomModeEnabled.checked = (properties.bloomMode === 'enabled'); - - elZoneBloomIntensity.value = properties.bloom.bloomIntensity.toFixed(2); - elZoneBloomThreshold.value = properties.bloom.bloomThreshold.toFixed(2); - elZoneBloomSize.value = properties.bloom.bloomSize.toFixed(2); - - elShapeType.value = properties.shapeType; - elCompoundShapeURL.value = properties.compoundShapeURL; - - elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," + - properties.skybox.color.green + "," + properties.skybox.color.blue + ")"; - elZoneSkyboxColorRed.value = properties.skybox.color.red; - elZoneSkyboxColorGreen.value = properties.skybox.color.green; - elZoneSkyboxColorBlue.value = properties.skybox.color.blue; - elZoneSkyboxURL.value = properties.skybox.url; - - elZoneFlyingAllowed.checked = properties.flyingAllowed; - elZoneGhostingAllowed.checked = properties.ghostingAllowed; - elZoneFilterURL.value = properties.filterURL; - - // Show/hide sections as required - showElements(document.getElementsByClassName('skybox-section'), - elZoneSkyboxModeEnabled.checked); - - showElements(document.getElementsByClassName('keylight-section'), - elZoneKeyLightModeEnabled.checked); - - showElements(document.getElementsByClassName('ambient-section'), - elZoneAmbientLightModeEnabled.checked); - - showElements(document.getElementsByClassName('haze-section'), - elZoneHazeModeEnabled.checked); - - showElements(document.getElementsByClassName('bloom-section'), - elZoneBloomModeEnabled.checked); - } else if (properties.type === "PolyVox") { - elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); - elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); - elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); - elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle; - setDropdownText(elVoxelSurfaceStyle); - elXTextureURL.value = properties.xTextureURL; - elYTextureURL.value = properties.yTextureURL; - elZTextureURL.value = properties.zTextureURL; - } else if (properties.type === "Material") { - elMaterialURL.value = properties.materialURL; - //elMaterialMappingMode.value = properties.materialMappingMode; - //setDropdownText(elMaterialMappingMode); - elPriority.value = properties.priority; - if (properties.parentMaterialName.startsWith(MATERIAL_PREFIX_STRING)) { - elParentMaterialNameString.value = properties.parentMaterialName.replace(MATERIAL_PREFIX_STRING, ""); - showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); - elParentMaterialNameCheckbox.checked = false; - } else { - elParentMaterialNameNumber.value = parseInt(properties.parentMaterialName); - showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); - elParentMaterialNameCheckbox.checked = true; - } - elMaterialMappingPosX.value = properties.materialMappingPos.x.toFixed(4); - elMaterialMappingPosY.value = properties.materialMappingPos.y.toFixed(4); - elMaterialMappingScaleX.value = properties.materialMappingScale.x.toFixed(4); - elMaterialMappingScaleY.value = properties.materialMappingScale.y.toFixed(4); - elMaterialMappingRot.value = properties.materialMappingRot.toFixed(2); - } - - // Only these types can cast a shadow - if (properties.type === "Model" || - properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") { - - showElements(document.getElementsByClassName('can-cast-shadow-section'), true); - } else { - showElements(document.getElementsByClassName('can-cast-shadow-section'), false); - } - - if (properties.locked) { + + if (selectedEntityProperties.locked) { disableProperties(); - elLocked.removeAttribute('disabled'); + getPropertyInputElement("locked").removeAttribute('disabled'); } else { enableProperties(); - elSaveUserData.disabled = true; - elSaveMaterialData.disabled = true; + disableSaveUserDataButton(); + disableSaveMaterialDataButton() } - - var activeElement = document.activeElement; + + let activeElement = document.activeElement; if (doSelectElement && typeof activeElement.select !== "undefined") { activeElement.select(); } } + } else if (data.type === 'tooltipsReply') { + createAppTooltip.setIsEnabled(!data.hmdActive); + createAppTooltip.setTooltipData(data.tooltips); + } else if (data.type === 'hmdActiveChanged') { + createAppTooltip.setIsEnabled(!data.hmdActive); + } else if (data.type === 'setSpaceMode') { + currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; + updateVisibleSpaceModeProperties(); } }); + + // Request tooltips as soon as we can process a reply: + EventBridge.emitWebEvent(JSON.stringify({ type: 'tooltipsRequest' })); } - - elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked')); - elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name')); - elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href')); - elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description')); - elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible')); - - var positionChangeFunction = createEmitVec3PropertyUpdateFunction( - 'position', elPositionX, elPositionY, elPositionZ); - elPositionX.addEventListener('change', positionChangeFunction); - elPositionY.addEventListener('change', positionChangeFunction); - elPositionZ.addEventListener('change', positionChangeFunction); - - var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction( - 'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ); - elDimensionsX.addEventListener('change', dimensionsChangeFunction); - elDimensionsY.addEventListener('change', dimensionsChangeFunction); - elDimensionsZ.addEventListener('change', dimensionsChangeFunction); - - elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID')); - elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex', 0)); - - var registrationChangeFunction = createEmitVec3PropertyUpdateFunction( - 'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ); - elRegistrationX.addEventListener('change', registrationChangeFunction); - elRegistrationY.addEventListener('change', registrationChangeFunction); - elRegistrationZ.addEventListener('change', registrationChangeFunction); - - var rotationChangeFunction = createEmitVec3PropertyUpdateFunction( - 'rotation', elRotationX, elRotationY, elRotationZ); - elRotationX.addEventListener('change', rotationChangeFunction); - elRotationY.addEventListener('change', rotationChangeFunction); - elRotationZ.addEventListener('change', rotationChangeFunction); - - var velocityChangeFunction = createEmitVec3PropertyUpdateFunction( - 'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ); - elLinearVelocityX.addEventListener('change', velocityChangeFunction); - elLinearVelocityY.addEventListener('change', velocityChangeFunction); - elLinearVelocityZ.addEventListener('change', velocityChangeFunction); - elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping')); - - var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier( - 'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS); - elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction); - elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction); - elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction); - elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping')); - - elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution')); - elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction')); - - var gravityChangeFunction = createEmitVec3PropertyUpdateFunction( - 'gravity', elGravityX, elGravityY, elGravityZ); - elGravityX.addEventListener('change', gravityChangeFunction); - elGravityY.addEventListener('change', gravityChangeFunction); - elGravityZ.addEventListener('change', gravityChangeFunction); - - var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction( - 'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ); - elAccelerationX.addEventListener('change', accelerationChangeFunction); - elAccelerationY.addEventListener('change', accelerationChangeFunction); - elAccelerationZ.addEventListener('change', accelerationChangeFunction); - - elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density')); - elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless')); - elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic')); - - elCollideDynamic.addEventListener('change', function() { - updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic'); - }); - - elCollideKinematic.addEventListener('change', function() { - updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic'); - }); - - elCollideStatic.addEventListener('change', function() { - updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static'); - }); - elCollideMyAvatar.addEventListener('change', function() { - updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar'); - }); - elCollideOtherAvatar.addEventListener('change', function() { - updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar'); - }); - - elGrabbable.addEventListener('change', function() { - if (elCloneable.checked) { - elGrabbable.checked = false; - } - userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true); - }); - elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); - elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneDynamic')); - elCloneableAvatarEntity.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneAvatarEntity')); - elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLifetime')); - elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLimit')); - - elTriggerable.addEventListener('change', function() { - userDataChanger("grabbableKey", "triggerable", elTriggerable, elUserData, false, ['wantsTrigger']); + // Server Script Status + let serverScriptProperty = properties["serverScripts"]; + let elServerScript = serverScriptProperty.elInput; + let serverScriptElementID = serverScriptProperty.elementID; + let serverScriptStatusElementID = serverScriptElementID + "-status"; + let elDiv = document.createElement('div'); + elDiv.className = "property"; + elDiv.setAttribute("id", "div-" + serverScriptStatusElementID); + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", serverScriptStatusElementID); + elLabel.innerText = "Server Script Status"; + createAppTooltip.registerTooltipElement(elLabel, "serverScriptsStatus"); + let elServerScriptStatus = document.createElement('span'); + elServerScriptStatus.setAttribute("id", serverScriptStatusElementID); + elDiv.appendChild(elLabel); + elDiv.appendChild(elServerScriptStatus); + elServerScript.parentNode.appendChild(elDiv); + + // Server Script Error + elDiv = document.createElement('div'); + elDiv.className = "property"; + let elServerScriptError = document.createElement('textarea'); + elServerScriptError.setAttribute("id", serverScriptElementID + "-error"); + elDiv.appendChild(elServerScriptError); + elServerScript.parentNode.appendChild(elDiv); + + let elScript = getPropertyInputElement("script"); + elScript.parentNode.className = "property url refresh"; + elServerScript.parentNode.className = "property url refresh"; + + // User Data + let userDataProperty = properties["userData"]; + let elUserData = userDataProperty.elInput; + let userDataElementID = userDataProperty.elementID; + elDiv = elUserData.parentNode; + let elStaticUserData = document.createElement('div'); + elStaticUserData.setAttribute("id", userDataElementID + "-static"); + let elUserDataEditor = document.createElement('div'); + elUserDataEditor.setAttribute("id", userDataElementID + "-editor"); + let elUserDataSaved = document.createElement('span'); + elUserDataSaved.setAttribute("id", userDataElementID + "-saved"); + elUserDataSaved.innerText = "Saved!"; + elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elUserDataSaved); + elDiv.insertBefore(elStaticUserData, elUserData); + elDiv.insertBefore(elUserDataEditor, elUserData); + + // Material Data + let materialDataProperty = properties["materialData"]; + let elMaterialData = materialDataProperty.elInput; + let materialDataElementID = materialDataProperty.elementID; + elDiv = elMaterialData.parentNode; + let elStaticMaterialData = document.createElement('div'); + elStaticMaterialData.setAttribute("id", materialDataElementID + "-static"); + let elMaterialDataEditor = document.createElement('div'); + elMaterialDataEditor.setAttribute("id", materialDataElementID + "-editor"); + let elMaterialDataSaved = document.createElement('span'); + elMaterialDataSaved.setAttribute("id", materialDataElementID + "-saved"); + elMaterialDataSaved.innerText = "Saved!"; + elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elMaterialDataSaved); + elDiv.insertBefore(elStaticMaterialData, elMaterialData); + elDiv.insertBefore(elMaterialDataEditor, elMaterialData); + + // Special Property Callbacks + let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); + let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); + let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); + elParentMaterialNameString.addEventListener('change', function () { + updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value, false); }); - elIgnoreIK.addEventListener('change', function() { - userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, true); + elParentMaterialNameNumber.addEventListener('change', function () { + updateProperty("parentMaterialName", this.value, false); }); - - elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL')); - - elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); - elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); - elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp')); - elServerScripts.addEventListener('change', createEmitTextPropertyUpdateFunction('serverScripts')); - elServerScripts.addEventListener('change', function() { - // invalidate the current status (so that same-same updates can still be observed visually) - elServerScriptStatus.innerText = PENDING_SCRIPT_STATUS; - }); - - elClearUserData.addEventListener("click", function() { - deleteJSONEditor(); - elUserData.value = ""; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - updateProperty('userData', elUserData.value); - }); - - elSaveUserData.addEventListener("click", function() { - saveJSONUserData(true); - }); - - elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); - - elNewJSONEditor.addEventListener('click', function() { - deleteJSONEditor(); - createJSONEditor(); - var data = {}; - setEditorJSON(data); - hideUserDataTextArea(); - hideNewJSONEditorButton(); - showSaveUserDataButton(); - }); - - elClearMaterialData.addEventListener("click", function() { - deleteJSONMaterialEditor(); - elMaterialData.value = ""; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - updateProperty('materialData', elMaterialData.value); - }); - - elSaveMaterialData.addEventListener("click", function() { - saveJSONMaterialData(true); - }); - - elMaterialData.addEventListener('change', createEmitTextPropertyUpdateFunction('materialData')); - - elNewJSONMaterialEditor.addEventListener('click', function() { - deleteJSONMaterialEditor(); - createJSONMaterialEditor(); - var data = {}; - setMaterialEditorJSON(data); - hideMaterialDataTextArea(); - hideNewJSONMaterialEditorButton(); - showSaveMaterialDataButton(); - }); - - var colorChangeFunction = createEmitColorPropertyUpdateFunction( - 'color', elColorRed, elColorGreen, elColorBlue); - elColorRed.addEventListener('change', colorChangeFunction); - elColorGreen.addEventListener('change', colorChangeFunction); - elColorBlue.addEventListener('change', colorChangeFunction); - colorPickers['#property-color-control2'] = $('#property-color-control2').colpick({ - colorScheme: 'dark', - layout: 'hex', - color: '000000', - submit: false, // We don't want to have a submission button - onShow: function(colpick) { - $('#property-color-control2').attr('active', 'true'); - // The original color preview within the picker needs to be updated on show because - // prior to the picker being shown we don't have access to the selections' starting color. - colorPickers['#property-color-control2'].colpickSetColor({ - "r": elColorRed.value, - "g": elColorGreen.value, - "b": elColorBlue.value}); - }, - onHide: function(colpick) { - $('#property-color-control2').attr('active', 'false'); - }, - onChange: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); - } - }); - - elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight')); - - var lightColorChangeFunction = createEmitColorPropertyUpdateFunction( - 'color', elLightColorRed, elLightColorGreen, elLightColorBlue); - elLightColorRed.addEventListener('change', lightColorChangeFunction); - elLightColorGreen.addEventListener('change', lightColorChangeFunction); - elLightColorBlue.addEventListener('change', lightColorChangeFunction); - colorPickers['#property-light-color'] = $('#property-light-color').colpick({ - colorScheme: 'dark', - layout: 'hex', - color: '000000', - submit: false, // We don't want to have a submission button - onShow: function(colpick) { - $('#property-light-color').attr('active', 'true'); - // The original color preview within the picker needs to be updated on show because - // prior to the picker being shown we don't have access to the selections' starting color. - colorPickers['#property-light-color'].colpickSetColor({ - "r": elLightColorRed.value, - "g": elLightColorGreen.value, - "b": elLightColorBlue.value - }); - }, - onHide: function(colpick) { - $('#property-light-color').attr('active', 'false'); - }, - onChange: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); - } - }); - - elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1)); - elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1)); - elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2)); - elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2)); - - elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); - - elCanCastShadow.addEventListener('change', createEmitCheckedPropertyUpdateFunction('canCastShadow')); - - elImageURL.addEventListener('change', createImageURLUpdateFunction('textures')); - - elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); - elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi', 0)); - - elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); - elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); - elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL')); - - elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url')); - elModelAnimationPlaying.addEventListener('change',createEmitGroupCheckedPropertyUpdateFunction('animation', 'running')); - elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'fps')); - elModelAnimationFrame.addEventListener('change', - createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame')); - elModelAnimationFirstFrame.addEventListener('change', - createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame')); - elModelAnimationLastFrame.addEventListener('change', - createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); - elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop')); - elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold')); - elModelAnimationAllowTranslation.addEventListener('change', - createEmitGroupCheckedPropertyUpdateFunction('animation', 'allowTranslation')); - - elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); - - elMaterialURL.addEventListener('change', createEmitTextPropertyUpdateFunction('materialURL')); - //elMaterialMappingMode.addEventListener('change', createEmitTextPropertyUpdateFunction('materialMappingMode')); - elPriority.addEventListener('change', createEmitNumberPropertyUpdateFunction('priority', 0)); - - elParentMaterialNameString.addEventListener('change', function () { updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value); }); - elParentMaterialNameNumber.addEventListener('change', function () { updateProperty("parentMaterialName", this.value); }); elParentMaterialNameCheckbox.addEventListener('change', function () { if (this.checked) { - updateProperty("parentMaterialName", elParentMaterialNameNumber.value); + updateProperty("parentMaterialName", elParentMaterialNameNumber.value, false); showParentMaterialNameBox(true, elParentMaterialNameNumber, elParentMaterialNameString); } else { - updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value); + updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + elParentMaterialNameString.value, false); showParentMaterialNameBox(false, elParentMaterialNameNumber, elParentMaterialNameString); } }); + + getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false)); + + // Collapsible sections + let elCollapsible = document.getElementsByClassName("section-header"); - var materialMappingPosChangeFunction = createEmitVec2PropertyUpdateFunction('materialMappingPos', elMaterialMappingPosX, elMaterialMappingPosY); - elMaterialMappingPosX.addEventListener('change', materialMappingPosChangeFunction); - elMaterialMappingPosY.addEventListener('change', materialMappingPosChangeFunction); - var materialMappingScaleChangeFunction = createEmitVec2PropertyUpdateFunction('materialMappingScale', elMaterialMappingScaleX, elMaterialMappingScaleY); - elMaterialMappingScaleX.addEventListener('change', materialMappingScaleChangeFunction); - elMaterialMappingScaleY.addEventListener('change', materialMappingScaleChangeFunction); - elMaterialMappingRot.addEventListener('change', createEmitNumberPropertyUpdateFunction('materialMappingRot', 2)); + let toggleCollapsedEvent = function(event) { + let element = event.target.parentNode.parentNode; + let isCollapsed = element.dataset.collapsed !== "true"; + element.dataset.collapsed = isCollapsed ? "true" : false; + element.setAttribute("collapsed", isCollapsed ? "true" : "false"); + element.getElementsByClassName(".collapse-icon")[0].textContent = isCollapsed ? "L" : "M"; + }; - elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text')); - elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera')); - elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight')); - var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction( - 'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue); - elTextTextColorRed.addEventListener('change', textTextColorChangeFunction); - elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction); - elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction); - colorPickers['#property-text-text-color'] = $('#property-text-text-color').colpick({ - colorScheme: 'dark', - layout: 'hex', - color: '000000', - submit: false, // We don't want to have a submission button - onShow: function(colpick) { - $('#property-text-text-color').attr('active', 'true'); - // The original color preview within the picker needs to be updated on show because - // prior to the picker being shown we don't have access to the selections' starting color. - colorPickers['#property-text-text-color'].colpickSetColor({ - "r": elTextTextColorRed.value, - "g": elTextTextColorGreen.value, - "b": elTextTextColorBlue.value - }); - }, - onHide: function(colpick) { - $('#property-text-text-color').attr('active', 'false'); - }, - onChange: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).attr('active', 'false'); - emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); + for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { + let curCollapsibleElement = elCollapsible[collapseIndex]; + curCollapsibleElement.getElementsByTagName('span')[0].addEventListener("click", toggleCollapsedEvent, true); + } + + // Textarea scrollbars + let elTextareas = document.getElementsByTagName("TEXTAREA"); + + let textareaOnChangeEvent = function(event) { + setTextareaScrolling(event.target); + }; + + for (let textAreaIndex = 0, numTextAreas = elTextareas.length; textAreaIndex < numTextAreas; ++textAreaIndex) { + let curTextAreaElement = elTextareas[textAreaIndex]; + setTextareaScrolling(curTextAreaElement); + curTextAreaElement.addEventListener("input", textareaOnChangeEvent, false); + curTextAreaElement.addEventListener("change", textareaOnChangeEvent, false); + /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize + event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ + curTextAreaElement.addEventListener("mouseup", textareaOnChangeEvent, false); + } + + // Dropdowns + // For each dropdown the following replacement is created in place of the original dropdown... + // Structure created: + //
+ //
display textcarat
+ //
+ //
    + //
  • 0) { + let el = elDropdowns[0]; + el.parentNode.removeChild(el); + elDropdowns = document.getElementsByTagName("select"); + } + document.addEventListener("keydown", function (keyDown) { if (keyDown.keyCode === KEY_P && keyDown.ctrlKey) { if (keyDown.shiftKey) { @@ -2199,156 +3435,29 @@ function loaded() { } } }); + window.onblur = function() { // Fake a change event - var ev = document.createEvent("HTMLEvents"); + let ev = document.createEvent("HTMLEvents"); ev.initEvent("change", true, true); document.activeElement.dispatchEvent(ev); }; - + // For input and textarea elements, select all of the text on focus - var els = document.querySelectorAll("input, textarea"); - for (var i = 0; i < els.length; i++) { + let els = document.querySelectorAll("input, textarea"); + for (let i = 0; i < els.length; ++i) { els[i].onfocus = function (e) { e.target.select(); }; } + + bindAllNonJSONEditorElements(); - bindAllNonJSONEditorElements(); + showGroupsForType("None"); + resetProperties(); + disableProperties(); }); - // Collapsible sections - var elCollapsible = document.getElementsByClassName("section-header"); - - var toggleCollapsedEvent = function(event) { - var element = event.target.parentNode.parentNode; - var isCollapsed = element.dataset.collapsed !== "true"; - element.dataset.collapsed = isCollapsed ? "true" : false; - element.setAttribute("collapsed", isCollapsed ? "true" : "false"); - element.getElementsByClassName(".collapse-icon")[0].textContent = isCollapsed ? "L" : "M"; - }; - - for (var collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { - var curCollapsibleElement = elCollapsible[collapseIndex]; - curCollapsibleElement.getElementsByTagName('span')[0].addEventListener("click", toggleCollapsedEvent, true); - } - - - // Textarea scrollbars - var elTextareas = document.getElementsByTagName("TEXTAREA"); - - var textareaOnChangeEvent = function(event) { - setTextareaScrolling(event.target); - }; - - for (var textAreaIndex = 0, numTextAreas = elTextareas.length; textAreaIndex < numTextAreas; ++textAreaIndex) { - var curTextAreaElement = elTextareas[textAreaIndex]; - setTextareaScrolling(curTextAreaElement); - curTextAreaElement.addEventListener("input", textareaOnChangeEvent, false); - curTextAreaElement.addEventListener("change", textareaOnChangeEvent, false); - /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize - event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ - curTextAreaElement.addEventListener("mouseup", textareaOnChangeEvent, false); - } - - // Dropdowns - // For each dropdown the following replacement is created in place of the original dropdown... - // Structure created: - //
    - //
    display textcarat
    - //
    - //
      - //
    • 0) { - var el = elDropdowns[0]; - el.parentNode.removeChild(el); - elDropdowns = document.getElementsByTagName("select"); - } - augmentSpinButtons(); // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 10dc37efba..62163ffe16 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -38,7 +38,7 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio this.lastRowShiftScrollTop = 0; this.initialize(); -}; +} ListView.prototype = { getNumRows: function() { @@ -152,6 +152,30 @@ ListView.prototype = { this.refresh(); } }, + + /** + * Scrolls firstRowIndex with least effort, also tries to make the window include the other selections in case lastRowIndex is set. + * In the case that firstRowIndex and lastRowIndex are already within the visible bounds then nothing will happen. + * @param {number} firstRowIndex - The row that will be scrolled to. + * @param {number} lastRowIndex - The last index of the bound. + */ + scrollToRow: function (firstRowIndex, lastRowIndex) { + lastRowIndex = lastRowIndex ? lastRowIndex : firstRowIndex; + let boundingTop = firstRowIndex * this.rowHeight; + let boundingBottom = (lastRowIndex * this.rowHeight) + this.rowHeight; + if ((boundingBottom - boundingTop) > this.elTableScroll.clientHeight) { + boundingBottom = boundingTop + this.elTableScroll.clientHeight; + } + + let currentVisibleAreaTop = this.elTableScroll.scrollTop; + let currentVisibleAreaBottom = currentVisibleAreaTop + this.elTableScroll.clientHeight; + + if (boundingTop < currentVisibleAreaTop) { + this.elTableScroll.scrollTop = boundingTop; + } else if (boundingBottom > currentVisibleAreaBottom) { + this.elTableScroll.scrollTop = boundingBottom - (this.elTableScroll.clientHeight); + } + }, refresh: function() { // block refreshing before rows are initialized diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 24a96023da..28451a14cb 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -27,6 +27,7 @@ var xmlHttpRequest = null; var isPreparing = false; // Explicitly track download request status. + var limitedCommerce = false; var commerceMode = false; var userIsLoggedIn = false; var walletNeedsSetup = false; @@ -59,7 +60,7 @@ ); // Footer. - var isInitialHiFiPage = location.href === marketplaceBaseURL + "/marketplace?"; + var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace?"); $("body").append( '
      ' + (!isInitialHiFiPage ? '' : '') + @@ -92,7 +93,7 @@ window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = marketplaceBaseURL + "/marketplace"; + window.location = marketplaceBaseURL + "/marketplace?"; }); } @@ -169,7 +170,7 @@ var span = document.createElement('span'); span.style = "margin:10px;color:#1b6420;font-size:15px;"; - span.innerHTML = "to purchase items from the Marketplace."; + span.innerHTML = "to get items from the Marketplace."; var xButton = document.createElement('a'); xButton.id = "xButton"; @@ -195,40 +196,6 @@ } } - function maybeAddPurchasesButton() { - if (userIsLoggedIn) { - // Why isn't this an id?! This really shouldn't be a class on the website, but it is. - var navbarBrandElement = document.getElementsByClassName('navbar-brand')[0]; - var purchasesElement = document.createElement('a'); - var dropDownElement = document.getElementById('user-dropdown'); - - $('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" + - "text-overflow:ellipsis;display:inline-block;position:relative;top:4px;"; - $('#user-dropdown').find('.caret')[0].style = "position:relative;top:-3px;"; - - purchasesElement.id = "purchasesButton"; - purchasesElement.setAttribute('href', "#"); - purchasesElement.innerHTML = ""; - if (messagesWaiting) { - purchasesElement.innerHTML += " "; - } - purchasesElement.innerHTML += "My Purchases"; - // FRONTEND WEBDEV RANT: The username dropdown should REALLY not be programmed to be on the same - // line as the search bar, overlaid on top of the search bar, floated right, and then relatively bumped up using "top:-50px". - $('.navbar-brand').css('margin-right', '10px'); - purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) + - "px;position:relative;z-index:999;"; - navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement); - $('#purchasesButton').on('click', function () { - EventBridge.emitWebEvent(JSON.stringify({ - type: "PURCHASES", - referrerURL: window.location.href, - hasUpdates: messagesWaiting - })); - }); - } - } - function changeDropdownMenu() { var logInOrOutButton = document.createElement('a'); logInOrOutButton.id = "logInOrOutButton"; @@ -283,6 +250,7 @@ $(this).attr('href', '#'); } cost = $(this).closest('.col-xs-3').find('.item-cost').text(); + var costInt = parseInt(cost, 10); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); @@ -312,11 +280,12 @@ var getString = "GET"; // Protection against the button getting stuck in the "BUY"/"GET" state. // That happens when the browser gets two MOUSEENTER events before getting a - // MOUSELEAVE event. - if ($this.text() === buyString || $this.text() === getString) { - return; - } - if ($this.text() === 'invalidated') { + // MOUSELEAVE event. Also, if not available for sale, just return. + if ($this.text() === buyString || + $this.text() === getString || + $this.text() === 'invalidated' || + $this.text() === 'sold out' || + $this.text() === 'not for sale' ) { return; } $this.data('initialHtml', $this.html()); @@ -337,7 +306,10 @@ $('.grid-item').find('#price-or-edit').find('a').on('click', function () { - if ($(this).closest('.grid-item').find('.price').text() === 'invalidated') { + var price = $(this).closest('.grid-item').find('.price').text(); + if (price === 'invalidated' || + price === 'sold out' || + price === 'not for sale') { return false; } buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), @@ -398,7 +370,6 @@ // Try this here in case it works (it will if the user just pressed the "back" button, // since that doesn't trigger another AJAX request. injectBuyButtonOnMainPage(); - maybeAddPurchasesButton(); } } @@ -419,7 +390,12 @@ var href = purchaseButton.attr('href'); purchaseButton.attr('href', '#'); + var cost = $('.item-cost').text(); + var costInt = parseInt(cost, 10); var availability = $.trim($('.item-availability').text()); + if (limitedCommerce && (costInt > 0)) { + availability = ''; + } if (availability === 'available') { purchaseButton.css({ "background": "linear-gradient(#00b4ef, #0093C5)", @@ -436,14 +412,13 @@ }); } - var cost = $('.item-cost').text(); var type = $('.item-type').text(); var isUpdating = window.location.href.indexOf('edition=') > -1; var urlParams = new URLSearchParams(window.location.search); if (isUpdating) { purchaseButton.html('UPDATE FOR FREE'); } else if (availability !== 'available') { - purchaseButton.html('UNAVAILABLE (' + availability + ')'); + purchaseButton.html('UNAVAILABLE ' + (availability ? ('(' + availability + ')') : '')); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE ' + cost); @@ -461,7 +436,6 @@ type); } }); - maybeAddPurchasesButton(); } } @@ -742,6 +716,7 @@ cancelClaraDownload(); } else if (message.type === "marketplaces") { if (message.action === "commerceSetting") { + limitedCommerce = !!message.data.limitedCommerce; commerceMode = !!message.data.commerceMode; userIsLoggedIn = !!message.data.userIsLoggedIn; walletNeedsSetup = !!message.data.walletNeedsSetup; diff --git a/scripts/system/particle_explorer/underscore-min.js b/scripts/system/html/js/underscore-min.js similarity index 100% rename from scripts/system/particle_explorer/underscore-min.js rename to scripts/system/html/js/underscore-min.js diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 19e603b4ab..e2db032d8c 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -14,17 +14,19 @@ (function() { Script.include("/~/system/libraries/Xform.js"); + Script.include("/~/system/libraries/globals.js"); var DEBUG = false; - var MIN_LOADING_PROGRESS = 3.6; - var TOTAL_LOADING_PROGRESS = 3.8; - var EPSILON = 0.01; + var TOTAL_LOADING_PROGRESS = 3.7; + var EPSILON = 0.05; + var TEXTURE_EPSILON = 0.01; var isVisible = false; var VOLUME = 0.4; - var tune = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/crystals_and_voices.wav"); + var tune = SoundCache.getSound(Script.resolvePath("/~/system/assets/sounds/crystals_and_voices.mp3")); var sample = null; var MAX_LEFT_MARGIN = 1.9; var INNER_CIRCLE_WIDTH = 4.7; var DEFAULT_Z_OFFSET = 5.45; + var LOADING_IMAGE_WIDTH_PIXELS = 1024; var previousCameraMode = Camera.mode; var renderViewTask = Render.getConfig("RenderMainView"); @@ -62,9 +64,9 @@ var loadingSphereID = Overlays.addOverlay("model", { name: "Loading-Sphere", - position: Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0.0, y: -1.0, z: 0.0}), Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.95, z: 0})), - orientation: Quat.multiply(Quat.fromVec3Degrees({x: 0, y: 180, z: 0}), MyAvatar.orientation), - url: "http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/black-sphere.fbx", + position: Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0.0, y: -1.0, z: 0.0 }), Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.95, z: 0 })), + orientation: Quat.multiply(Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), MyAvatar.orientation), + url: Script.resolvePath("/~/system/assets/models/black-sphere.fbx"), dimensions: DEFAULT_DIMENSIONS, alpha: 1, visible: isVisible, @@ -75,12 +77,12 @@ }); var anchorOverlay = Overlays.addOverlay("cube", { - dimensions: {x: 0.2, y: 0.2, z: 0.2}, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, visible: false, grabbable: false, ignoreRayIntersection: true, - localPosition: {x: 0.0, y: getAnchorLocalYOffset(), z: DEFAULT_Z_OFFSET }, - orientation: Quat.multiply(Quat.fromVec3Degrees({x: 0, y: 180, z: 0}), MyAvatar.orientation), + localPosition: { x: 0.0, y: getAnchorLocalYOffset(), z: DEFAULT_Z_OFFSET }, + orientation: Quat.multiply(Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), MyAvatar.orientation), solid: true, drawInFront: true, parentID: loadingSphereID @@ -112,7 +114,6 @@ backgroundAlpha: 1, lineHeight: 0.13, visible: isVisible, - backgroundAlpha: 0, ignoreRayIntersection: true, drawInFront: true, grabbable: false, @@ -124,7 +125,7 @@ var domainToolTip = Overlays.addOverlay("text3d", { name: "Loading-Tooltip", - localPosition: { x: 0.0 , y: -1.6, z: 0.0 }, + localPosition: { x: 0.0, y: -1.6, z: 0.0 }, text: toolTip, textAlpha: 1, backgroundAlpha: 0.00393, @@ -139,14 +140,14 @@ var loadingToTheSpotText = Overlays.addOverlay("text3d", { name: "Loading-Destination-Card-Text", - localPosition: { x: 0.0 , y: -1.687, z: -0.3 }, + localPosition: { x: 0.0, y: -1.687, z: -0.3 }, text: "Go To TheSpot", textAlpha: 1, backgroundAlpha: 0.00393, lineHeight: 0.10, visible: isVisible, ignoreRayIntersection: true, - dimensions: {x: 1, y: 0.17}, + dimensions: { x: 1, y: 0.17 }, drawInFront: true, grabbable: false, localOrientation: Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), @@ -155,7 +156,7 @@ var loadingToTheSpotID = Overlays.addOverlay("image3d", { name: "Loading-Destination-Card-GoTo-Image", - localPosition: { x: 0.0 , y: -1.75, z: -0.3 }, + localPosition: { x: 0.0, y: -1.75, z: -0.3 }, url: Script.resourcesPath() + "images/interstitialPage/button.png", alpha: 1, visible: isVisible, @@ -169,7 +170,7 @@ var loadingToTheSpotHoverID = Overlays.addOverlay("image3d", { name: "Loading-Destination-Card-GoTo-Image-Hover", - localPosition: { x: 0.0 , y: -1.75, z: -0.3 }, + localPosition: { x: 0.0, y: -1.75, z: -0.3 }, url: Script.resourcesPath() + "images/interstitialPage/button_hover.png", alpha: 1, visible: false, @@ -181,27 +182,29 @@ parentID: anchorOverlay }); - var loadingBarPlacard = Overlays.addOverlay("image3d", { - name: "Loading-Bar-Placard", - localPosition: { x: 0.0, y: -0.99, z: 0.3 }, - url: Script.resourcesPath() + "images/loadingBar_placard.png", - alpha: 1, - dimensions: { x: 4, y: 2.8}, - visible: isVisible, - emissive: true, - ignoreRayIntersection: false, - drawInFront: true, - grabbable: false, - localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 180.0, z: 0.0 }), - parentID: anchorOverlay - }); var loadingBarProgress = Overlays.addOverlay("image3d", { name: "Loading-Bar-Progress", - localPosition: { x: 0.0, y: -0.90, z: 0.0 }, + localPosition: { x: 0.0, y: -0.86, z: 0.0 }, url: Script.resourcesPath() + "images/loadingBar_progress.png", alpha: 1, - dimensions: {x: 3.8, y: 2.8}, + dimensions: { x: TOTAL_LOADING_PROGRESS, y: 0.3}, + visible: isVisible, + emissive: true, + ignoreRayIntersection: false, + drawInFront: true, + grabbable: false, + localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 180.0, z: 0.0 }), + parentID: anchorOverlay, + keepAspectRatio: false + }); + + var loadingBarPlacard = Overlays.addOverlay("image3d", { + name: "Loading-Bar-Placard", + localPosition: { x: 0.0, y: -0.99, z: 0.4 }, + url: Script.resourcesPath() + "images/loadingBar_placard.png", + alpha: 1, + dimensions: { x: 4, y: 2.8 }, visible: isVisible, emissive: true, ignoreRayIntersection: false, @@ -211,7 +214,7 @@ parentID: anchorOverlay }); - var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update + var TARGET_UPDATE_HZ = 30; var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var lastInterval = Date.now(); var currentDomain = "no domain"; @@ -244,15 +247,7 @@ } function resetValues() { - var properties = { - localPosition: { x: 1.85, y: -0.935, z: 0.0 }, - dimensions: { - x: 0.1, - y: 2.8 - } - }; - - Overlays.editOverlay(loadingBarProgress, properties); + updateProgressBar(0.0); } function startInterstitialPage() { @@ -262,11 +257,12 @@ target = 0; textureMemSizeStabilityCount = 0; textureMemSizeAtLastCheck = 0; - currentProgress = 0.1; + currentProgress = 0.0; connectionToDomainFailed = false; previousCameraMode = Camera.mode; Camera.mode = "first person"; - timer = Script.setTimeout(update, BASIC_TIMER_INTERVAL_MS); + updateProgressBar(0.0); + timer = Script.setTimeout(update, 2000); } } @@ -347,12 +343,12 @@ } } - var THE_PLACE = (HifiAbout.buildVersion === "dev") ? "hifi://TheSpot-dev": "hifi://TheSpot"; + var THE_PLACE = (HifiAbout.buildVersion === "dev") ? "hifi://TheSpot-dev" : "hifi://TheSpot"; function clickedOnOverlay(overlayID, event) { if (loadingToTheSpotHoverID === overlayID) { location.handleLookupString(THE_PLACE); - Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false}); - Overlays.editOverlay(loadingToTheSpotID, {visible: true}); + Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false }); + Overlays.editOverlay(loadingToTheSpotID, { visible: true }); } } @@ -361,8 +357,8 @@ return; } if (overlayID === loadingToTheSpotID) { - Overlays.editOverlay(loadingToTheSpotID, {visible: false}); - Overlays.editOverlay(loadingToTheSpotHoverID, {visible: true}); + Overlays.editOverlay(loadingToTheSpotID, { visible: false }); + Overlays.editOverlay(loadingToTheSpotHoverID, { visible: true }); } } @@ -371,14 +367,20 @@ return; } if (overlayID === loadingToTheSpotHoverID) { - Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false}); - Overlays.editOverlay(loadingToTheSpotID, {visible: true}); + Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false }); + Overlays.editOverlay(loadingToTheSpotID, { visible: true }); } } - var currentProgress = 0.1; + var currentProgress = 0.0; function updateOverlays(physicsEnabled) { + + if (isInterstitialOverlaysVisible !== !physicsEnabled && !physicsEnabled === true) { + // visible changed to true. + isInterstitialOverlaysVisible = !physicsEnabled; + } + var properties = { visible: !physicsEnabled }; @@ -393,7 +395,6 @@ }; var loadingBarProperties = { - dimensions: { x: 0.0, y: 2.8 }, visible: !physicsEnabled }; @@ -425,6 +426,11 @@ if (physicsEnabled) { Camera.mode = previousCameraMode; } + + if (isInterstitialOverlaysVisible !== !physicsEnabled && !physicsEnabled === false) { + // visible changed to false. + isInterstitialOverlaysVisible = !physicsEnabled; + } } function scaleInterstitialPage(sensorToWorldScale) { @@ -441,12 +447,40 @@ function sleep(milliseconds) { var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { - if ((new Date().getTime() - start) > milliseconds){ + if ((new Date().getTime() - start) > milliseconds) { break; } } } + function updateProgressBar(progress) { + var progressPercentage = progress / TOTAL_LOADING_PROGRESS; + var subImageWidth = progressPercentage * LOADING_IMAGE_WIDTH_PIXELS; + + var start = TOTAL_LOADING_PROGRESS / 2; + var end = 0; + var xLocalPosition = (progressPercentage * (end - start)) + start; + var properties = { + localPosition: { x: xLocalPosition, y: -0.93, z: 0.0 }, + dimensions: { + x: progress, + y: 0.3 + }, + localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 180.0, z: 0.0 }), + subImage: { + x: 0.0, + y: 0.0, + width: subImageWidth, + height: 128 + } + }; + + Overlays.editOverlay(loadingBarProgress, properties); + } + + var MAX_TEXTURE_STABILITY_COUNT = 30; + var INTERVAL_PROGRESS = 0.04; + var INTERVAL_PROGRESS_PHYSICS_ENABLED = 0.09; function update() { var renderStats = Render.getConfig("Stats"); var physicsEnabled = Window.isPhysicsEnabled(); @@ -460,7 +494,7 @@ target = progress; } - if (currentProgress >= (TOTAL_LOADING_PROGRESS * 0.4)) { + if (currentProgress >= ((TOTAL_LOADING_PROGRESS * 0.4) - TEXTURE_EPSILON)) { var textureResourceGPUMemSize = renderStats.textureResourceGPUMemSize; var texturePopulatedGPUMemSize = renderStats.textureResourcePopulatedGPUMemSize; @@ -472,10 +506,9 @@ textureMemSizeAtLastCheck = textureResourceGPUMemSize; - if (textureMemSizeStabilityCount >= 20) { + if (textureMemSizeStabilityCount >= MAX_TEXTURE_STABILITY_COUNT) { if (textureResourceGPUMemSize > 0) { - // print((texturePopulatedGPUMemSize / textureResourceGPUMemSize)); var gpuPercantage = (TOTAL_LOADING_PROGRESS * 0.6) * (texturePopulatedGPUMemSize / textureResourceGPUMemSize); var totalProgress = progress + gpuPercantage; if (totalProgress >= target) { @@ -489,21 +522,14 @@ target = TOTAL_LOADING_PROGRESS; } - currentProgress = lerp(currentProgress, target, 0.2); - var properties = { - localPosition: { x: (1.85 - (currentProgress / 2) - (-0.029 * (currentProgress / TOTAL_LOADING_PROGRESS))), y: -0.935, z: 0.0 }, - dimensions: { - x: currentProgress, - y: 2.8 - } - }; + currentProgress = lerp(currentProgress, target, (physicsEnabled ? INTERVAL_PROGRESS_PHYSICS_ENABLED : INTERVAL_PROGRESS)); - Overlays.editOverlay(loadingBarProgress, properties); + updateProgressBar(currentProgress); if (errorConnectingToDomain) { updateOverlays(errorConnectingToDomain); // setting hover id to invisible - Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false}); + Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false }); endAudio(); currentDomain = "no domain"; timer = null; @@ -519,7 +545,7 @@ } else if ((physicsEnabled && (currentProgress >= (TOTAL_LOADING_PROGRESS - EPSILON)))) { updateOverlays((physicsEnabled || connectionToDomainFailed)); // setting hover id to invisible - Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false}); + Overlays.editOverlay(loadingToTheSpotHoverID, { visible: false }); endAudio(); currentDomain = "no domain"; timer = null; @@ -527,25 +553,23 @@ } timer = Script.setTimeout(update, BASIC_TIMER_INTERVAL_MS); } - var whiteColor = {red: 255, green: 255, blue: 255}; - var greyColor = {red: 125, green: 125, blue: 125}; + var whiteColor = { red: 255, green: 255, blue: 255 }; + var greyColor = { red: 125, green: 125, blue: 125 }; + Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay); Overlays.hoverEnterOverlay.connect(onEnterOverlay); - Overlays.hoverLeaveOverlay.connect(onLeaveOverlay); - location.hostChanged.connect(domainChanged); - location.lookupResultsFinished.connect(function() { - Script.setTimeout(function() { - connectionToDomainFailed = !location.isConnected; - }, 1200); - }); Window.redirectErrorStateChanged.connect(toggleInterstitialPage); MyAvatar.sensorToWorldScaleChanged.connect(scaleInterstitialPage); MyAvatar.sessionUUIDChanged.connect(function() { var avatarSessionUUID = MyAvatar.sessionUUID; - Overlays.editOverlay(loadingSphereID, { parentID: avatarSessionUUID }); + Overlays.editOverlay(loadingSphereID, { + position: Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0.0, y: -1.0, z: 0.0}), Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.95, z: 0})), + orientation: Quat.multiply(Quat.fromVec3Degrees({x: 0, y: 180, z: 0}), MyAvatar.orientation), + parentID: avatarSessionUUID + }); }); var toggle = true; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c201a251d0..8e5b41deda 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -129,9 +129,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { url: modelURL, // for overlay grabbable: true, // for overlay loadPriority: 10.0, // for overlay - userData: JSON.stringify({ - "grabbableKey": {"grabbable": true} - }), + grab: { grabbable: true }, dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, parentID: MyAvatar.SELF_ID, visible: visible, diff --git a/scripts/system/libraries/accountUtils.js b/scripts/system/libraries/accountUtils.js index 9f0d690a5f..42204340c7 100644 --- a/scripts/system/libraries/accountUtils.js +++ b/scripts/system/libraries/accountUtils.js @@ -6,11 +6,5 @@ // openLoginWindow = function openLoginWindow() { - if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) - || (!HMD.active && Settings.getValue("desktopTabletBecomesToolbar", true))) { - Menu.triggerOption("Login/Sign Up"); - } else { - tablet.loadQMLOnTop("dialogs/TabletLoginDialog.qml"); - HMD.openTablet(); - } + Menu.triggerOption("Login/Sign Up"); }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index c34fd76802..e9d5255d28 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -48,6 +48,7 @@ BUMPER_ON_VALUE:true, getEntityParents:true, findHandChildEntities:true, + findFarGrabJointChildEntities:true, makeLaserParams:true, TEAR_AWAY_DISTANCE:true, TEAR_AWAY_COUNT:true, @@ -127,13 +128,25 @@ DISPATCHER_PROPERTIES = [ "parentJointIndex", "density", "dimensions", - "userData", "type", "href", "cloneable", "cloneDynamic", "localPosition", - "localRotation" + "localRotation", + "grab.grabbable", + "grab.grabKinematic", + "grab.grabFollowsController", + "grab.triggerable", + "grab.equippable", + "grab.equippableLeftPosition", + "grab.equippableLeftRotation", + "grab.equippableRightPosition", + "grab.equippableRightRotation", + "grab.equippableIndicatorURL", + "grab.equippableIndicatorScale", + "grab.equippableIndicatorOffset", + "userData" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step @@ -215,25 +228,56 @@ getGrabbableData = function (ggdProps) { } catch (err) { userDataParsed = {}; } + if (userDataParsed.grabbableKey) { grabbableData = userDataParsed.grabbableKey; + } else { + grabbableData = ggdProps.grab; } + + // extract grab-related properties, provide defaults if any are missing if (!grabbableData.hasOwnProperty("grabbable")) { grabbableData.grabbable = true; } - if (!grabbableData.hasOwnProperty("ignoreIK")) { - grabbableData.ignoreIK = true; + // kinematic has been renamed to grabKinematic + if (!grabbableData.hasOwnProperty("grabKinematic") && + !grabbableData.hasOwnProperty("kinematic")) { + grabbableData.grabKinematic = true; } - if (!grabbableData.hasOwnProperty("kinematic")) { - grabbableData.kinematic = true; + if (!grabbableData.hasOwnProperty("grabKinematic")) { + grabbableData.grabKinematic = grabbableData.kinematic; } - if (!grabbableData.hasOwnProperty("wantsTrigger")) { - grabbableData.wantsTrigger = false; + // ignoreIK has been renamed to grabFollowsController + if (!grabbableData.hasOwnProperty("grabFollowsController") && + !grabbableData.hasOwnProperty("ignoreIK")) { + grabbableData.grabFollowsController = true; } - if (!grabbableData.hasOwnProperty("triggerable")) { + if (!grabbableData.hasOwnProperty("grabFollowsController")) { + grabbableData.grabFollowsController = grabbableData.ignoreIK; + } + // wantsTrigger has been renamed to triggerable + if (!grabbableData.hasOwnProperty("triggerable") && + !grabbableData.hasOwnProperty("wantsTrigger")) { grabbableData.triggerable = false; } - + if (!grabbableData.hasOwnProperty("triggerable")) { + grabbableData.triggerable = grabbableData.wantsTrigger; + } + if (!grabbableData.hasOwnProperty("equippable")) { + grabbableData.equippable = false; + } + if (!grabbableData.hasOwnProperty("equippableLeftPosition")) { + grabbableData.equippableLeftPosition = { x: 0, y: 0, z: 0 }; + } + if (!grabbableData.hasOwnProperty("equippableLeftRotation")) { + grabbableData.equippableLeftPosition = { x: 0, y: 0, z: 0, w: 1 }; + } + if (!grabbableData.hasOwnProperty("equippableRightPosition")) { + grabbableData.equippableRightPosition = { x: 0, y: 0, z: 0 }; + } + if (!grabbableData.hasOwnProperty("equippableRightRotation")) { + grabbableData.equippableRightPosition = { x: 0, y: 0, z: 0, w: 1 }; + } return grabbableData; }; @@ -417,6 +461,18 @@ findHandChildEntities = function(hand) { }); }; +findFarGrabJointChildEntities = function(hand) { + // find children of avatar's far-grab joint + var farGrabJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "_FARGRAB_RIGHTHAND" : "_FARGRAB_LEFTHAND"); + var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, farGrabJointIndex); + children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, farGrabJointIndex)); + + return children.filter(function (childID) { + var childType = Entities.getNestableType(childID); + return childType == "entity"; + }); +}; + distanceBetweenEntityLocalPositionAndBoundingBox = function(entityProps, jointGrabOffset) { var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; var rotInv = Quat.inverse(entityProps.localRotation); diff --git a/scripts/system/libraries/controllers.js b/scripts/system/libraries/controllers.js index cc20c196aa..be7d22e073 100644 --- a/scripts/system/libraries/controllers.js +++ b/scripts/system/libraries/controllers.js @@ -5,20 +5,25 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global MyAvatar, Vec3, Controller, Quat */ +/* global MyAvatar, Vec3, HMD, Controller, Camera, Quat, Settings, + getGrabPointSphereOffset:true, + setGrabCommunications:true, + getGrabCommunications:true, + getControllerWorldLocation:true + */ var GRAB_COMMUNICATIONS_SETTING = "io.highfidelity.isFarGrabbing"; setGrabCommunications = function setFarGrabCommunications(on) { Settings.setValue(GRAB_COMMUNICATIONS_SETTING, on ? "on" : ""); -} +}; getGrabCommunications = function getFarGrabCommunications() { return !!Settings.getValue(GRAB_COMMUNICATIONS_SETTING, ""); -} +}; // this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378 -var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral getGrabPointSphereOffset = function(handController, ignoreSensorToWorldScale) { + var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral var offset = GRAB_POINT_SPHERE_OFFSET; if (handController === Controller.Standard.LeftHand) { offset = { @@ -39,7 +44,7 @@ getControllerWorldLocation = function (handController, doOffset) { var orientation; var position; var valid = false; - + if (handController >= 0) { var pose = Controller.getPoseValue(handController); valid = pose.valid; diff --git a/scripts/system/libraries/entityCameraTool.js b/scripts/system/libraries/entityCameraTool.js index 73e73d67a6..4410f19a5e 100644 --- a/scripts/system/libraries/entityCameraTool.js +++ b/scripts/system/libraries/entityCameraTool.js @@ -98,16 +98,18 @@ CameraManager = function() { } function getActionForKeyEvent(event) { - var action = keyToActionMapping[event.key]; - if (action !== undefined) { - if (event.isShifted) { - if (action === "orbitForward") { - action = "orbitUp"; - } else if (action === "orbitBackward") { - action = "orbitDown"; + if (!event.isControl) { + var action = keyToActionMapping[event.key]; + if (action !== undefined) { + if (event.isShifted) { + if (action === "orbitForward") { + action = "orbitUp"; + } else if (action === "orbitBackward") { + action = "orbitDown"; + } } + return action; } - return action; } return null; } diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 30e952723f..c47e8045d6 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -15,7 +15,7 @@ var PROFILING_ENABLED = false; var profileIndent = ''; const PROFILE_NOOP = function(_name, fn, args) { fn.apply(this, args); -} ; +}; PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) { console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin"); var previousIndent = profileIndent; @@ -98,7 +98,11 @@ EntityListTool = function(shouldUseEditTabletApp) { that.setVisible(!visible); }; - selectionManager.addEventListener(function() { + selectionManager.addEventListener(function(isSelectionUpdate, caller) { + if (caller === that) { + // ignore events that we emitted from the entity list itself + return; + } var selectedIDs = []; for (var i = 0; i < selectionManager.selections.length; i++) { @@ -111,6 +115,13 @@ EntityListTool = function(shouldUseEditTabletApp) { }); }); + that.setSpaceMode = function(spaceMode) { + emitJSONScriptEvent({ + type: 'setSpaceMode', + spaceMode: spaceMode + }); + }; + that.clearEntityList = function() { emitJSONScriptEvent({ type: 'clearEntityList' @@ -196,6 +207,7 @@ EntityListTool = function(shouldUseEditTabletApp) { type: "update", entities: entities, selectedIDs: selectedIDs, + spaceMode: SelectionDisplay.getSpaceMode(), }); }); }; @@ -224,7 +236,7 @@ EntityListTool = function(shouldUseEditTabletApp) { for (var i = 0; i < ids.length; i++) { entityIDs.push(ids[i]); } - selectionManager.setSelections(entityIDs); + selectionManager.setSelections(entityIDs, that); if (data.focus) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, @@ -245,7 +257,7 @@ EntityListTool = function(shouldUseEditTabletApp) { Window.saveAsync("Select Where to Save", "", "*.json"); } } else if (data.type === "pal") { - var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. + var sessionIds = {}; // Collect the sessionsIds of all selected entities, w/o duplicates. selectionManager.selections.forEach(function (id) { var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy; if (lastEditedBy) { @@ -271,6 +283,21 @@ EntityListTool = function(shouldUseEditTabletApp) { filterInView = data.filterInView === true; } else if (data.type === "radius") { searchRadius = data.radius; + } else if (data.type === "cut") { + SelectionManager.cutSelectedEntities(); + } else if (data.type === "copy") { + SelectionManager.copySelectedEntities(); + } else if (data.type === "paste") { + SelectionManager.pasteEntities(); + } else if (data.type === "duplicate") { + SelectionManager.duplicateSelection(); + that.sendUpdate(); + } else if (data.type === "rename") { + Entities.editEntity(data.entityID, {name: data.name}); + // make sure that the name also gets updated in the properties window + SelectionManager._update(); + } else if (data.type === "toggleSpaceMode") { + SelectionDisplay.toggleSpaceMode(); } }; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index a13c645b81..168c81db1f 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -26,6 +26,11 @@ Script.include([ "./utils.js" ]); + +function deepCopy(v) { + return JSON.parse(JSON.stringify(v)); +} + SelectionManager = (function() { var that = {}; @@ -35,7 +40,7 @@ SelectionManager = (function() { Messages.messageReceived.connect(handleEntitySelectionToolUpdates); } - // FUNCTION: HANDLE ENTITY SELECTION TOOL UDPATES + // FUNCTION: HANDLE ENTITY SELECTION TOOL UPDATES function handleEntitySelectionToolUpdates(channel, message, sender) { if (channel !== 'entityToolUpdates') { return; @@ -58,7 +63,7 @@ SelectionManager = (function() { if (wantDebug) { print("setting selection to " + messageParsed.entityID); } - that.setSelections([messageParsed.entityID]); + that.setSelections([messageParsed.entityID], that); } } else if (messageParsed.method === "clearSelection") { if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { @@ -131,7 +136,7 @@ SelectionManager = (function() { return that.selections.length > 0; }; - that.setSelections = function(entityIDs) { + that.setSelections = function(entityIDs, caller) { that.selections = []; for (var i = 0; i < entityIDs.length; i++) { var entityID = entityIDs[i]; @@ -139,10 +144,10 @@ SelectionManager = (function() { Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } - that._update(true); + that._update(true, caller); }; - that.addEntity = function(entityID, toggleSelection) { + that.addEntity = function(entityID, toggleSelection, caller) { if (entityID) { var idx = -1; for (var i = 0; i < that.selections.length; i++) { @@ -160,7 +165,7 @@ SelectionManager = (function() { } } - that._update(true); + that._update(true, caller); }; function removeEntityByID(entityID) { @@ -171,21 +176,21 @@ SelectionManager = (function() { } } - that.removeEntity = function (entityID) { + that.removeEntity = function (entityID, caller) { removeEntityByID(entityID); - that._update(true); + that._update(true, caller); }; - that.removeEntities = function(entityIDs) { + that.removeEntities = function(entityIDs, caller) { for (var i = 0, length = entityIDs.length; i < length; i++) { removeEntityByID(entityIDs[i]); } - that._update(true); + that._update(true, caller); }; - that.clearSelections = function() { + that.clearSelections = function(caller) { that.selections = []; - that._update(true); + that._update(true, caller); }; that.addChildrenEntities = function(parentEntityID, entityList) { @@ -199,9 +204,11 @@ SelectionManager = (function() { } }; - // Return true if the given entity with `properties` is being grabbed by an avatar. + // Determine if an entity is being grabbed. // This is mostly a heuristic - there is no perfect way to know if an entity is being // grabbed. + // + // @return {boolean} true if the given entity with `properties` is being grabbed by an avatar function nonDynamicEntityIsBeingGrabbedByAvatar(properties) { if (properties.dynamic || Uuid.isNull(properties.parentID)) { return false; @@ -215,7 +222,9 @@ SelectionManager = (function() { var grabJointNames = [ 'RightHand', 'LeftHand', '_CONTROLLER_RIGHTHAND', '_CONTROLLER_LEFTHAND', - '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND', '_CAMERA_RELATIVE_CONTROLLER_LEFTHAND']; + '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND', '_CAMERA_RELATIVE_CONTROLLER_LEFTHAND', + '_FARGRAB_RIGHTHAND', '_FARGRAB_LEFTHAND', '_FARGRAB_MOUSE' + ]; for (var i = 0; i < grabJointNames.length; ++i) { if (avatar.getJointIndex(grabJointNames[i]) === properties.parentJointIndex) { @@ -226,6 +235,12 @@ SelectionManager = (function() { return false; } + var entityClipboard = { + entities: {}, // Map of id -> properties for copied entities + position: { x: 0, y: 0, z: 0 }, + dimensions: { x: 0, y: 0, z: 0 }, + }; + that.duplicateSelection = function() { var entitiesToDuplicate = []; var duplicatedEntityIDs = []; @@ -266,7 +281,7 @@ SelectionManager = (function() { var actionArguments = Entities.getActionArguments(properties.id, actionID); if (actionArguments) { var type = actionArguments.type; - if (type == 'hold' || type == 'far-grab') { + if (type === 'hold' || type === 'far-grab') { continue; } delete actionArguments.ttl; @@ -303,7 +318,166 @@ SelectionManager = (function() { return duplicatedEntityIDs; }; - that._update = function(selectionUpdated) { + // Create the entities in entityProperties, maintaining parent-child relationships. + // @param entityPropertites {array} - Array of entity property objects + that.createEntities = function(entityProperties) { + var entitiesToCreate = []; + var createdEntityIDs = []; + var createdChildrenWithOldParents = []; + var originalEntityToNewEntityID = []; + + that.saveProperties(); + + for (var i = 0; i < entityProperties.length; ++i) { + var properties = entityProperties[i]; + if (properties.parentID in originalEntityToNewEntityID) { + properties.parentID = originalEntityToNewEntityID[properties.parentID]; + } else { + delete properties.parentID; + } + + delete properties.actionData; + var newEntityID = Entities.addEntity(properties); + + if (newEntityID) { + createdEntityIDs.push({ + entityID: newEntityID, + properties: properties + }); + if (properties.parentID !== Uuid.NULL) { + createdChildrenWithOldParents[newEntityID] = properties.parentID; + } + originalEntityToNewEntityID[properties.id] = newEntityID; + properties.id = newEntityID; + } + } + + return createdEntityIDs; + }; + + that.cutSelectedEntities = function() { + that.copySelectedEntities(); + deleteSelectedEntities(); + }; + + that.copySelectedEntities = function() { + var entityProperties = Entities.getMultipleEntityProperties(that.selections); + var entities = {}; + entityProperties.forEach(function(props) { + entities[props.id] = props; + }); + + function appendChildren(entityID, entities) { + var childrenIDs = Entities.getChildrenIDs(entityID); + for (var i = 0; i < childrenIDs.length; ++i) { + var id = childrenIDs[i]; + if (!(id in entities)) { + entities[id] = Entities.getEntityProperties(id); + appendChildren(id, entities); + } + } + } + + var len = entityProperties.length; + for (var i = 0; i < len; ++i) { + appendChildren(entityProperties[i].id, entities); + } + + for (var id in entities) { + var parentID = entities[id].parentID; + entities[id].root = !(parentID in entities); + } + + entityClipboard.entities = []; + + var ids = Object.keys(entities); + while (ids.length > 0) { + // Go through all remaining entities. + // If an entity does not have a parent left, move it into the list + for (var i = 0; i < ids.length; ++i) { + var id = ids[i]; + var parentID = entities[id].parentID; + if (parentID in entities) { + continue; + } + entityClipboard.entities.push(entities[id]); + delete entities[id]; + } + ids = Object.keys(entities); + } + + // Calculate size + if (entityClipboard.entities.length === 0) { + entityClipboard.dimensions = { x: 0, y: 0, z: 0 }; + entityClipboard.position = { x: 0, y: 0, z: 0 }; + } else { + var properties = entityClipboard.entities; + var brn = properties[0].boundingBox.brn; + var tfl = properties[0].boundingBox.tfl; + for (var i = 1; i < properties.length; i++) { + var bb = properties[i].boundingBox; + brn.x = Math.min(bb.brn.x, brn.x); + brn.y = Math.min(bb.brn.y, brn.y); + brn.z = Math.min(bb.brn.z, brn.z); + tfl.x = Math.max(bb.tfl.x, tfl.x); + tfl.y = Math.max(bb.tfl.y, tfl.y); + tfl.z = Math.max(bb.tfl.z, tfl.z); + } + entityClipboard.dimensions = { + x: tfl.x - brn.x, + y: tfl.y - brn.y, + z: tfl.z - brn.z + }; + entityClipboard.position = { + x: brn.x + entityClipboard.dimensions.x / 2, + y: brn.y + entityClipboard.dimensions.y / 2, + z: brn.z + entityClipboard.dimensions.z / 2 + }; + } + }; + + that.pasteEntities = function() { + var dimensions = entityClipboard.dimensions; + var maxDimension = Math.max(dimensions.x, dimensions.y, dimensions.z); + var pastePosition = getPositionToCreateEntity(maxDimension); + var deltaPosition = Vec3.subtract(pastePosition, entityClipboard.position); + + var copiedProperties = []; + var ids = []; + entityClipboard.entities.forEach(function(originalProperties) { + var properties = deepCopy(originalProperties); + if (properties.root) { + properties.position = Vec3.sum(properties.position, deltaPosition); + delete properties.localPosition; + } else { + delete properties.position; + } + copiedProperties.push(properties); + }); + + var currentSelections = deepCopy(SelectionManager.selections); + + function redo(copiedProperties) { + var created = that.createEntities(copiedProperties); + var ids = []; + for (var i = 0; i < created.length; ++i) { + ids.push(created[i].entityID); + } + SelectionManager.setSelections(ids); + } + + function undo(copiedProperties) { + for (var i = 0; i < copiedProperties.length; ++i) { + Entities.deleteEntity(copiedProperties[i].id); + } + SelectionManager.setSelections(currentSelections); + } + + redo(copiedProperties); + undoHistory.pushCommand(undo, copiedProperties, redo, copiedProperties); + }; + + that._update = function(selectionUpdated, caller) { var properties = null; if (that.selections.length === 0) { that.localDimensions = null; @@ -326,7 +500,7 @@ SelectionManager = (function() { that.entityType = properties.type; if (selectionUpdated) { - SelectionDisplay.setSpaceMode(SPACE_LOCAL); + SelectionDisplay.useDesiredSpaceMode(); } } else { properties = Entities.getEntityProperties(that.selections[0], ['type', 'boundingBox']); @@ -363,12 +537,12 @@ SelectionManager = (function() { }; // For 1+ selections we can only modify selections in world space - SelectionDisplay.setSpaceMode(SPACE_WORLD); + SelectionDisplay.setSpaceMode(SPACE_WORLD, false); } for (var j = 0; j < listeners.length; j++) { try { - listeners[j](selectionUpdated === true); + listeners[j](selectionUpdated === true, caller); } catch (e) { print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); } @@ -459,24 +633,26 @@ SelectionDisplay = (function() { ALL: 3 }; - const SCALE_DIRECTION = { - LBN: 0, - RBN: 1, - LBF: 2, - RBF: 3, - LTN: 4, - RTN: 5, - LTF: 6, - RTF: 7 - }; - const ROTATE_DIRECTION = { PITCH: 0, YAW: 1, ROLL: 2 }; + /** + * The current space mode, this could have been a forced space mode since we do not support multi selection while in + * local space mode. + * @type {string} - should only be set to SPACE_LOCAL or SPACE_WORLD + */ var spaceMode = SPACE_LOCAL; + + /** + * The desired space mode, this is the user set space mode, which should be respected whenever it is possible. In the case + * of multi entity selection this space mode may differ from the actual spaceMode. + * @type {string} - should only be set to SPACE_LOCAL or SPACE_WORLD + */ + var desiredSpaceMode = SPACE_LOCAL; + var overlayNames = []; var lastControllerPoses = [ getControllerWorldLocation(Controller.Standard.LeftHand, true), @@ -811,7 +987,7 @@ SelectionDisplay = (function() { that.pressedHand = NO_HAND; that.triggered = function() { return that.triggeredHand !== NO_HAND; - } + }; function pointingAtDesktopWindowOrTablet(hand) { var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtDesktopWindowRight) || @@ -858,7 +1034,7 @@ SelectionDisplay = (function() { that.disableTriggerMapping = function() { that.triggerClickMapping.disable(); that.triggerPressMapping.disable(); - } + }; Script.scriptEnding.connect(that.disableTriggerMapping); // FUNCTION DEF(s): Intersection Check Helpers @@ -1060,7 +1236,7 @@ SelectionDisplay = (function() { if (wantDebug) { print(" Trigger SelectionManager::update"); } - SelectionManager._update(); + SelectionManager._update(false, that); if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); @@ -1125,7 +1301,7 @@ SelectionDisplay = (function() { lastMouseEvent.isControl = event.isControl; lastMouseEvent.isAlt = event.isAlt; activeTool.onMove(lastMouseEvent); - SelectionManager._update(); + SelectionManager._update(false, this); } }; @@ -1141,7 +1317,7 @@ SelectionDisplay = (function() { lastMouseEvent.isControl = event.isControl; lastMouseEvent.isAlt = event.isAlt; activeTool.onMove(lastMouseEvent); - SelectionManager._update(); + SelectionManager._update(false, this); } }; @@ -1166,7 +1342,7 @@ SelectionDisplay = (function() { } }; - function controllerComputePickRay(hand) { + function controllerComputePickRay() { var hand = that.triggered() ? that.triggeredHand : that.pressedHand; var controllerPose = getControllerWorldLocation(hand, true); if (controllerPose.valid) { @@ -1226,8 +1402,21 @@ SelectionDisplay = (function() { that.updateHandles(); }; + + /** + * This callback is used for spaceMode changes. + * @callback spaceModeChangedCallback + * @param {string} spaceMode + */ + + /** + * set this property with a callback to keep track of spaceMode changes. + * @type {spaceModeChangedCallback} + */ + that.onSpaceModeChange = null; + // FUNCTION: SET SPACE MODE - that.setSpaceMode = function(newSpaceMode) { + that.setSpaceMode = function(newSpaceMode, isDesiredChange) { var wantDebug = false; if (wantDebug) { print("======> SetSpaceMode called. ========"); @@ -1237,7 +1426,15 @@ SelectionDisplay = (function() { if (wantDebug) { print(" Updating SpaceMode From: " + spaceMode + " To: " + newSpaceMode); } + if (isDesiredChange) { + desiredSpaceMode = newSpaceMode; + } spaceMode = newSpaceMode; + + if (that.onSpaceModeChange !== null) { + that.onSpaceModeChange(newSpaceMode); + } + that.updateHandles(); } else if (wantDebug) { print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + @@ -1263,14 +1460,36 @@ SelectionDisplay = (function() { if (wantDebug) { print("PreToggle: " + spaceMode); } - spaceMode = (spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL; - that.updateHandles(); + that.setSpaceMode((spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL, true); if (wantDebug) { print("PostToggle: " + spaceMode); print("======== ToggleSpaceMode called. <========="); } }; + /** + * Switches the display mode back to the set desired display mode + */ + that.useDesiredSpaceMode = function() { + var wantDebug = false; + if (wantDebug) { + print("========> UseDesiredSpaceMode called. ========="); + } + that.setSpaceMode(desiredSpaceMode, false); + if (wantDebug) { + print("PostToggle: " + spaceMode); + print("======== UseDesiredSpaceMode called. <========="); + } + }; + + /** + * Get the currently set SpaceMode + * @returns {string} spaceMode + */ + that.getSpaceMode = function() { + return spaceMode; + }; + function addHandleTool(overlay, tool) { handleTools[overlay] = tool; return tool; @@ -1613,7 +1832,7 @@ SelectionDisplay = (function() { !isActiveTool(handleRotateYawRing) && !isActiveTool(handleRotateRollRing))); - // keep duplicator always hidden for now since you can hold Alt to duplciate while + // keep duplicator always hidden for now since you can hold Alt to duplicate while // translating an entity - we may bring duplicator back for HMD only later // that.setHandleDuplicatorVisible(!activeTool || isActiveTool(handleDuplicator)); @@ -2005,7 +2224,7 @@ SelectionDisplay = (function() { } } - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2127,7 +2346,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2314,7 +2533,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2425,7 +2644,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } diff --git a/scripts/system/libraries/globals.js b/scripts/system/libraries/globals.js index 0c382314c9..b51a905e0a 100644 --- a/scripts/system/libraries/globals.js +++ b/scripts/system/libraries/globals.js @@ -9,3 +9,5 @@ // HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +isInterstitialOverlaysVisible = false; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index cca535a064..74c1e4baf0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -10,7 +10,7 @@ /* global Tablet, Script, HMD, UserActivityLogger, Entities, Account, Wallet, ContextOverlay, Settings, Camera, Vec3, Quat, MyAvatar, Clipboard, Menu, Grid, Uuid, GlobalServices, openLoginWindow, getConnectionData, Overlays, SoundCache, - DesktopPreviewProvider */ + DesktopPreviewProvider, ResourceRequestObserver */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ var selectionDisplay = null; // for gridTool.js to ignore @@ -23,7 +23,7 @@ Script.include("/~/system/libraries/connectionUtils.js"); var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; +var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; // HRS FIXME "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); @@ -49,6 +49,42 @@ var NO_BUTTON = 0; // QMessageBox::NoButton var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; + +var resourceRequestEvents = []; +function signalResourceRequestEvent(data) { + // Once we can tie resource request events to specific resources, + // we will have to update the "0" in here. + var resourceData = "from: " + data.extra + ": " + data.url.toString().replace("__NONE__,", ""); + + if (resourceObjectsInTest[0].resourceDataArray.indexOf(resourceData) === -1) { + resourceObjectsInTest[0].resourceDataArray.push(resourceData); + + resourceObjectsInTest[0].resourceAccessEventText += "[" + data.date.toISOString() + "] " + + resourceData + "\n"; + + ui.tablet.sendToQml({ + method: "resourceRequestEvent", + data: data, + resourceAccessEventText: resourceObjectsInTest[0].resourceAccessEventText + }); + } +} + +function onResourceRequestEvent(data) { + // Once we can tie resource request events to specific resources, + // we will have to update the "0" in here. + if (resourceObjectsInTest[0] && resourceObjectsInTest[0].currentlyRecordingResources) { + var resourceRequestEvent = { + "date": new Date(), + "url": data.url, + "callerId": data.callerId, + "extra": data.extra + }; + resourceRequestEvents.push(resourceRequestEvent); + signalResourceRequestEvent(resourceRequestEvent); + } +} + function onMessageBoxClosed(id, button) { if (id === messageBox && button === CANCEL_BUTTON) { isDownloadBeingCancelled = true; @@ -79,10 +115,10 @@ function setTabletVisibleInSecondaryCamera(visibleInSecondaryCam) { tabletShouldBeVisibleInSecondaryCamera = Overlays.getProperty(HMD.tabletID, "isVisibleInSecondaryCamera"); } - Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); } function openWallet() { @@ -101,7 +137,7 @@ function setupWallet(referrer) { } function onMarketplaceOpen(referrer) { - var cta = referrer, match; + var match; if (Account.loggedIn && walletNeedsSetup()) { if (referrer === MARKETPLACE_URL_INITIAL) { setupWallet('marketplace cta'); @@ -182,7 +218,7 @@ function onUsernameChanged() { } function walletNeedsSetup() { - return Wallet.walletStatus === 1; + return WalletScriptingInterface.walletStatus === 1; } function sendCommerceSettings() { @@ -194,289 +230,11 @@ function sendCommerceSettings() { userIsLoggedIn: Account.loggedIn, walletNeedsSetup: walletNeedsSetup(), metaverseServerURL: Account.metaverseServerURL, - messagesWaiting: shouldShowDot + limitedCommerce: WalletScriptingInterface.limitedCommerce } }); } -// BEGIN AVATAR SELECTOR LOGIC -var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; -var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; -var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 }; - -var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. - -function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with. - overlays[key] = this; - this.key = key; - this.selected = false; - this.hovering = false; - this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... -} -// Instance methods: -ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay - Overlays.deleteOverlay(this.activeOverlay); - delete overlays[this.key]; -}; - -ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay - Overlays.editOverlay(this.activeOverlay, properties); -}; - -function color(selected, hovering) { - var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; - function scale(component) { - return component; - } - return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; -} -// so we don't have to traverse the overlays to get the last one -var lastHoveringId = 0; -ExtendedOverlay.prototype.hover = function (hovering) { - this.hovering = hovering; - if (this.key === lastHoveringId) { - if (hovering) { - return; - } - lastHoveringId = 0; - } - this.editOverlay({ color: color(this.selected, hovering) }); - if (hovering) { - // un-hover the last hovering overlay - if (lastHoveringId && lastHoveringId !== this.key) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } - lastHoveringId = this.key; - } -}; -ExtendedOverlay.prototype.select = function (selected) { - if (this.selected === selected) { - return; - } - - this.editOverlay({ color: color(selected, this.hovering) }); - this.selected = selected; -}; -// Class methods: -var selectedId = false; -ExtendedOverlay.isSelected = function (id) { - return selectedId === id; -}; -ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier - return overlays[key]; -}; -ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. - var key; - for (key in overlays) { - if (iterator(ExtendedOverlay.get(key))) { - return; - } - } -}; -ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) - if (lastHoveringId) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } -}; - -// hit(overlay) on the one overlay intersected by pickRay, if any. -// noHit() if no ExtendedOverlay was intersected (helps with hover) -ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { - var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. - if (!pickedOverlay.intersects) { - if (noHit) { - return noHit(); - } - return; - } - ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. - if ((overlay.activeOverlay) === pickedOverlay.overlayID) { - hit(overlay); - return true; - } - }); -}; - -function addAvatarNode(id) { - return new ExtendedOverlay(id, "sphere", { - drawInFront: true, - solid: true, - alpha: 0.8, - color: color(false, false), - ignoreRayIntersection: false - }); -} - -var pingPong = true; -function updateOverlays() { - var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id) { - return; // don't update ourself, or avatars we're not interested in - } - var avatar = AvatarList.getAvatar(id); - if (!avatar) { - return; // will be deleted below if there had been an overlay. - } - var overlay = ExtendedOverlay.get(id); - if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - overlay = addAvatarNode(id); - } - var target = avatar.position; - var distance = Vec3.distance(target, eye); - var offset = 0.2; - var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) - var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } - - // move a bit in front, towards the camera - target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); - - // now bump it up a bit - target.y = target.y + offset; - - overlay.ping = pingPong; - overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering), - position: target, - dimensions: 0.032 * distance - }); - }); - pingPong = !pingPong; - ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) - if (overlay.ping === pingPong) { - overlay.deleteOverlay(); - } - }); -} -function removeOverlays() { - selectedId = false; - lastHoveringId = 0; - ExtendedOverlay.some(function (overlay) { - overlay.deleteOverlay(); - }); -} - -// -// Clicks. -// -function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { - if (selectedId === id) { - var message = { - method: 'updateSelectedRecipientUsername', - userName: username === "" ? "unknown username" : username - }; - ui.tablet.sendToQml(message); - } -} -function handleClick(pickRay) { - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - var nextSelectedStatus = !overlay.selected; - var avatarId = overlay.key; - selectedId = nextSelectedStatus ? avatarId : false; - if (nextSelectedStatus) { - Users.requestUsernameFromID(avatarId); - } - var message = { - method: 'selectRecipient', - id: avatarId, - isSelected: nextSelectedStatus, - displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', - userName: '' - }; - ui.tablet.sendToQml(message); - - ExtendedOverlay.some(function (overlay) { - var id = overlay.key; - var selected = ExtendedOverlay.isSelected(id); - overlay.select(selected); - }); - - return true; - }); -} -function handleMouseEvent(mousePressEvent) { // handleClick if we get one. - if (!mousePressEvent.isLeftButton) { - return; - } - handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); -} -function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - overlay.hover(true); - }, function () { - ExtendedOverlay.unHover(); - }); -} - -// handy global to keep track of which hand is the mouse (if any) -var currentHandPressed = 0; -var TRIGGER_CLICK_THRESHOLD = 0.85; -var TRIGGER_PRESS_THRESHOLD = 0.05; - -function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position - var pickRay; - if (HMD.active) { - if (currentHandPressed !== 0) { - pickRay = controllerComputePickRay(currentHandPressed); - } else { - // nothing should hover, so - ExtendedOverlay.unHover(); - return; - } - } else { - pickRay = Camera.computePickRay(event.x, event.y); - } - handleMouseMove(pickRay); -} -function handleTriggerPressed(hand, value) { - // The idea is if you press one trigger, it is the one - // we will consider the mouse. Even if the other is pressed, - // we ignore it until this one is no longer pressed. - var isPressed = value > TRIGGER_PRESS_THRESHOLD; - if (currentHandPressed === 0) { - currentHandPressed = isPressed ? hand : 0; - return; - } - if (currentHandPressed === hand) { - currentHandPressed = isPressed ? hand : 0; - return; - } - // otherwise, the other hand is still triggered - // so do nothing. -} - -// We get mouseMoveEvents from the handControllers, via handControllerPointer. -// But we don't get mousePressEvents. -var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); -var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); -function controllerComputePickRay(hand) { - var controllerPose = getControllerWorldLocation(hand, true); - if (controllerPose.valid) { - return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; - } -} -function makeClickHandler(hand) { - return function (clicked) { - if (clicked > TRIGGER_CLICK_THRESHOLD) { - var pickRay = controllerComputePickRay(hand); - handleClick(pickRay); - } - }; -} -function makePressHandler(hand) { - return function (value) { - handleTriggerPressed(hand, value); - }; -} -triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); -triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); -triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); -triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); -// END AVATAR SELECTOR LOGIC - var grid = new Grid(); function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original @@ -522,13 +280,19 @@ function getPositionToCreateEntity(extra) { return position; } -function rezEntity(itemHref, itemType) { +function defaultFor(arg, val) { + return typeof arg !== 'undefined' ? arg : val; +} + +var CERT_ID_URLPARAM_LENGTH = 15; // length of "certificate_id=" +function rezEntity(itemHref, itemType, marketplaceItemTesterId) { var isWearable = itemType === "wearable"; - var success = Clipboard.importEntities(itemHref); + var success = Clipboard.importEntities(itemHref, true, marketplaceItemTesterId); var wearableLocalPosition = null; var wearableLocalRotation = null; var wearableLocalDimensions = null; var wearableDimensions = null; + marketplaceItemTesterId = defaultFor(marketplaceItemTesterId, -1); if (itemType === "contentSet") { console.log("Item is a content set; codepath shouldn't go here."); @@ -543,7 +307,7 @@ function rezEntity(itemHref, itemType) { } var certPos = itemHref.search("certificate_id="); // TODO how do I parse a URL from here? if (certPos >= 0) { - certPos += 15; // length of "certificate_id=" + certPos += CERT_ID_URLPARAM_LENGTH; var certURLEncoded = itemHref.substring(certPos); var certB64Encoded = decodeURIComponent(certURLEncoded); for (var key in wearableTransforms) { @@ -552,7 +316,7 @@ function rezEntity(itemHref, itemType) { if (certificateTransforms) { for (var certID in certificateTransforms) { if (certificateTransforms.hasOwnProperty(certID) && - certID == certB64Encoded) { + certID === certB64Encoded) { var certificateTransform = certificateTransforms[certID]; wearableLocalPosition = certificateTransform.localPosition; wearableLocalRotation = certificateTransform.localRotation; @@ -595,8 +359,10 @@ function rezEntity(itemHref, itemType) { targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); var targetPosition = getPositionToCreateEntity(); - var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. - var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. + // Distance to move entities parallel to targetDirection. + var deltaParallel = HALF_TREE_SCALE; + // Distance to move entities perpendicular to targetDirection. + var deltaPerpendicular = Vec3.ZERO; for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", "registrationPoint", "rotation", "parentID"]); @@ -624,7 +390,8 @@ function rezEntity(itemHref, itemType) { } if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { - for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { + for (var editEntityIndex = 0, + numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { if (Uuid.isNull(entityParentIDs[editEntityIndex])) { Entities.editEntity(pastedEntityIDs[editEntityIndex], { position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) @@ -727,79 +494,6 @@ function onWebEventReceived(message) { }); } } -var sendAssetRecipient; -var sendAssetParticleEffectUpdateTimer; -var particleEffectTimestamp; -var sendAssetParticleEffect; -var SEND_ASSET_PARTICLE_TIMER_UPDATE = 250; -var SEND_ASSET_PARTICLE_EMITTING_DURATION = 3000; -var SEND_ASSET_PARTICLE_LIFETIME_SECONDS = 8; -var SEND_ASSET_PARTICLE_PROPERTIES = { - accelerationSpread: { x: 0, y: 0, z: 0 }, - alpha: 1, - alphaFinish: 1, - alphaSpread: 0, - alphaStart: 1, - azimuthFinish: 0, - azimuthStart: -6, - color: { red: 255, green: 222, blue: 255 }, - colorFinish: { red: 255, green: 229, blue: 225 }, - colorSpread: { red: 0, green: 0, blue: 0 }, - colorStart: { red: 243, green: 255, blue: 255 }, - emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate - emitDimensions: { x: 0, y: 0, z: 0 }, - emitOrientation: { x: 0, y: 0, z: 0 }, - emitRate: 4, - emitSpeed: 2.1, - emitterShouldTrail: true, - isEmitting: 1, - lifespan: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate - lifetime: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, - maxParticles: 20, - name: 'asset-particles', - particleRadius: 0.2, - polarFinish: 0, - polarStart: 0, - radiusFinish: 0.05, - radiusSpread: 0, - radiusStart: 0.2, - speedSpread: 0, - textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", - type: 'ParticleEffect' -}; - -function updateSendAssetParticleEffect() { - var timestampNow = Date.now(); - if ((timestampNow - particleEffectTimestamp) > (SEND_ASSET_PARTICLE_LIFETIME_SECONDS * 1000)) { - deleteSendAssetParticleEffect(); - return; - } else if ((timestampNow - particleEffectTimestamp) > SEND_ASSET_PARTICLE_EMITTING_DURATION) { - Entities.editEntity(sendAssetParticleEffect, { - isEmitting: 0 - }); - } else if (sendAssetParticleEffect) { - var recipientPosition = AvatarList.getAvatar(sendAssetRecipient).position; - var distance = Vec3.distance(recipientPosition, MyAvatar.position); - var accel = Vec3.subtract(recipientPosition, MyAvatar.position); - accel.y -= 3.0; - var life = Math.sqrt(2 * distance / Vec3.length(accel)); - Entities.editEntity(sendAssetParticleEffect, { - emitAcceleration: accel, - lifespan: life - }); - } -} - -function deleteSendAssetParticleEffect() { - if (sendAssetParticleEffectUpdateTimer) { - Script.clearInterval(sendAssetParticleEffectUpdateTimer); - sendAssetParticleEffectUpdateTimer = null; - } - if (sendAssetParticleEffect) { - sendAssetParticleEffect = Entities.deleteEntity(sendAssetParticleEffect); - } - sendAssetRecipient = null; -} var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview"); var UI_FADE_TIMEOUT_MS = 150; @@ -816,7 +510,8 @@ var resourceObjectsInTest = []; function signalNewResourceObjectInTest(resourceObject) { ui.tablet.sendToQml({ method: "newResourceObjectInTest", - resourceObject: resourceObject }); + resourceObject: resourceObject + }); } var onQmlMessageReceived = function onQmlMessageReceived(message) { @@ -825,24 +520,21 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { } switch (message.method) { case 'gotoBank': - ui.close(); + ui.close(); if (Account.metaverseServerURL.indexOf("staging") >= 0) { Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging. } else { Window.location = "hifi://BankOfHighFidelity"; } - break; - case 'purchases_openWallet': - case 'checkout_openWallet': - case 'checkout_setUpClicked': - openWallet(); break; - case 'purchases_walletNotSetUp': + case 'checkout_openRecentActivity': + ui.open(MARKETPLACE_WALLET_QML_PATH); wireQmlEventBridge(true); ui.tablet.sendToQml({ - method: 'updateWalletReferrer', - referrer: "purchases" + method: 'checkout_openRecentActivity' }); + break; + case 'checkout_setUpClicked': openWallet(); break; case 'checkout_walletNotSetUp': @@ -865,23 +557,21 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'checkout_itemLinkClicked': openMarketplace(message.itemId); break; - case 'checkout_continueShopping': + case 'checkout_continue': openMarketplace(); break; - case 'purchases_itemInfoClicked': - var itemId = message.itemId; - if (itemId && itemId !== "") { - openMarketplace(itemId); - } - break; case 'checkout_rezClicked': case 'purchases_rezClicked': case 'tester_rezClicked': - rezEntity(message.itemHref, message.itemType); + rezEntity(message.itemHref, message.itemType, message.itemId); break; case 'tester_newResourceObject': var resourceObject = message.resourceObject; - resourceObjectsInTest[resourceObject.id] = resourceObject; + resourceObjectsInTest = []; // REMOVE THIS once we support specific referrers + resourceObject.currentlyRecordingResources = false; + resourceObject.resourceAccessEventText = ""; + resourceObjectsInTest[resourceObject.resourceObjectId] = resourceObject; + resourceObjectsInTest[resourceObject.resourceObjectId].resourceDataArray = []; signalNewResourceObjectInTest(resourceObject); break; case 'tester_updateResourceObjectAssetType': @@ -890,8 +580,14 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'tester_deleteResourceObject': delete resourceObjectsInTest[message.objectId]; break; + case 'tester_updateResourceRecordingStatus': + resourceObjectsInTest[message.objectId].currentlyRecordingResources = message.status; + if (message.status) { + resourceObjectsInTest[message.objectId].resourceDataArray = []; + resourceObjectsInTest[message.objectId].resourceAccessEventText = ""; + } + break; case 'header_marketplaceImageClicked': - case 'purchases_backClicked': openMarketplace(message.referrerURL); break; case 'purchases_goToMarketplaceClicked': @@ -899,9 +595,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { break; case 'updateItemClicked': openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition); - break; - case 'giftAsset': - break; case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': @@ -921,13 +614,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'maybeEnableHmdPreview': maybeEnableHMDPreview(); break; - case 'purchases_openGoTo': + case 'checkout_openGoTo': ui.open("hifi/tablet/TabletAddressDialog.qml"); break; - case 'purchases_itemCertificateClicked': - contextOverlayEntity = ""; - setCertificateInfo(contextOverlayEntity, message.itemCertificateId); - break; case 'inspectionCertificate_closeClicked': ui.close(); break; @@ -946,99 +635,45 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { method: 'purchases_showMyItems' }); break; - case 'refreshConnections': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - print('Refreshing Connections...'); - getConnectionData(false); - } - break; - case 'enable_ChooseRecipientNearbyMode': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - if (!isUpdateOverlaysWired) { - Script.update.connect(updateOverlays); - isUpdateOverlaysWired = true; - } - } - break; - case 'disable_ChooseRecipientNearbyMode': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); - } - break; - case 'purchases_availableUpdatesReceived': - shouldShowDot = message.numUpdates > 0; - ui.messagesWaiting(shouldShowDot && !ui.isOpen); - break; - case 'purchases_updateWearables': - var currentlyWornWearables = []; - var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case) - - var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS); - - for (var i = 0; i < nearbyEntities.length; i++) { - var currentProperties = Entities.getEntityProperties( - nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID'] - ); - if (currentProperties.parentID === MyAvatar.sessionUUID) { - currentlyWornWearables.push({ - entityID: nearbyEntities[i], - entityCertID: currentProperties.certificateID, - entityEdition: currentProperties.editionNumber - }); - } - } - - ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); - break; - case 'sendAsset_sendPublicly': - if (message.assetName !== "") { - deleteSendAssetParticleEffect(); - sendAssetRecipient = message.recipient; - var props = SEND_ASSET_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendAssetParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendAssetParticleEffect(); - sendAssetParticleEffectUpdateTimer = Script.setInterval(updateSendAssetParticleEffect, - SEND_ASSET_PARTICLE_TIMER_UPDATE); - } - break; case 'http.request': // Handled elsewhere, don't log. break; - case 'goToPurchases_fromWalletHome': // HRS FIXME What's this about? + // All of these are handled by wallet.js + case 'purchases_updateWearables': + case 'enable_ChooseRecipientNearbyMode': + case 'disable_ChooseRecipientNearbyMode': + case 'sendAsset_sendPublicly': + case 'refreshConnections': + case 'transactionHistory_goToBank': + case 'purchases_walletNotSetUp': + case 'purchases_openGoTo': + case 'purchases_itemInfoClicked': + case 'purchases_itemCertificateClicked': + case 'clearShouldShowDotHistory': + case 'giftAsset': break; default: - print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); + print('Unrecognized message from Checkout.qml: ' + JSON.stringify(message)); } }; function pushResourceObjectsInTest() { - var maxObjectId = -1; - for (var objectId in resourceObjectsInTest) { - signalNewResourceObjectInTest(resourceObjectsInTest[objectId]); - maxObjectId = (maxObjectId < objectId) ? parseInt(objectId) : maxObjectId; + var maxResourceObjectId = -1; + var length = resourceObjectsInTest.length; + for (var i = 0; i < length; i++) { + if (i in resourceObjectsInTest) { + signalNewResourceObjectInTest(resourceObjectsInTest[i]); + var resourceObjectId = resourceObjectsInTest[i].resourceObjectId; + maxResourceObjectId = (maxResourceObjectId < resourceObjectId) ? parseInt(resourceObjectId) : maxResourceObjectId; + } } // N.B. Thinking about removing the following sendToQml? Be sure // that the marketplace item tester QML has heard from us, at least // so that it can indicate to the user that all of the resoruce // objects in test have been transmitted to it. - ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: maxObjectId + 1 }); + //ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: maxResourceObjectId + 1 }); + // Since, for now, we only support 1 object in test, always send id: 0 + ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: 0 }); } // Function Name: onTabletScreenChanged() @@ -1087,11 +722,18 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { referrerURL: referrerURL, filterText: filterText }); + referrerURL = ""; + filterText = ""; } + var wasIsOpen = ui.isOpen; ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; ui.buttonActive(ui.isOpen); + if (wasIsOpen !== ui.isOpen && Keyboard.raised) { + Keyboard.raised = false; + } + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { @@ -1103,15 +745,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { } if (onCommerceScreen) { - if (!isWired) { - Users.usernameFromIDReply.connect(usernameFromIDReply); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - } - isWired = true; - Wallet.refreshWalletStatus(); + WalletScriptingInterface.refreshWalletStatus(); } else { if (onMarketplaceScreen) { onMarketplaceOpen('marketplace cta'); @@ -1133,44 +767,11 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { "\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n"); }; -function notificationDataProcessPage(data) { - return data.data.updates; -} - -var shouldShowDot = false; -function notificationPollCallback(updatesArray) { - shouldShowDot = shouldShowDot || updatesArray.length > 0; - ui.messagesWaiting(shouldShowDot && !ui.isOpen); - - if (updatesArray.length > 0) { - var message; - if (!ui.notificationInitialCallbackMade) { - message = updatesArray.length + " of your purchased items " + - (updatesArray.length === 1 ? "has an update " : "have updates ") + - "available. Open MARKET to update."; - ui.notificationDisplayBanner(message); - - ui.notificationPollCaresAboutSince = true; - } else { - for (var i = 0; i < updatesArray.length; i++) { - message = "Update available for \"" + - updatesArray[i].base_item_title + "\"." + - "Open MARKET to update."; - ui.notificationDisplayBanner(message); - } - } - } -} - -function isReturnedDataEmpty(data) { - var historyArray = data.data.updates; - return historyArray.length === 0; -} - var BUTTON_NAME = "MARKET"; var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. +// Append "?" if necessary to signal injected script that it's the initial page. +var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + (MARKETPLACE_URL.indexOf("?") > -1 ? "" : "?"); var ui; function startup() { ui = new AppUi({ @@ -1179,53 +780,31 @@ function startup() { inject: MARKETPLACES_INJECT_SCRIPT_URL, home: MARKETPLACE_URL_INITIAL, onScreenChanged: onTabletScreenChanged, - onMessage: onQmlMessageReceived, - notificationPollEndpoint: "/api/v1/commerce/available_updates?per_page=10", - notificationPollTimeoutMs: 300000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false // Changes to true after first poll + onMessage: onQmlMessageReceived }); ContextOverlay.contextOverlayClicked.connect(openInspectionCertificateQML); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); GlobalServices.myUsernameChanged.connect(onUsernameChanged); ui.tablet.webEventReceived.connect(onWebEventReceived); - Wallet.walletStatusChanged.connect(sendCommerceSettings); + WalletScriptingInterface.walletStatusChanged.connect(sendCommerceSettings); Window.messageBoxClosed.connect(onMessageBoxClosed); + ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent); - Wallet.refreshWalletStatus(); + WalletScriptingInterface.refreshWalletStatus(); } -var isWired = false; -var isUpdateOverlaysWired = false; function off() { - if (isWired) { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); - - isWired = false; - } - - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); } function shutdown() { maybeEnableHMDPreview(); - deleteSendAssetParticleEffect(); Window.messageBoxClosed.disconnect(onMessageBoxClosed); - Wallet.walletStatusChanged.disconnect(sendCommerceSettings); + WalletScriptingInterface.walletStatusChanged.disconnect(sendCommerceSettings); ui.tablet.webEventReceived.disconnect(onWebEventReceived); GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); ContextOverlay.contextOverlayClicked.disconnect(openInspectionCertificateQML); + ResourceRequestObserver.resourceRequestEvent.disconnect(onResourceRequestEvent); off(); } diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index fdc00c1ebf..a2f0d074f0 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -23,12 +23,17 @@ miniState = null, // Hands. + NO_HAND = -1, LEFT_HAND = 0, RIGHT_HAND = 1, HAND_NAMES = ["LeftHand", "RightHand"], - // Miscellaneous. + // Track controller grabbing state. HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", + grabbingHand = NO_HAND, + grabbedItem = null, + + // Miscellaneous. tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), DEBUG = false; @@ -737,37 +742,11 @@ setState(MINI_VISIBLE); } - function enterMiniShowing(hand) { - miniHand = hand; - ui.show(miniHand); - miniScaleStart = Date.now(); - miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); - } - - function updateMiniShowing() { - if (HMD.showTablet) { - setState(MINI_HIDDEN); - } - } - - function exitMiniShowing() { - if (miniScaleTimer) { - Script.clearTimeout(miniScaleTimer); - miniScaleTimer = null; - } - } - - function updateMiniVisible() { + function checkMiniVisibility() { var showLeft, showRight; - // Hide mini tablet if tablet proper has been displayed by other means. - if (HMD.showTablet) { - setState(MINI_HIDDEN); - return; - } - - // Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is + // Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is // gazing at. showLeft = shouldShowMini(LEFT_HAND); showRight = shouldShowMini(RIGHT_HAND); @@ -790,8 +769,47 @@ setState(MINI_HIDING); } } else { - setState(MINI_HIDING); + if (grabbedItem === null || grabbingHand !== miniHand) { + setState(MINI_HIDING); + } else { + setState(MINI_HIDDEN); + } } + } + + function enterMiniShowing(hand) { + miniHand = hand; + ui.show(miniHand); + miniScaleStart = Date.now(); + miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); + } + + function updateMiniShowing() { + // Hide mini tablet if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + } + + // Hide mini tablet if it should no longer be visible. + checkMiniVisibility(); + } + + function exitMiniShowing() { + if (miniScaleTimer) { + Script.clearTimeout(miniScaleTimer); + miniScaleTimer = null; + } + } + + function updateMiniVisible() { + // Hide mini tablet if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + return; + } + + // Hide mini tablet if it should no longer be visible. + checkMiniVisibility(); // If state hasn't changed, update mini tablet rotation. if (miniState === MINI_VISIBLE) { @@ -973,6 +991,21 @@ return; } + // Track grabbed state and item. + switch (message.action) { + case "grab": + grabbingHand = HAND_NAMES.indexOf(message.joint); + grabbedItem = message.grabbedEntity; + break; + case "release": + grabbingHand = NO_HAND; + grabbedItem = null; + break; + default: + error("Unexpected grab message!"); + return; + } + if (message.grabbedEntity !== HMD.tabletID && message.grabbedEntity !== ui.getMiniTabletID()) { return; } diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 36fe264274..9558b99310 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -634,7 +634,7 @@ Window.notifyEditError = onEditError; Window.notify = onNotify; Tablet.tabletNotification.connect(tabletNotification); - Wallet.walletNotSetup.connect(walletNotSetup); + WalletScriptingInterface.walletNotSetup.connect(walletNotSetup); Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL); Messages.messageReceived.connect(onMessageReceived); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a2ebae1a33..341ce9ebc8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -844,7 +844,7 @@ function notificationPollCallback(connectionsArray) { newOnlineUsers++; storedOnlineUsers[user.username] = user; - if (!ui.isOpen && ui.notificationInitialCallbackMade) { + if (!ui.isOpen && ui.notificationInitialCallbackMade[0]) { message = user.username + " is available in " + user.location.root.name + ". Open PEOPLE to join them."; ui.notificationDisplayBanner(message); @@ -868,7 +868,7 @@ function notificationPollCallback(connectionsArray) { shouldShowDot: shouldShowDot }); - if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade) { + if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade[0]) { message = newOnlineUsers + " of your connections " + (newOnlineUsers === 1 ? "is" : "are") + " available online. Open PEOPLE to join them."; ui.notificationDisplayBanner(message); @@ -889,12 +889,12 @@ function startup() { onOpened: palOpened, onClosed: off, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/users?filter=connections&status=online&per_page=10", - notificationPollTimeoutMs: 60000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false + notificationPollEndpoint: ["/api/v1/users?filter=connections&status=online&per_page=10"], + notificationPollTimeoutMs: [60000], + notificationDataProcessPage: [notificationDataProcessPage], + notificationPollCallback: [notificationPollCallback], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty], + notificationPollCaresAboutSince: [false] }); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); diff --git a/scripts/system/particle_explorer/hifi-entity-ui.js b/scripts/system/particle_explorer/hifi-entity-ui.js deleted file mode 100644 index 62a0aadc86..0000000000 --- a/scripts/system/particle_explorer/hifi-entity-ui.js +++ /dev/null @@ -1,709 +0,0 @@ -/* global window, document, print, alert, console,setTimeout, clearTimeout, _ $ */ -/* eslint no-console: 0 */ - -/** -UI Builder V1.0 - -Created by Matti 'Menithal' Lahtinen -24/5/2017 -Copyright 2017 High Fidelity, Inc. - -This can eventually be expanded to all of Edit, for now, starting -with Particles Only. - -This is created for the sole purpose of streamliming the bridge, and to simplify -the logic between an inputfield in WebView and Entities in High Fidelity. - -We also do not need anything as heavy as jquery or any other platform, -as we are mostly only building for QT (while, all the other JS frameworks usually do alot of polyfilling) - -Available Types: - - JSONInputField - Accepts JSON input, once one presses Save, it will be propegated. - Button- A Button that listens for a custom event as defined by callback - Boolean - Creates a checkbox that the user can either check or uncheck - SliderFloat - Creates a slider (with input) that has Float values from min to max. - Default is min 0, max 1 - SliderInteger - Creates a slider (with input) that has a Integer value from min to max. - Default is min 1, max 10000 - SliderRadian - Creates a slider (with input) that has Float values in degrees, - that are converted to radians. default is min 0, max Math.PI. - Texture - Creates a Image with an url input field that points to texture. - If image cannot form, show "cannot find image" - VecQuaternion - Creates a 3D Vector field that converts to quaternions. - Checkbox exists to show quaternions instead. - Color - Create field color button, that when pressed, opens the color picker. - Vector - Create a 3D Vector field that has one to one correspondence. - -The script will use this structure to build a UI that is connected The -id fields within High Fidelity - -This should make editing, and everything related much more simpler to maintain, -and If there is any changes to either the Entities or properties of - -**/ - -var RADIANS_PER_DEGREE = Math.PI / 180; -var DEBOUNCE_TIMEOUT = 125; - -var roundFloat = function (input, round) { - round = round ? round : 1000; - var sanitizedInput; - if (typeof input === "string") { - sanitizedInput = parseFloat(input); - } else { - sanitizedInput = input; - } - return Math.round(sanitizedInput * round) / round; -}; - -function HifiEntityUI(parent) { - this.parent = parent; - - var self = this; - this.sendPackage = {}; - this.settingsUpdateLock = false; - this.webBridgeSync = function(id, val) { - if (!this.settingsUpdateLock) { - this.sendPackage[id] = val; - this.webBridgeSyncDebounce(); - } - }; - this.webBridgeSyncDebounce = _.debounce(function () { - if (self.EventBridge) { - self.submitChanges(self.sendPackage); - self.sendPackage = {}; - } - }, DEBOUNCE_TIMEOUT); -} - -HifiEntityUI.prototype = { - setOnSelect: function (callback) { - this.onSelect = callback; - }, - submitChanges: function (structure) { - var message = { - messageType: "settings_update", - updatedSettings: structure - }; - this.EventBridge.emitWebEvent(JSON.stringify(message)); - }, - setUI: function (structure) { - this.structure = structure; - }, - disableFields: function () { - var fields = document.getElementsByTagName("input"); - for (var i = 0; i < fields.length; i++) { - if (fields[i].getAttribute("type") !== "button") { - fields[i].value = ""; - } - - fields[i].setAttribute("disabled", true); - } - var textures = document.getElementsByTagName("img"); - for (i = 0; i < textures.length; i++) { - textures[i].src = ""; - } - - textures = document.getElementsByClassName("with-texture"); - for (i = 0; i < textures.length; i++) { - textures[i].classList.remove("with-textures"); - textures[i].classList.add("no-texture"); - } - - var textareas = document.getElementsByTagName("textarea"); - for (var x = 0; x < textareas.length; x++) { - textareas[x].remove(); - } - }, - getSettings: function () { - var self = this; - var json = {}; - var keys = Object.keys(self.builtRows); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var el = self.builtRows[key]; - if (el.className.indexOf("checkbox") !== -1) { - json[key] = document.getElementById(key) - .checked ? true : false; - } else if (el.className.indexOf("vector-section") !== -1) { - var vector = {}; - if (el.className.indexOf("rgb") !== -1) { - var red = document.getElementById(key + "-red"); - var blue = document.getElementById(key + "-blue"); - var green = document.getElementById(key + "-green"); - vector.red = red.value; - vector.blue = blue.value; - vector.green = green.value; - } else if (el.className.indexOf("pyr") !== -1) { - var p = document.getElementById(key + "-Pitch"); - var y = document.getElementById(key + "-Yaw"); - var r = document.getElementById(key + "-Roll"); - vector.x = p.value; - vector.y = y.value; - vector.z = r.value; - } else { - var x = document.getElementById(key + "-x"); - var ey = document.getElementById(key + "-y"); - var z = document.getElementById(key + "-z"); - vector.x = x.value; - vector.y = ey.value; - vector.z = z.value; - } - json[key] = vector; - } else if (el.className.indexOf("radian") !== -1) { - json[key] = document.getElementById(key).value * RADIANS_PER_DEGREE; - } else if (el.className.length > 0) { - json[key] = document.getElementById(key).value; - } - } - - - return json; - }, - fillFields: function (currentProperties) { - var self = this; - var fields = document.getElementsByTagName("input"); - - if (!currentProperties.locked) { - for (var i = 0; i < fields.length; i++) { - fields[i].removeAttribute("disabled"); - if (fields[i].hasAttribute("data-max")) { - // Reset Max to original max - fields[i].setAttribute("max", fields[i].getAttribute("data-max")); - } - } - } - - if (self.onSelect) { - self.onSelect(); - } - var keys = Object.keys(currentProperties); - - - for (var e in keys) { - if (keys.hasOwnProperty(e)) { - var value = keys[e]; - - var property = currentProperties[value]; - var field = self.builtRows[value]; - if (field) { - var el = document.getElementById(value); - - if (field.className.indexOf("radian") !== -1) { - el.value = property / RADIANS_PER_DEGREE; - el.onchange({ - target: el - }); - } else if (field.className.indexOf("range") !== -1 || field.className.indexOf("texture") !== -1) { - el.value = property; - el.onchange({ - target: el - }); - } else if (field.className.indexOf("checkbox") !== -1) { - if (property) { - el.setAttribute("checked", property); - } else { - el.removeAttribute("checked"); - } - } else if (field.className.indexOf("vector-section") !== -1) { - if (field.className.indexOf("rgb") !== -1) { - var red = document.getElementById(value + "-red"); - var blue = document.getElementById(value + "-blue"); - var green = document.getElementById(value + "-green"); - red.value = parseInt(property.red); - blue.value = parseInt(property.blue); - green.value = parseInt(property.green); - - red.oninput({ - target: red - }); - } else if (field.className.indexOf("xyz") !== -1) { - var x = document.getElementById(value + "-x"); - var y = document.getElementById(value + "-y"); - var z = document.getElementById(value + "-z"); - - x.value = roundFloat(property.x, 100); - y.value = roundFloat(property.y, 100); - z.value = roundFloat(property.z, 100); - } else if (field.className.indexOf("pyr") !== -1) { - var pitch = document.getElementById(value + "-Pitch"); - var yaw = document.getElementById(value + "-Yaw"); - var roll = document.getElementById(value + "-Roll"); - - pitch.value = roundFloat(property.x, 100); - yaw.value = roundFloat(property.y, 100); - roll.value = roundFloat(property.z, 100); - - } - } - } - } - } - }, - connect: function (EventBridge) { - this.EventBridge = EventBridge; - - var self = this; - - EventBridge.emitWebEvent(JSON.stringify({ - messageType: 'page_loaded' - })); - - EventBridge.scriptEventReceived.connect(function (data) { - data = JSON.parse(data); - - if (data.messageType === 'particle_settings') { - self.settingsUpdateLock = true; - self.fillFields(data.currentProperties); - self.settingsUpdateLock = false; - // Do expected property match with structure; - } else if (data.messageType === 'particle_close') { - self.disableFields(); - } - }); - }, - build: function () { - var self = this; - var sections = Object.keys(this.structure); - this.builtRows = {}; - sections.forEach(function (section, index) { - var properties = self.structure[section]; - self.addSection(self.parent, section, properties, index); - }); - }, - addSection: function (parent, section, properties, index) { - var self = this; - - var sectionDivHeader = document.createElement("fieldset"); - var title = document.createElement("legend"); - var dropDown = document.createElement("span"); - - dropDown.className = "arrow"; - sectionDivHeader.className = "major"; - title.className = "section-header"; - title.id = section + "-section"; - title.innerHTML = section; - title.appendChild(dropDown); - sectionDivHeader.appendChild(title); - - var collapsed = index !== 0; - - dropDown.innerHTML = collapsed ? "L" : "M"; - sectionDivHeader.setAttribute("collapsed", collapsed); - parent.appendChild(sectionDivHeader); - - var sectionDivBody = document.createElement("div"); - sectionDivBody.className = "property-group"; - - var animationWrapper = document.createElement("div"); - animationWrapper.className = "section-wrap"; - - for (var property in properties) { - if (properties.hasOwnProperty(property)) { - var builtRow = self.addElement(animationWrapper, properties[property]); - var id = properties[property].id; - if (id) { - self.builtRows[id] = builtRow; - } - } - } - sectionDivBody.appendChild(animationWrapper); - sectionDivHeader.appendChild(sectionDivBody); - _.defer(function () { - var height = (animationWrapper.clientHeight) + "px"; - if (collapsed) { - sectionDivBody.classList.remove("visible"); - sectionDivBody.style.maxHeight = "0px"; - } else { - sectionDivBody.classList.add("visible"); - sectionDivBody.style.maxHeight = height; - } - - title.onclick = function () { - collapsed = !collapsed; - if (collapsed) { - sectionDivBody.classList.remove("visible"); - sectionDivBody.style.maxHeight = "0px"; - } else { - sectionDivBody.classList.add("visible"); - sectionDivBody.style.maxHeight = (animationWrapper.clientHeight) + "px"; - } - // sectionDivBody.style.display = collapsed ? "none": "block"; - dropDown.innerHTML = collapsed ? "L" : "M"; - title.setAttribute("collapsed", collapsed); - }; - }); - }, - addLabel: function (parent, group) { - var label = document.createElement("label"); - label.innerHTML = group.name; - parent.appendChild(label); - if (group.unit) { - var span = document.createElement("span"); - span.innerHTML = group.unit; - span.className = "unit"; - label.appendChild(span); - } - return label; - }, - addVector: function (parent, group, labels, domArray) { - var self = this; - var inputs = labels ? labels : ["x", "y", "z"]; - domArray = domArray ? domArray : []; - parent.id = group.id; - for (var index in inputs) { - var element = document.createElement("input"); - - element.setAttribute("type", "number"); - element.className = inputs[index]; - element.id = group.id + "-" + inputs[index]; - - if (group.defaultRange) { - if (group.defaultRange.min) { - element.setAttribute("min", group.defaultRange.min); - } - if (group.defaultRange.max) { - element.setAttribute("max", group.defaultRange.max); - } - if (group.defaultRange.step) { - element.setAttribute("step", group.defaultRange.step); - } - } - if (group.oninput) { - element.oninput = group.oninput; - } else { - element.oninput = function (event) { - self.webBridgeSync(group.id, { - x: domArray[0].value, - y: domArray[1].value, - z: domArray[2].value - }); - }; - } - element.onchange = element.oninput; - domArray.push(element); - } - - this.addLabel(parent, group); - var className = ""; - for (var i = 0; i < inputs.length; i++) { - className += inputs[i].charAt(0) - .toLowerCase(); - } - parent.className += " property vector-section " + className; - - // Add Tuple and the rest - var tupleContainer = document.createElement("div"); - tupleContainer.className = "tuple"; - for (var domIndex in domArray) { - var container = domArray[domIndex]; - var div = document.createElement("div"); - var label = document.createElement("label"); - label.innerHTML = inputs[domIndex] + ":"; - label.setAttribute("for", container.id); - div.appendChild(container); - div.appendChild(label); - tupleContainer.appendChild(div); - } - parent.appendChild(tupleContainer); - }, - addVectorQuaternion: function (parent, group) { - this.addVector(parent, group, ["Pitch", "Yaw", "Roll"]); - }, - addColorPicker: function (parent, group) { - var self = this; - var $colPickContainer = $('
      ', { - id: group.id, - class: "color-picker" - }); - var updateColors = function (red, green, blue) { - $colPickContainer.css('background-color', "rgb(" + - red + "," + - green + "," + - blue + ")"); - }; - - var inputs = ["red", "green", "blue"]; - var domArray = []; - group.oninput = function (event) { - $colPickContainer.colpickSetColor( - { - r: domArray[0].value, - g: domArray[1].value, - b: domArray[2].value - }, - true); - }; - group.defaultRange = { - min: 0, - max: 255, - step: 1 - }; - - parent.appendChild($colPickContainer[0]); - self.addVector(parent, group, inputs, domArray); - - updateColors(domArray[0].value, domArray[1].value, domArray[2].value); - - // Could probably write a custom one for this to completely write out jquery, - // but for now, using the same as earlier. - - /* Color Picker Logic Here */ - - - $colPickContainer.colpick({ - colorScheme: (group.layoutColorScheme === undefined ? 'dark' : group.layoutColorScheme), - layout: (group.layoutType === undefined ? 'hex' : group.layoutType), - submit: (group.useSubmitButton === undefined ? true : group.useSubmitButton), - color: { - r: domArray[0].value, - g: domArray[1].value, - b: domArray[2].value - }, - onChange: function (hsb, hex, rgb, el) { - updateColors(rgb.r, rgb.g, rgb.b); - - domArray[0].value = rgb.r; - domArray[1].value = rgb.g; - domArray[2].value = rgb.b; - self.webBridgeSync(group.id, { - red: rgb.r, - green: rgb.g, - blue: rgb.b - }); - }, - onSubmit: function (hsb, hex, rgb, el) { - $(el) - .css('background-color', '#' + hex); - $(el) - .colpickHide(); - domArray[0].value = rgb.r; - domArray[1].value = rgb.g; - domArray[2].value = rgb.b; - self.webBridgeSync(group.id, { - red: rgb.r, - green: rgb.g, - blue: rgb.b - }); - } - }); - }, - addTextureField: function (parent, group) { - var self = this; - this.addLabel(parent, group); - parent.className += " property texture"; - var textureImage = document.createElement("div"); - var textureUrl = document.createElement("input"); - textureUrl.setAttribute("type", "text"); - textureUrl.id = group.id; - textureImage.className = "texture-image no-texture"; - var image = document.createElement("img"); - var imageLoad = _.debounce(function (url) { - if (url.slice(0, 5).toLowerCase() === "atp:/") { - image.src = ""; - image.style.display = "none"; - textureImage.classList.remove("with-texture"); - textureImage.classList.remove("no-texture"); - textureImage.classList.add("no-preview"); - } else if (url.length > 0) { - textureImage.classList.remove("no-texture"); - textureImage.classList.remove("no-preview"); - textureImage.classList.add("with-texture"); - image.src = url; - image.style.display = "block"; - } else { - image.src = ""; - image.style.display = "none"; - textureImage.classList.remove("with-texture"); - textureImage.classList.remove("no-preview"); - textureImage.classList.add("no-texture"); - } - }, DEBOUNCE_TIMEOUT * 2); - - textureUrl.oninput = function (event) { - // Add throttle - var url = event.target.value; - imageLoad(url); - self.webBridgeSync(group.id, url); - }; - textureUrl.onchange = textureUrl.oninput; - textureImage.appendChild(image); - parent.appendChild(textureImage); - parent.appendChild(textureUrl); - }, - addSlider: function (parent, group) { - var self = this; - this.addLabel(parent, group); - parent.className += " property range"; - var container = document.createElement("div"); - container.className = "slider-wrapper"; - var slider = document.createElement("input"); - slider.setAttribute("type", "range"); - - var inputField = document.createElement("input"); - inputField.setAttribute("type", "number"); - - container.appendChild(slider); - container.appendChild(inputField); - parent.appendChild(container); - - if (group.type === "SliderInteger") { - inputField.setAttribute("min", group.min !== undefined ? group.min : 0); - inputField.setAttribute("step", 1); - - slider.setAttribute("min", group.min !== undefined ? group.min : 0); - slider.setAttribute("max", group.max !== undefined ? group.max : 10000); - slider.setAttribute("data-max", group.max !== undefined ? group.max : 10000); - slider.setAttribute("step", 1); - - inputField.oninput = function (event) { - // TODO: Remove this functionality? Alan finds it confusing - if (parseInt(event.target.value) > parseInt(slider.getAttribute("max")) && group.max !== 1) { - slider.setAttribute("max", event.target.value); - } - slider.value = event.target.value; - self.webBridgeSync(group.id, slider.value); - }; - inputField.onchange = inputField.oninput; - slider.oninput = function (event) { - inputField.value = event.target.value; - self.webBridgeSync(group.id, inputField.value); - }; - - inputField.id = group.id; - } else if (group.type === "SliderRadian") { - slider.setAttribute("min", group.min !== undefined ? group.min : 0); - slider.setAttribute("max", group.max !== undefined ? group.max : 180); - slider.setAttribute("step", 1); - parent.className += " radian"; - inputField.setAttribute("min", (group.min !== undefined ? group.min : 0)); - inputField.setAttribute("max", (group.max !== undefined ? group.max : 180)); - - inputField.oninput = function (event) { - slider.value = event.target.value; - self.webBridgeSync(group.id, slider.value * RADIANS_PER_DEGREE); - }; - inputField.onchange = inputField.oninput; - - inputField.id = group.id; - slider.oninput = function (event) { - if (event.target.value > 0) { - inputField.value = Math.floor(event.target.value); - } else { - inputField.value = Math.ceil(event.target.value); - } - self.webBridgeSync(group.id, inputField.value * RADIANS_PER_DEGREE); - }; - var degrees = document.createElement("label"); - degrees.innerHTML = "°"; - degrees.style.fontSize = "1.4rem"; - degrees.style.display = "inline"; - degrees.style.verticalAlign = "top"; - degrees.style.paddingLeft = "0.4rem"; - container.appendChild(degrees); - - } else { - // Must then be Float - inputField.setAttribute("min", group.min !== undefined ? group.min : 0); - slider.setAttribute("step", 0.01); - - slider.setAttribute("min", group.min !== undefined ? group.min : 0); - slider.setAttribute("max", group.max !== undefined ? group.max : 1); - slider.setAttribute("data-max", group.max !== undefined ? group.max : 1); - slider.setAttribute("step", 0.01); - - inputField.oninput = function (event) { - // TODO: Remove this functionality? Alan finds it confusing - if (parseFloat(event.target.value) > parseFloat(slider.getAttribute("max")) && group.max !== 1) { - slider.setAttribute("max", event.target.value); - } - - slider.value = event.target.value; - self.webBridgeSync(group.id, slider.value); - // bind web sock update here. - }; - inputField.onchange = inputField.oninput; - slider.oninput = function (event) { - inputField.value = event.target.value; - self.webBridgeSync(group.id, inputField.value); - }; - - inputField.id = group.id; - } - - // UpdateBinding - }, - addCheckBox: function (parent, group) { - var checkBox = document.createElement("input"); - checkBox.setAttribute("type", "checkbox"); - var self = this; - checkBox.onchange = function (event) { - self.webBridgeSync(group.id, event.target.checked); - }; - checkBox.id = group.id; - parent.appendChild(checkBox); - var label = this.addLabel(parent, group); - label.setAttribute("for", checkBox.id); - parent.className += " property checkbox"; - }, - addElement: function (parent, group) { - var self = this; - var property = document.createElement("div"); - property.id = group.id; - - var row = document.createElement("div"); - switch (group.type) { - case "Button": - var button = document.createElement("input"); - button.setAttribute("type", "button"); - button.id = group.id; - if (group.disabled) { - button.disabled = group.disabled; - } - button.className = group.class; - button.value = group.name; - - button.onclick = group.callback; - parent.appendChild(button); - break; - case "Row": - var hr = document.createElement("hr"); - hr.className = "splitter"; - if (group.id) { - hr.id = group.id; - } - parent.appendChild(hr); - break; - case "Boolean": - self.addCheckBox(row, group); - parent.appendChild(row); - break; - case "SliderFloat": - case "SliderInteger": - case "SliderRadian": - self.addSlider(row, group); - parent.appendChild(row); - break; - case "Texture": - self.addTextureField(row, group); - parent.appendChild(row); - break; - case "Color": - self.addColorPicker(row, group); - parent.appendChild(row); - break; - case "Vector": - self.addVector(row, group); - parent.appendChild(row); - break; - case "VectorQuaternion": - self.addVectorQuaternion(row, group); - parent.appendChild(row); - break; - default: - console.log("not defined"); - } - return row; - } -}; \ No newline at end of file diff --git a/scripts/system/particle_explorer/particle-style.css b/scripts/system/particle_explorer/particle-style.css deleted file mode 100644 index cde325f6c6..0000000000 --- a/scripts/system/particle_explorer/particle-style.css +++ /dev/null @@ -1,140 +0,0 @@ -/* -// particle-style.css -// -// Created by Matti 'Menithal' Lahtinen on 21 May 2017 -// Copyright 2017 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 -*/ - - -.property-group { - max-height: 0; - -webkit-transition: max-height 0.15s ease-out; - transition: max-height 0.15s ease-out; - overflow: hidden; -} -.property-group.visible { - transition: max-height 0.25s ease-in; -} -.section-wrap { - width: 100%; -} -.property { - padding: 0.4rem 0; - margin: 0; -} -.property.checkbox { - margin: 0; -} -.property.range label{ - display: block; -} - -input[type="button"] { - margin: 0.4rem; - min-width: 6rem; -} -input[type="text"] { - margin: 0; -} -.property.range input[type=number]{ - margin-left: 0.8rem; - width: 5.4rem; - height: 1.8rem; -} -input[type=range] { - -webkit-appearance: none; - background: #2e2e2e; - height: 1.8rem; - border-radius: 1rem; -} -input[type=range]::-webkit-slider-thumb { - -webkit-appearance:none; - width: 0.6rem; - height: 1.8rem; - padding:0; - margin: 0; - background-color: #696969; - border-radius: 1rem; -} -input[type=range]::-webkit-slider-thumb:hover { - background-color: white; -} -input[type=range]:focus { /*#252525*/ - outline: none; -} -.tuple label { - text-transform: capitalize; -} -.slider-wrapper { - display: table; - padding: 0.4rem 0; -} -hr.splitter{ - width: 100%; - padding: 0.2rem 0 0 0; - margin: 0; - position: relative; - clear: both; -} -hr.splitter:last-of-type{ - padding:0; -} -#rem { - height: 1rem; - width: 1rem; -} -.property { - min-height: 2rem; -} -.property.vector-section{ - - width: 24rem; -} - -.property.texture { - display: block; -} -.property.texture input{ - margin: 0.4rem 0; -} -.texture-image img{ - padding: 0; - margin: 0; - width: 100%; - height: 100%; - display: none; -} -.texture-image { - display: block; - position: relative; - background-repeat: no-repeat; - background-position: center; - background-size: 100% 100%; - margin-top: 0.4rem; - height:128px; - width: 128px; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABhNJREFUeNrsnVFy4joQRVsSCwAqBMwqsrRsIavMEkICoeAf2+8j1R5ZGDBgpLzoUDVVmTT2dc8It/paOpi3t7faOSciImVZyn6/l6qqRETEWivj8VistYPFd7ud1HUtIiLGGBmPx5JKX0RkMplIzvmPnHNijBERaS7Ef1lrB40bY1oXgH5a/ZH+8P7+LlVVycfHR/MGa60URdGcYOi4MUaKomhGaGx9EZHlcplMP2X+Ly8vPwOgLEtxzklVVVJVVfOznqAsy9YFXhuvqqq5AF/Lj+srtr7+LpV+yvz1mNF+vxcRkdVqJdZaeXp6ap1ws9m0TjibzVoj6lJ8vV6fjJdlKev1ujViU+j7t8tc8p9Op1KWpYw06L9JL0Av0r9l+jXl3nhd11JV1VE8tn5YM3PI3xjzoxVOGvyDU7zQj6s/0tGmI++amtNV087F9Wf/FnVPzRtCXz8RdV1nlb/efUbaJy4Wi0FqzjU1yRgjs9ls0Jp3jb6IyPPzczL9lPkvFot/dwCtB/om/x9oyJoXxps65NW8mPpdNTeX/JtBEtYE/+AUL/Tj6g/qA3TVnD41a6g++Bp9rYOp9FPnH80HOBcvy1I2m81D++BL+o/2AX5r/vgA+AD4AOif8AH8EdpVcy71sX3jWp/8W2AKff/TkUv+Oufr9AF0YuKc66xJ18T7eNP3nP9WfZ0EzufzJPqp8y+KQuq67vYBdETqCDpVU/rEw5oUnr+rD46h73/qUuinzh8fAP22D6AjxznXcqq6akrf+KmaFB6vf4+t7/sAelfIJf/GB9jtdmKMkdVq1dQM3zg4VVNU/NY+1Bgjh8Oh6YM1+dj6X19fzXwgp/wbH0DFtS7oyf0RdKqmhPFr+1RdseKfP7a+Px/IKX98APTbPoDOJrv60L417d54TH3V8lfS5pT/yfUA6/X6qOZcqkm3xrUm6X9CTH3fB0ihnzr/Ix9A/3T1qbfWpGvjMfX9T0UK/dT54wOg/88H8EfGPTVr6D740frhLDmn/Hv5AH1qku9t31KTzh3/aP1LPsBfzr+XDxCO0K6ack/N6qp5MfUv+QB/Of/ePsCQfWmfc6EfV3/kjzZrrRwOh9YtKHSm/LjOH3yrMTzej4c1y//51PHoP0a/tR7AOSdFURw9rz5VU049zw7jl2qWrosP++BY+iI/+wJS6afMv9kXoA6gvimsieHzZr/m6MTp3PPuc3G9SP95OPpx9JtOgT4cHwA+QCJ9+ADwAeADsC+AfQHo/4b1APAB4APAB4APAB8APgB9OD4AfAD4AFFqEnwA+AD4APgA6P86HwA+AHyAZhIBHwA+AHwA+AD04X/eB4APAB8APgB8APgA8AHow/P0AeADwAeADwAfAD4AfAD68Px8APgA8AHgA8AHgA8AH0DO70/v6lHvjaOfVn8U/iLcXx5OUML96X49vRTX3/nPw9FPo9+sB5hMJuKck+VyeVRTrLWtdfNdcf95eldNCuOfn5+tSYy/Pz+2voi0fICc8p/P5z93gJAPEN4+wufN4evaePj99eH+ePTj6p/1Abp60kt9Ksf/v46HDwAfAD6A/6gUPgD7AtgXwPP4DNcDwAeADwAfAD4AfAD4ADyPz289AHyA+Pqp84cPIPAB8AHwAfAB8AHgA7Q+HfAB4APAB4APAB+APjw3HwA+AHwA+ADwAeADwAegD8/TB4APAB8APgB8APgA8AHow/PzAeADwAeADwAfAD4AfACJ//316KfVH/mjLeb31+vx/kWhH0+/tR7AOSdFUUT9/nq9oK4+OJa+iLT25+eUf7MvIOQDxPr+en2F++PRj6PfdAr04fgA8AES6cMHgA8AH4B9AewLQP83rAeADwAfAD4AfAD4APAB6MPxAeADwAeIUpPgA8AHwAfAB0D/1/kA8AHgAzSTCPgA8AHgA8AHoA//8z4AfAD4APAB4APAB4APQB+epw8AHwA+AHwA+ADwAeAD0Ifn5wPAB4APAB8APgB8gBz5AOb19bX2TYLpdNpqQ7bbbctJGjJeVZVst9vWLSu2/vf3t+Sc/yicFIRr0C7Fu76f/lw8XBePflr9/wYAqWwWUSLcO54AAAAASUVORK5CYII='); -} - -.texture-image.no-texture { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAB81JREFUeNrsnTGPm0oXht97FWm2Ch2pTEeHpUihsyvTuXO67Ta/IPkr+Qfp3MWdO7Zad0SKZDo6XIWOrTzV9xVXZ8SygGHXG4/t96lW68GGw8vMmZlzDv98+/btfyBXy780wXXzTv74/fs3rXFFfPz4kT0AoQAoAJqAAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQCgAQgEQCoBQAIQCIBQAoQAIBUAoAHLmvDv3C7i7u4PjOMiyDOv1+mC75XKJoiga2wRBAN/34TgOHMdBWZYoigJpmiLPcwrARhzHAQD4vg/P81pvlLRrwvM8zGYz00ZrbY5xHAe+7yPPc9zf36MsSwrAVmazGX78+DHoGM/zsFgsAAB5nmOz2ZgeQimF8XiMMAxNu+VyaQRCH8Ai8jyH4zgIw7D3MUopzOdzAECaplitVk+GB601kiTBz58/obWG4ziIoohOoI38+vULABCGYWd3X2U6nUIphbIsEcdxa7uiKPDw8GCGGtd1KQDbKMsSWZZBKYXJZNLrGN/3zdN/iDRNTdcvx1EAFqGUwmazeeIQduG6LpRSAIAsy3r9hrRjD2BxL5AkiXEI+8wetNa9PXtp13eIoQBOQJIkxmHrcgjlJkov8JKpJwVgIVpr47CFYdh6g/f7/ZM5/9CehgKwmDRNURQFlFKYTqeNN/rx8dH0AH2faBn7KYAzQKZ1QRCYZd0qf/78MX+PRqNe3ymO5W63owBsR9bwZShoGirEq++zeBQEweBZAwVwYh4eHqC1RhAErQ6jOHVdK3yu65qhJE1TDgHn5BDKTW6auxdFYdYOgiDAYrF40k4phTAM8fnzZyilUBRF54rhOfIOF06SJMYPaPt8v99jOp3C8zx4nget9bPZQ5ZlF3fzL0IAZVke9OLv7+/Njl/brCHLMozHY4xGI3z48MH0EEVRIMuyi40H+EdqBbNS6HXBSqGEAiAUAAVAE1AAhAIgFAChAAgFQCgAQgGQq+Eom0GLxeJgGHYVSdCUhM02yrI0qV5hGGIymaAsy9b0LNd1cXt7CwDYbDa98wOA/zKLVquVSQGr/nYTbe2iKDIh53JtZVmiLEvsdjtst9tn5z7EDmfXA3QFXdaTMbvYbrdm568tgkdueJ7njbt3QwJA+8YJ1tsFQQDXdXFzc2N2E0Uwk8kEX758eXbMEDtY2QOsVqtn//v69SsAYL1eH9xK7dNGgjuiKMJ4PH4WmSN7+QBMFu/3798bn1oAzz47NvVrqmYgz2azRpv1scNV+wDVaN969y6JIEmSWBmyJenlIgZbcgvOzgmUqJxqkmY18ldCvGwkz/MntQcogBcgETrVMV98Aptvfh1JTKEAXsBms4HWGp7nYT6fw3Ec5Hlufbi253lQSkFr3VqmhgLoQVmW2G63ZigQx8/2my/FKCR17WLWAV7LfD5vzOFLkqS1W0/T1HT9RVFY5/jNZjMz3ouvorVGHMet9QheYoer7AGq478Y2LaiDTc3N3Bd90megSwG2YQVPcDQ+a/ccK01ttutWSWsetl/i7bfq16TzP1lGFgul0exw9X2AJLGJV3joRXCl3rnXbUDhmQKl2WJ9XoNrbV1vdXZCUCWWqvVQGR8HFIgqmuaKUiCSJcA+nrzWmvzdA/ZN6EAKlTz/eXmA3iSuXOoNEzfBRsA+PTpU+PnUjxSfnvo9/ZNR6cAakjFj2rqd3VtQJ6u1z5h1e+SdYbqdK5aWHLImC0OoFQgpRN4YPoD/LfRVC8C2TQlkhVC3/dfVDG0/l1xHCOKIvi+b572atJoURSdtYnbfAHxV0aj0TP/oY8dzqYH6OscHXK26tO+rqcujmNTIKqtJkDfc0vTFMvl8smu436/R57niOO4NSbh0HfLkFHtpYbY4dgwOfRKYXIooQAIBUAB0AQUAKEACAVAKABCARAKgFAA5Gp4s93AKIrw/v17ExsnFEWB/X6P3W6HLMtaN0+GJkwOad+W2FlPLq3GHFSRdq85h2PYyGoByG6cvJOnHiEryZJSg7e+s1ZNmOyzSza0ffWYJsIwbMzk7Tp+6Dm81kZWC0BoCnSU7dowDE2K12q1alT60EDJYwVWKqUQRdHgPf9jnfMQG52dDyA5fLKnLlGztiB5Bn1eP3fuNvr31IaWZM9jhHIdEwk5G1Jk4hxtdPJZQJZlJrLWlnBpx3FMmrnrup3RReduIyumgXJxtryRUxw4mQXIO4Yv0UZWCMDWN3I2vX7u0mxk1RtDmp6yoQmTbe27kjK7iOMYt7e3CIIA2+22VyLIWyZ5Hrsnsmol0Jac+fo51QtSXJKNrOgBuvLsTrUOUO8FxAP3ff/gTXiLc3irt5aevAdQSpmpja0vZqq+fm4ymfz18i5vaaOTC0DSvapv8rQRmRY6joPxeHwxNjqpAGSpUwx8ikKJQ5AyNFKb4BJsdBIfwPM8BEFgFjXSNG3debMJSUv7GyuWf8tGby6Aaq2c+qvaJce/a3p2ioTJQ73A3d3di6aBbef8WhtZKQDJ6K1fTJ7neHx8PFjWTcbbvvPePm8QbVtc6ft/+UwKUdfbDT3n19roGDA59EphciihAAgFQAHQBBQAoQAIBUAoAEIBEAqAUACEAiAUAKEACAVAKABCARAKgFAAhAIgFAChAAgFQC4CkxgiceKEPQC5Iv4/APgB2O7x8IXXAAAAAElFTkSuQmCC'); -} - -.texture-image.no-preview { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA8sSURBVHhe7Z3rbxXFG8d7B9SWthRabLmIYlHkIEXKJdXYBEXxHtEXprwxxsR3/jG+8PLCaDDGeAkmKsTEoCUVKoVCA6WNtLS2UEUKBSy0tKW/D+eZM9nu7tmz55z+mC2Zz4tl9tk5c2bnO/PMM2dnS+6nn36aYzFH7vvvv6+SFhMoAY4fPy7nljvG448/zjFPTiymsAIYxgpgGCuAYawAhrECGMYKYBgrgGGsAIaxAhjGCmAYK4BhrACGsQIYxgpgGCuAYawAhrECGMYKYBgrgGGsAIaxAhjmLhQgPz+/pKRk3rx56jzaRHFf0ObNmxctWkTi7Nmzp0+fFqNm+/btRUVFP/30kzp3UFtbu27duqVLl+bl3e5Y169f7+rqam1tvXnzpmSIFNHdF1RTU7M6TkNDQ0FBgbImWLVqFZfUSQKyvfzyy88991x1dfXU1NSFCxdGRkbuueeeurq6pqam0tJSlS96RNcFSQvSo9V5IC+88MIDDzwwOjr6448/fvTRR19++eVnn322Z8+ev//+u7i4+M0331ywYIHKGjGiK8Aff/zBMRaL5ebmiiUZjz322MqVK/Ez33333ZkzZxgBYh8eHt67d++lS5do/W3btokxakRXANxIf38/3mPNmjXKlARxpkeOHKGtxaIZHx9vaWkhwfTg9WZRILoCgIQG0r7JKC8vlxm7s7NTLC6YyW/cuFFYWIiPUqYoEWkB+vr6cOJLlizBwyiTB2l9vA0xj1hcTE9PDw4OkiA6EkukiLQAcOzYMY4bN26UUy8LFy7k+O+//8qpL1euXOF43333yWmkiLoATKqEQwSmlZWVyjQTIiWOwZG+npYjSNQFwIG0tbWRqK+vF4sL1r0qlZzJyUmOYXLeeaIuAHR3d+PfmQbE27hgguUY3LgS/0RzHMwBAei/R48ezcvL8x0EOCiOxEJy6osoJ1JFjTkgAHR0dExMTBDLexe0EvsTKQUMgsWLF3OUWChqzA0BGARoQBN7wyHWa6Ojo1x6+OGHlWkmaEOoeuvWrXPnzilTlJgbAgBeiEEQi8W8Pf3kyZMct27d6v0JGsf15JNPkmA5lmyhYJY5IwAenNmYBW1RUZEyJSBMYiYoLi7etWtXWVmZsubkkHPHjh2EsCjX3NysrBFjzggANDSeRJ04wEF9//33rLYqKip27979yiuvNDY2Pvvss2+//TZ+ieBn//79V69eVbkjRv6WLVv4hxW/nEcB+iyuo6ura3x8XJnicIqToV8zGpgSlDXO2NhYZ2cnV+WnIVZtTLxEn+fPn9+3b180p9+qqiqOd9ub8ihH67M8xuPT65mf1YXocXe+KY+PGhoa6unp4Rjl1tfcbQLMOawAhrECGMYKYBgrgGGsAIaxAhjGCmAYK4BhrACGyfy3oNdff72mpkadJLh27Vpvb29LS8vExIRYdu7c6dpLOz09ffPmTXLypadOnVLWnJzGxsZYLKZOPHR0dDQ3N7/33nv5+fkff/yx7/PFBQsWvPPOO5T/4YcfLly4sKmpaXBw8Ntvv5Wr7777bsAOUbINDw+Th5IpX1kTyGcPHz7c2tqqTHG4NW7wzz//9N2tHczs/BY0NjZ2PQFVLy4uXr9+/UsvvaQuJxgfH1eZ4tkKCwsrKiq2b9/u3XbozOkEzaamps6ePUueZHvcsOfl5ZFHtkH4oorzQOFU7MqVKzS0S6fy8nKxeDvckiVLOGbza2u22yW/+eYbOo46ie9Te/XVV5ctW7Z8+fK//vpLWXNyfvjhB2ctaaaGhoYNGzZs3bq1q6tLWeP88ssvdCh14oFLDz30EA3tuxFRhBGRkvHJJ5+olB8XLlxg6NCs/f39ypRo93/++Wfp0qWMP+fuCnna7N2TGp5ZngMQ48iRIyQefPBBsfhy69atgwcPjo6OlpSU+G42SQaicv80tPfBJBbslBwsQDBDQ0McpVk1CMBAx2HyFa79jUhFfeRTmTH7k7DsEky5DxBPffHiRRKytS0kNMTAwAAN4d0tigX7+fPnfaeHkEjlxbFoEIAvlTFRXV0tRhBnNTIy4hwT6TL7Asgz2zBvBUlO/K+chkQc1IoVK+RUI5YzZ87IaWZIX3buMpIJAP+Jroxv5zQgOmW52WL2BZDtyv/995+cJkMeHHJX6T42wcPgZ5gJ1HkCsWTjf4C+TCuXlpZqFyctLl6etpZpIH5F6eScAjNglgVg+n3iiSdIuHoiI/f2S19xamtrN23a9NprrzEVt7W1uSKWtWvXPu2HuhzfHkF/pFfef//9ypSTQxoLPi3lw3dV3Ez4UnU5/nicJpZuBAigvTzfyyU9DWQfAkG2UdCLL76oPeC99947f/58Et3d3cQMYhTk0b8TejGhfXt7uzpPgCfxuhf49ddfVSonp6enhyhr1apVeHyxkOYYxv8QJauUA9yaXpEQCKEH8zAJThGA1pd7lLamM0mCPNhl73vGZDsCGK10FgGffvnyZZYqP//8s7qcgCY7EUemMvz+F198ceDAAaZiyaA5duwYixov6nIcaWhpdEHSfIucBqCKm4m8hSDIBhHp3URoMgHEr9wefHoaYChw71qbjMlWgK+//pp1o/DBBx98/vnnLBfp3epyAmI4ujDs3bv3t99+I/J5/vnnfd++4/7pj17U5TjohzsuKysTL8yRNM5HwqpgVHEzce7KoYlpUynZO83qaYAOxzGbFYCQrQAsXOkXgrc7+4IYuA5WwgHvvaSEVuMoKy859vb23r6QNbQ+zof2Je2cAAQ9DYhCWU4AMPtRUBhko2B9fX1aiwAnEu3IakCOYfxPSFgN4HnwP7h7xHA6GT0NyFScZQgEZgRgimYyKCwsrKurU6Y0weHIbwO0FEfGX5bxuBPp8kR0jAPX22d8EY2Oa6qqqiJt3gVlzKFDhzjGYjFaUCzpgs/BGzQ2NnJkWg7pAMMg8Y/8Wul1Mn19fUiONtl3fzAmAP0XN8IgcM0EGzZs2JkElSOBTAMsLDiGnwBUWR74XpUjvuxiJS/TgK8AdBpUz34CAGMCgPy27hoEdC5Zr3lRORIQ8krYMzExMTAwIMaUqLI8iE/XyCCgj+NnxKLRoWf2/gcyfyBDGDNv3jw6csCP70C0QPvSUq6tzgKelK5EUxJZElazlFMX/PB6efkIJXsD0IKCgsrKSuclmpi1t6S9uBy6lJzMy1My5ae892DExdn/R8wYd+fu6DmHFcAwVgDDWAEMYwUwjBXAMFYAw1gBDGMFMIwVwDBp/xSxZs2aqqqqsbGxw4cPK1PiD2W0t7cne0K9ePHitWvXXr9+Xf4aKFRWVj7yyCMkKIfSxKgpLS1lpT4yMqIrxinGU6dOBf95OGH16tXV1dWuSmrkmbs6iTM5OXnjxo2enh7560Oap+O7MZz7AVzIF6kTPwI+m+FPEbT1+vXrN2/eXFJSokzxfXAYH330UXXuYd26dWRw/uoZi8WwgPPZukYKdO5vJI0FDdR5IL6V1KxYseL2FzvYuHFjQ0NDU1OTa7uRXFUnftTU1EieZKh8yUlPALott3T58mXSiC9GkJ/mA/aDyo1JNsjPz6fdr169OjU15SxnVqioqCgrK/NW0oXefrF///4DBw5QN2r1zDPPFBcXqxyhOXnypBTlReVITnoCyP20tLS4Gq6/v58hvGjRIudfi9HIrqnR0VG9jWfZsmXz58/nnoeGhiQt9llBVxIXFCCA3n7R3d3d0dFBY3EXRUVF4hjTAq8oRXlROZKTtgATExN9fX0DAwMyGsQ+PT0te3V8b1iMztqIpbe3l6JkNIh9VtCVpEGdlUyJPOjnI3J6Z0hDALkZbozuL63pbG6vReMSQFqcEcOACPhUZoj/kUrKPonwhcvTlTDbimeRNASQt1mkp9N5uUPn+y2Dg4M4Ge7f1eOQTR4taf+zcuVKfI6UI5sbli9f7pyfs0GaWwpnmLoqGYxswwr/dHNWSEMA7o37kfdecK+4b+luchUv5NudnS0iiEU/Rmfg5+XlBb/QEZ7gSjoh0CpPwOy1adMmQrVz58653tgJAz1MFTQT79+w8xJWACZSvobeoWN2r9MXAWSfmkb8u8v/UIjuaOk6igCkrYMrqXnqqad2JyAA3bZtG8N037593n2VKamvr1cFzaS2tlblSE5YAeQenLvPpJc57w0ng0thYaL3u0mLcGN6Bwf+p7CwkOmRfiqWixcv4rsIqLP3QmEqqRkeHqZWQK8njMH1U+233nor5FLDCcs3KcpFypckIOz2dLkHhiqrG7EAlZYmlqAb6Oksaoj65W+6iWOhG+pdU1IOGjjLQSGGF5nlD1BmTMhKCq2trXpcAkOT5RuV37Fjx1dffaWs4Whvb3f9DbvwhBoBdE8aiASr5y0O5B0j519MlVvSDt21/iooKBCPxFEVEYcGwhhmwAYgrUwiZSV9YUQeOnQI31VVVZXWe4NZEkoAqT3tyIrRibwQ6Ww4Qho6mvgTmoNG4ZZ0/EO70/cZ7+rzDojc+VTGe3VBur+3kvq/MInnCgINqD+JDLxQxqQWIDc3VzoyHYSB5uT333/HfUtDS2agCYhqWN8CpxKwyiVpI/XhmUhQJBkyQz7rrWRbWxvu3lXJZMhw0RW+A6QWQLoz9+DyoYI3hmFlzxHN+CAJp/+RAMk5SWqyjIXE/ySrJOsyjikLp+OzaiEKohxl+v+TWgCpt2+rgTfOu3TpEoENrQ/OcBP/w0RHyMGUKxYnrAbod84IyheCa/K4YH4KrqSvAK6i6urq3njjDcbu6dOnXTVUOWZCf1KX48opqweZOwNIEQVp/6PXTS7w77SyDHC9C5NeT0RBorOz0+V/5PcWL5OTk0hFkEq2EydOKKsHJlWVcoCjl8KTVVJUd1XStyjmp4MHD6qTBLt27VIpB3v27NEDZUMcSbugbrhBdeJHij9dTDyAvFQrWaMQXyLS+Pj4tWvX9PAn/kV5hgJhJXYxMgLIQDm+u3SBeZgOKJM2/YuhwJSoN+SWlJTQiJTphTZlzRlQSXBWkjUwsan6cBy+iLD9+PHjzc3Nzv22RLQqhwfEphBukx6mTH6wEEn2kOru/NPFc4gMn4hZZhcrgGGsAIaxAhjGCmAYK4BhrACGsQIYxgpgGCuAYawAhrECGMYKYBgrgGGsAIaxAhjGCmAYK4BhrACGsQIYxgpgGCuAYdS2FIsp7AgwSk7O/wCqCi/+JioQYgAAAABJRU5ErkJggg=='); -} - -#properties-list > fieldset { - margin-top: 0px; -} - -#main-header { - margin-bottom: 21px; -} - -.section-wrap { - padding: 21px 0px; -} \ No newline at end of file diff --git a/scripts/system/particle_explorer/particleExplorer.html b/scripts/system/particle_explorer/particleExplorer.html deleted file mode 100644 index ab4c249cc3..0000000000 --- a/scripts/system/particle_explorer/particleExplorer.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
      -
      - -
      - -
      -
      - - - diff --git a/scripts/system/particle_explorer/particleExplorer.js b/scripts/system/particle_explorer/particleExplorer.js deleted file mode 100644 index f1b7c8600f..0000000000 --- a/scripts/system/particle_explorer/particleExplorer.js +++ /dev/null @@ -1,485 +0,0 @@ -// -// particleExplorer.js -// -// Created by James B. Pollack @imgntn on 9/26/2015 -// Copyright 2017 High Fidelity, Inc. -// -// Reworked by Menithal on 20/5/2017 -// Reworked by Daniela Fontes and Artur Gomes (Mimicry) on 12/18/2017 -// -// Web app side of the App - contains GUI. -// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -/* global HifiEntityUI, openEventBridge, console, EventBridge, document, window */ -/* eslint no-console: 0, no-global-assign: 0 */ - -(function () { - - var root = document.getElementById("properties-list"); - - window.onload = function () { - var ui = new HifiEntityUI(root); - var textarea = document.createElement("textarea"); - var properties = ""; - var menuStructure = { - General: [ - { - type: "Row", - id: "export-import-field" - }, - { - id: "show-properties-button", - name: "Show Properties", - type: "Button", - class: "blue", - disabled: true, - callback: function (event) { - var insertZone = document.getElementById("export-import-field"); - var json = ui.getSettings(); - properties = JSON.stringify(json); - textarea.value = properties; - if (!insertZone.contains(textarea)) { - insertZone.appendChild(textarea); - insertZone.parentNode.parentNode.style.maxHeight = - insertZone.parentNode.clientHeight + "px"; - document.getElementById("export-properties-button").removeAttribute("disabled"); - textarea.onchange = function (e) { - if (e.target.value !== properties) { - document.getElementById("import-properties-button").removeAttribute("disabled"); - } - }; - textarea.oninput = textarea.onchange; - document.getElementById("show-properties-button").value = "Hide Properties"; - } else { - textarea.onchange = function () {}; - textarea.oninput = textarea.onchange; - textarea.value = ""; - textarea.remove(); - insertZone.parentNode.parentNode.style.maxHeight = - insertZone.parentNode.clientHeight + "px"; - document.getElementById("export-properties-button").setAttribute("disabled", true); - document.getElementById("import-properties-button").setAttribute("disabled", true); - document.getElementById("show-properties-button").value = "Show Properties"; - } - } - }, - { - id: "import-properties-button", - name: "Import", - type: "Button", - class: "blue", - disabled: true, - callback: function (event) { - ui.fillFields(JSON.parse(textarea.value)); - ui.submitChanges(JSON.parse(textarea.value)); - } - }, - { - id: "export-properties-button", - name: "Export", - type: "Button", - class: "red", - disabled: true, - callback: function (event) { - textarea.select(); - try { - var success = document.execCommand('copy'); - if (!success) { - throw "Not success :("; - } - } catch (e) { - print("couldnt copy field"); - } - } - }, - { - type: "Row" - }, - { - id: "isEmitting", - name: "Is Emitting", - type: "Boolean" - }, - { - type: "Row" - }, - { - id: "lifespan", - name: "Lifespan", - type: "SliderFloat", - min: 0.01, - max: 10 - }, - { - type: "Row" - }, - { - id: "maxParticles", - name: "Max Particles", - type: "SliderInteger", - min: 1, - max: 10000 - }, - { - type: "Row" - }, - { - id: "textures", - name: "Textures", - type: "Texture" - }, - { - type: "Row" - } - ], - Emit: [ - { - id: "emitRate", - name: "Emit Rate", - type: "SliderInteger", - max: 1000, - min: 1 - }, - { - type: "Row" - }, - { - id: "emitSpeed", - name: "Emit Speed", - type: "SliderFloat", - max: 5 - }, - { - id: "speedSpread", - name: "Speed Spread", - type: "SliderFloat", - max: 5 - }, - { - type: "Row" - }, - { - id: "emitDimensions", - name: "Emit Dimension", - type: "Vector", - defaultRange: { - min: 0, - step: 0.01 - } - }, - { - type: "Row" - }, - { - id: "emitOrientation", - unit: "deg", - name: "Emit Orientation", - type: "VectorQuaternion", - defaultRange: { - min: 0, - step: 0.01 - } - }, - { - type: "Row" - }, - { - id: "emitterShouldTrail", - name: "Emitter Should Trail", - type: "Boolean" - }, - { - type: "Row" - } - ], - Radius: [ - { - id: "particleRadius", - name: "Particle Radius", - type: "SliderFloat", - max: 4.0 - }, - { - type: "Row" - }, - { - id: "radiusSpread", - name: "Radius Spread", - type: "SliderFloat", - max: 4.0 - }, - { - type: "Row" - }, - { - id: "radiusStart", - name: "Radius Start", - type: "SliderFloat", - max: 4.0 - }, - { - type: "Row" - }, - { - id: "radiusFinish", - name: "Radius Finish", - type: "SliderFloat", - max: 4.0 - }, - { - type: "Row" - } - ], - Color: [ - { - id: "color", - name: "Color", - type: "Color", - defaultColor: { - red: 255, - green: 255, - blue: 255 - }, - layoutType: "hex", - layoutColorScheme: "dark", - useSubmitButton: false - }, - { - type: "Row" - }, - { - id: "colorSpread", - name: "Color Spread", - type: "Color", - defaultColor: { - red: 0, - green: 0, - blue: 0 - }, - layoutType: "hex", - layoutColorScheme: "dark", - useSubmitButton: false - }, - { - type: "Row" - }, - { - id: "colorStart", - name: "Color Start", - type: "Color", - defaultColor: { - red: 255, - green: 255, - blue: 255 - }, - layoutType: "hex", - layoutColorScheme: "dark", - useSubmitButton: false - }, - { - type: "Row" - }, - { - id: "colorFinish", - name: "Color Finish", - type: "Color", - defaultColor: { - red: 255, - green: 255, - blue: 255 - }, - layoutType: "hex", - layoutColorScheme: "dark", - useSubmitButton: false - }, - { - type: "Row" - } - ], - Acceleration: [ - { - id: "emitAcceleration", - name: "Emit Acceleration", - type: "Vector", - defaultRange: { - step: 0.01 - } - }, - { - type: "Row" - }, - { - id: "accelerationSpread", - name: "Acceleration Spread", - type: "Vector", - defaultRange: { - step: 0.01 - } - }, - { - type: "Row" - } - ], - Alpha: [ - { - id: "alpha", - name: "Alpha", - type: "SliderFloat", - max: 1.0 - }, - { - type: "Row" - }, - { - id: "alphaSpread", - name: "Alpha Spread", - type: "SliderFloat", - max: 1.0 - }, - { - type: "Row" - }, - { - id: "alphaStart", - name: "Alpha Start", - type: "SliderFloat", - max: 1.0 - }, - { - type: "Row" - }, - { - id: "alphaFinish", - name: "Alpha Finish", - type: "SliderFloat", - max: 1.0 - }, - { - type: "Row" - } - ], - Spin: [ - { - id: "particleSpin", - name: "Particle Spin", - type: "SliderRadian", - min: -360.0, - max: 360.0 - }, - { - type: "Row" - }, - { - id: "spinSpread", - name: "Spin Spread", - type: "SliderRadian", - max: 360.0 - }, - { - type: "Row" - }, - { - id: "spinStart", - name: "Spin Start", - type: "SliderRadian", - min: -360.0, - max: 360.0 - }, - { - type: "Row" - }, - { - id: "spinFinish", - name: "Spin Finish", - type: "SliderRadian", - min: -360.0, - max: 360.0 - }, - { - type: "Row" - }, - { - id: "rotateWithEntity", - name: "Rotate with Entity", - type: "Boolean" - }, - { - type: "Row" - } - ], - Polar: [ - { - id: "polarStart", - name: "Polar Start", - unit: "deg", - type: "SliderRadian" - }, - { - type: "Row" - }, - { - id: "polarFinish", - name: "Polar Finish", - unit: "deg", - type: "SliderRadian" - }, - { - type: "Row" - } - ], - Azimuth: [ - { - id: "azimuthStart", - name: "Azimuth Start", - unit: "deg", - type: "SliderRadian", - min: -180, - max: 0 - }, - { - type: "Row" - }, - { - id: "azimuthFinish", - name: "Azimuth Finish", - unit: "deg", - type: "SliderRadian" - }, - { - type: "Row" - } - ] - }; - ui.setUI(menuStructure); - ui.setOnSelect(function () { - document.getElementById("show-properties-button").removeAttribute("disabled"); - document.getElementById("export-properties-button").setAttribute("disabled", true); - document.getElementById("import-properties-button").setAttribute("disabled", true); - }); - ui.build(); - var overrideLoad = false; - if (openEventBridge === undefined) { - overrideLoad = true, - openEventBridge = function (callback) { - callback({ - emitWebEvent: function () {}, - submitChanges: function () {}, - scriptEventReceived: { - connect: function () { - - } - } - }); - }; - } - openEventBridge(function (EventBridge) { - ui.connect(EventBridge); - }); - if (overrideLoad) { - openEventBridge(); - } - }; -})(); diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js deleted file mode 100644 index a3be004329..0000000000 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ /dev/null @@ -1,144 +0,0 @@ -// -// particleExplorerTool.js -// -// Created by Eric Levin on 2/15/16 -// Copyright 2016 High Fidelity, Inc. -// Adds particleExplorer tool to the edit panel when a user selects a particle entity from the edit tool window -// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -/* global ParticleExplorerTool */ - - -var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html'); - -ParticleExplorerTool = function(createToolsWindow) { - var that = {}; - that.activeParticleEntity = 0; - that.updatedActiveParticleProperties = {}; - - that.createWebView = function() { - that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - that.webView.setVisible = function(value) {}; - that.webView.webEventReceived.connect(that.webEventReceived); - createToolsWindow.webEventReceived.addListener(this, that.webEventReceived); - }; - - function emitScriptEvent(data) { - var messageData = JSON.stringify(data); - that.webView.emitScriptEvent(messageData); - createToolsWindow.emitScriptEvent(messageData); - } - - that.destroyWebView = function() { - if (!that.webView) { - return; - } - that.activeParticleEntity = 0; - that.updatedActiveParticleProperties = {}; - - emitScriptEvent({ - messageType: "particle_close" - }); - }; - - function sendParticleProperties(properties) { - emitScriptEvent({ - messageType: "particle_settings", - currentProperties: properties - }); - } - - function sendActiveParticleProperties() { - var properties = Entities.getEntityProperties(that.activeParticleEntity); - if (properties.emitOrientation) { - properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); - } - // Update uninitialized variables - if (isNaN(properties.alphaStart)) { - properties.alphaStart = properties.alpha; - } - if (isNaN(properties.alphaFinish)) { - properties.alphaFinish = properties.alpha; - } - if (isNaN(properties.radiusStart)) { - properties.radiusStart = properties.particleRadius; - } - if (isNaN(properties.radiusFinish)) { - properties.radiusFinish = properties.particleRadius; - } - if (isNaN(properties.colorStart.red)) { - properties.colorStart = properties.color; - } - if (isNaN(properties.colorFinish.red)) { - properties.colorFinish = properties.color; - } - if (isNaN(properties.spinStart)) { - properties.spinStart = properties.particleSpin; - } - if (isNaN(properties.spinFinish)) { - properties.spinFinish = properties.particleSpin; - } - sendParticleProperties(properties); - } - - function sendUpdatedActiveParticleProperties() { - sendParticleProperties(that.updatedActiveParticleProperties); - that.updatedActiveParticleProperties = {}; - } - - that.webEventReceived = function(message) { - var data = JSON.parse(message); - if (data.messageType === "settings_update") { - var updatedSettings = data.updatedSettings; - - var optionalProps = ["alphaStart", "alphaFinish", "radiusStart", "radiusFinish", "colorStart", "colorFinish", "spinStart", "spinFinish"]; - var fallbackProps = ["alpha", "particleRadius", "color", "particleSpin"]; - for (var i = 0; i < optionalProps.length; i++) { - var fallbackProp = fallbackProps[Math.floor(i / 2)]; - var optionalValue = updatedSettings[optionalProps[i]]; - var fallbackValue = updatedSettings[fallbackProp]; - if (optionalValue && fallbackValue) { - delete updatedSettings[optionalProps[i]]; - } - } - - if (updatedSettings.emitOrientation) { - updatedSettings.emitOrientation = Quat.fromVec3Degrees(updatedSettings.emitOrientation); - } - - Entities.editEntity(that.activeParticleEntity, updatedSettings); - - var entityProps = Entities.getEntityProperties(that.activeParticleEntity, optionalProps); - - var needsUpdate = false; - for (var i = 0; i < optionalProps.length; i++) { - var fallbackProp = fallbackProps[Math.floor(i / 2)]; - var fallbackValue = updatedSettings[fallbackProp]; - if (fallbackValue) { - var optionalProp = optionalProps[i]; - if ((fallbackProp !== "color" && isNaN(entityProps[optionalProp])) || (fallbackProp === "color" && isNaN(entityProps[optionalProp].red))) { - that.updatedActiveParticleProperties[optionalProp] = fallbackValue; - needsUpdate = true; - } - } - } - - if (needsUpdate) { - sendUpdatedActiveParticleProperties(); - } - - } else if (data.messageType === "page_loaded") { - sendActiveParticleProperties(); - } - }; - - that.setActiveParticleEntity = function(id) { - that.activeParticleEntity = id; - sendActiveParticleProperties(); - }; - - return that; -}; diff --git a/scripts/system/progress.js b/scripts/system/progress.js index db09d40608..b373612790 100644 --- a/scripts/system/progress.js +++ b/scripts/system/progress.js @@ -14,11 +14,11 @@ // (function () { // BEGIN LOCAL_SCOPE - function debug() { //print.apply(null, arguments); } + Script.include("/~/system/libraries/globals.js"); var rawProgress = 100, // % raw value. displayProgress = 100, // % smoothed value to display. alpha = 0.0, @@ -83,7 +83,9 @@ // The initial delay cooldown keeps us from tracking progress before the allotted time // has passed. INITIAL_DELAY_COOLDOWN_TIME = 1000, - initialDelayCooldown = 0; + initialDelayCooldown = 0, + + isInInterstitialMode = false; function fade() { @@ -265,7 +267,7 @@ // Update state if (!visible) { // Not visible because no recent downloads - if (displayProgress < 100 || gpuTextures > 0) { // Have started downloading so fade in + if ((displayProgress < 100 || gpuTextures > 0) && !isInInterstitialMode && !isInterstitialOverlaysVisible) { // Have started downloading so fade in visible = true; alphaDelta = ALPHA_DELTA_IN; fadeTimer = Script.setInterval(fade, FADE_INTERVAL); @@ -305,10 +307,13 @@ } else { x = x * BAR_HMD_REPEAT; } + if (isInInterstitialMode || isInterstitialOverlaysVisible) { + visible = false; + } // Update progress bar Overlays.editOverlay(barDesktop.overlay, { - visible: !isHMD, + visible: !isHMD && visible, bounds: { x: barDesktop.repeat - x, y: windowHeight - barDesktop.height, @@ -318,7 +323,7 @@ }); Overlays.editOverlay(barHMD.overlay, { - visible: isHMD, + visible: isHMD && visible, bounds: { x: BAR_HMD_REPEAT - x, y: windowHeight - BAR_HMD_HEIGHT, @@ -328,11 +333,11 @@ }); Overlays.editOverlay(textDesktop.overlay, { - visible: !isHMD + visible: !isHMD && visible }); Overlays.editOverlay(textHMD.overlay, { - visible: isHMD + visible: isHMD && visible }); // Update 2D overlays to maintain positions at bottom middle of window @@ -344,6 +349,10 @@ } } + function interstitialModeChanged(inMode) { + isInInterstitialMode = inMode; + } + function setUp() { var is4k = Window.innerWidth > 3000; @@ -369,6 +378,7 @@ } setUp(); + Window.interstitialModeChanged.connect(interstitialModeChanged); GlobalServices.downloadInfoChanged.connect(onDownloadInfoChanged); GlobalServices.updateDownloadInfo(); Script.setInterval(update, 1000 / 60); diff --git a/scripts/system/redirectOverlays.js b/scripts/system/redirectOverlays.js index b1180e0cd0..bb537bee0e 100644 --- a/scripts/system/redirectOverlays.js +++ b/scripts/system/redirectOverlays.js @@ -5,14 +5,20 @@ "Oops! Protocol version mismatch.", "Oops! Not authorized to join domain.", "Oops! Connection timed out.", + "Oops! The domain is full.", "Oops! Something went wrong." ]; var PROTOCOL_VERSION_MISMATCH = 1; var NOT_AUTHORIZED = 3; + var DOMAIN_FULL = 4; var TIMEOUT = 5; - var hardRefusalErrors = [PROTOCOL_VERSION_MISMATCH, - NOT_AUTHORIZED, TIMEOUT]; + var hardRefusalErrors = [ + PROTOCOL_VERSION_MISMATCH, + NOT_AUTHORIZED, + TIMEOUT, + DOMAIN_FULL + ]; var timer = null; var isErrorState = false; @@ -26,7 +32,7 @@ return ERROR_MESSAGE_MAP[errorMessageMapIndex]; } else { // some other text. - return ERROR_MESSAGE_MAP[4]; + return ERROR_MESSAGE_MAP[ERROR_MESSAGE_MAP.length - 1]; } }; diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 3d744b3bd2..ed6ff80398 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -44,7 +44,10 @@ try { } function removeFromStoryIDsToMaybeDelete(story_id) { - storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1); + story_id = parseInt(story_id); + if (storyIDsToMaybeDelete.indexOf(story_id) > -1) { + storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1); + } print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); } @@ -258,6 +261,7 @@ function onMessage(message) { } break; case 'removeFromStoryIDsToMaybeDelete': + console.log("Facebook OR Twitter button clicked for story_id " + message.story_id); removeFromStoryIDsToMaybeDelete(message.story_id); break; default: @@ -312,9 +316,7 @@ function printToPolaroid(image_url) { "dynamic": true, "collisionsWillMove": true, - "userData": { - "grabbableKey": { "grabbable" : true } - } + "grab": { "grabbable": true } }; var polaroid = Entities.addEntity(properties); @@ -335,11 +337,13 @@ function fillImageDataFromPrevious() { var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); + snapshotOptions = { containsGif: previousAnimatedSnapPath !== "", processingGif: false, shouldUpload: false, - canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID"), + canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID") && + snapshotDomainID === location.domainID, isLoggedIn: isLoggedIn }; imageData = []; @@ -371,7 +375,8 @@ function snapshotUploaded(isError, reply) { isGif = fileExtensionMatches(imageURL, "gif"), ignoreGifSnapshotData = false, ignoreStillSnapshotData = false; - storyIDsToMaybeDelete.push(storyID); + storyIDsToMaybeDelete.push(parseInt(storyID)); + print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); if (isGif) { if (mostRecentGifSnapshotFilename !== replyJson.user_story.details.original_image_file_name) { ignoreGifSnapshotData = true; diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 6d8ba3a927..94117fd9ea 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -37,7 +37,7 @@ function notificationPollCallback(userStoriesArray) { // pingPong = !pingPong; var totalNewStories = 0; - var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade; + var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade[0]; userStoriesArray.forEach(function (story) { if (story.audience !== "for_connections" && story.audience !== "for_feed") { @@ -91,7 +91,7 @@ function notificationPollCallback(userStoriesArray) { shouldShowDot = totalNewStories > 0 || (totalStories > 0 && shouldShowDot); ui.messagesWaiting(shouldShowDot && !ui.isOpen); - if (totalStories > 0 && !ui.isOpen && !ui.notificationInitialCallbackMade) { + if (totalStories > 0 && !ui.isOpen && !ui.notificationInitialCallbackMade[0]) { message = "There " + (totalStories === 1 ? "is " : "are ") + totalStories + " event" + (totalStories === 1 ? "" : "s") + " to know about. " + "Open GOTO to see " + (totalStories === 1 ? "it" : "them") + "."; @@ -122,12 +122,12 @@ function startup() { sortOrder: 8, onOpened: gotoOpened, home: GOTO_QML_SOURCE, - notificationPollEndpoint: endpoint, - notificationPollTimeoutMs: 60000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false + notificationPollEndpoint: [endpoint], + notificationPollTimeoutMs: [60000], + notificationDataProcessPage: [notificationDataProcessPage], + notificationPollCallback: [notificationPollCallback], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty], + notificationPollCaresAboutSince: [false] }); } diff --git a/scripts/tutorials/createCow.js b/scripts/tutorials/createCow.js index 16498e0e8c..0f034ecefa 100644 --- a/scripts/tutorials/createCow.js +++ b/scripts/tutorials/createCow.js @@ -45,11 +45,7 @@ var cow = Entities.addEntity({ lifetime: 3600, shapeType: "box", script: SCRIPT_URL, - userData: JSON.stringify({ - grabbableKey: { - grabbable: true - } - }) + grab: { grabbable: true } }); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createFlashlight.js b/scripts/tutorials/createFlashlight.js index f3e1e72182..329be56af7 100644 --- a/scripts/tutorials/createFlashlight.js +++ b/scripts/tutorials/createFlashlight.js @@ -37,11 +37,6 @@ var flashlight = Entities.addEntity({ shapeType: 'box', lifetime: 3600, script: SCRIPT_URL, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true - } - }) }); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createGolfClub.js b/scripts/tutorials/createGolfClub.js index 21e60f26ef..4135b6680d 100644 --- a/scripts/tutorials/createGolfClub.js +++ b/scripts/tutorials/createGolfClub.js @@ -53,35 +53,34 @@ var golfClubProperties = { y: -5.0, z: 0 }, - userData: JSON.stringify({ - wearable: { - joints: { - LeftHand: [{ - x: -0.1631782054901123, - y: 0.44648152589797974, - z: 0.10100018978118896 - }, { - x: -0.9181621670722961, - y: -0.0772884339094162, - z: -0.3870723247528076, - w: -0.0343472845852375 - }], - RightHand: [{ - x: 0.16826771199703217, - y: 0.4757269620895386, - z: 0.07139724493026733 - }, { - x: -0.7976328134536743, - y: -0.0011603273451328278, - z: 0.6030101776123047, - w: -0.012610925361514091 - }] - } + grab: { + equippable: true, + equippableLeftPosition: { + x: -0.1631782054901123, + y: 0.44648152589797974, + z: 0.10100018978118896 + }, + equippableLeftRotation: { + x: -0.9181621670722961, + y: -0.0772884339094162, + z: -0.3870723247528076, + w: -0.0343472845852375 + }, + equippableRightPosition: { + x: 0.16826771199703217, + y: 0.4757269620895386, + z: 0.07139724493026733 + }, + equippableRightRotation: { + x: -0.7976328134536743, + y: -0.0011603273451328278, + z: 0.6030101776123047, + w: -0.012610925361514091 } - }) -} + } +}; var golfClub = Entities.addEntity(golfClubProperties); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createPingPongGun.js b/scripts/tutorials/createPingPongGun.js index c86a78e96d..927738f29e 100644 --- a/scripts/tutorials/createPingPongGun.js +++ b/scripts/tutorials/createPingPongGun.js @@ -37,35 +37,31 @@ var pingPongGunProperties = { }, lifetime: 3600, dynamic: true, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true + grab: { + equippable: true, + equippableLeftPosition: { + x: 0.09151676297187805, + y: 0.13639454543590546, + z: 0.09354984760284424 }, - wearable: { - joints: { - RightHand: [{ - x: 0.1177130937576294, - y: 0.12922893464565277, - z: 0.08307232707738876 - }, { - x: 0.4934672713279724, - y: 0.3605862259864807, - z: 0.6394805908203125, - w: -0.4664038419723511 - }], - LeftHand: [{ - x: 0.09151676297187805, - y: 0.13639454543590546, - z: 0.09354984760284424 - }, { - x: -0.19628101587295532, - y: 0.6418180465698242, - z: 0.2830369472503662, - w: 0.6851521730422974 - }] - } + equippableLeftRotation: { + x: -0.19628101587295532, + y: 0.6418180465698242, + z: 0.2830369472503662, + w: 0.6851521730422974 + }, + equippableRightPosition: { + x: 0.1177130937576294, + y: 0.12922893464565277, + z: 0.08307232707738876 + }, + equippableRightRotation: { + x: 0.4934672713279724, + y: 0.3605862259864807, + z: 0.6394805908203125, + w: -0.4664038419723511 } - }) + } } var pingPongGun = Entities.addEntity(pingPongGunProperties); diff --git a/scripts/tutorials/createPistol.js b/scripts/tutorials/createPistol.js index 8851f53d09..0912645c35 100644 --- a/scripts/tutorials/createPistol.js +++ b/scripts/tutorials/createPistol.js @@ -37,38 +37,34 @@ var pistolProperties = { restitution: 0, damping: 0.5, collisionSoundURL: COLLISION_SOUND_URL, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true + grab: { + equippable: true, + equippableLeftPosition: { + x: 0.1802254319190979, + y: 0.13442856073379517, + z: 0.08504903316497803 }, - wearable: { - joints: { - RightHand: [{ - x: 0.07079616189002991, - y: 0.20177987217903137, - z: 0.06374628841876984 - }, { - x: -0.5863648653030396, - y: -0.46007341146469116, - z: 0.46949487924575806, - w: -0.4733745753765106 - }], - LeftHand: [{ - x: 0.1802254319190979, - y: 0.13442856073379517, - z: 0.08504903316497803 - }, { - x: 0.2198076844215393, - y: -0.7377811074256897, - z: 0.2780133783817291, - w: 0.574519157409668 - }] - } + equippableLeftRotation: { + x: 0.2198076844215393, + y: -0.7377811074256897, + z: 0.2780133783817291, + w: 0.574519157409668 + }, + equippableRightPosition: { + x: 0.07079616189002991, + y: 0.20177987217903137, + z: 0.06374628841876984 + }, + equippableRightRotation: { + x: -0.5863648653030396, + y: -0.46007341146469116, + z: 0.46949487924575806, + w: -0.4733745753765106 } - }) + } }; var pistol = Entities.addEntity(pistolProperties); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createSwords.js b/scripts/tutorials/createSwords.js index 749df3c5ed..f375939dc8 100644 --- a/scripts/tutorials/createSwords.js +++ b/scripts/tutorials/createSwords.js @@ -29,11 +29,7 @@ var sword1 = Entities.addEntity({ angularDamping: 0, damping: 0, script: SCRIPT_URL, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:true - } - }) + grab: { grabbable: true } }); var sword2 = Entities.addEntity({ @@ -45,11 +41,7 @@ var sword2 = Entities.addEntity({ angularDamping: 0, damping: 0, script: SCRIPT_URL, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:true - } - }) + grab: { grabbable: true } }); Script.scriptEnding.connect(function scriptEnding() { diff --git a/scripts/tutorials/createTetherballStick.js b/scripts/tutorials/createTetherballStick.js index 1b5bc36932..b940762137 100644 --- a/scripts/tutorials/createTetherballStick.js +++ b/scripts/tutorials/createTetherballStick.js @@ -1,6 +1,6 @@ "use strict"; /* jslint vars: true, plusplus: true, forin: true*/ -/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */ +/* globals Script, Entities, MyAvatar, Vec3, Quat */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // createTetherballStick.js @@ -59,11 +59,7 @@ var ballID = Entities.addEntity({ restitution: BALL_RESTITUTION, dynamic: true, collidesWith: "static,dynamic,otherAvatar,", - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) + grab: { grabbable: false } }); var lineID = Entities.addEntity({ @@ -109,43 +105,38 @@ var STICK_PROPERTIES = { }, shapeType: 'box', lifetime: LIFETIME, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true, - ignoreIK: false + grab: { + grabbable: false, + grabFollowsController: false, + equippable: true, + equippableLeftPosition: { + x: -0.14998853206634521, + y: 0.17033983767032623, + z: 0.023199155926704407 }, - wearable: { - joints: { - RightHand: [{ - x: 0.15539926290512085, - y: 0.14493153989315033, - z: 0.023641478270292282 - }, { - x: 0.5481458902359009, - y: -0.4470711946487427, - z: -0.3148134648799896, - w: 0.6328644752502441 - }], - LeftHand: [{ - x: -0.14998853206634521, - y: 0.17033983767032623, - z: 0.023199155926704407 - }, - { - x: 0.6623835563659668, - y: -0.1671387255191803, - z: 0.7071226835250854, - w: 0.1823924481868744 - }] - } + equippableLeftRotation: { + x: 0.6623835563659668, + y: -0.1671387255191803, + z: 0.7071226835250854, + w: 0.1823924481868744 }, - ownerID: MyAvatar.sessionUUID, - ballID: ballID, - lineID: lineID, - actionID: actionID, - lifetime: LIFETIME, - maxDistanceBetweenBallAndStick: ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER - }) + equippableRightPosition: { + x: 0.15539926290512085, + y: 0.14493153989315033, + z: 0.023641478270292282 + }, + equippableRightRotation: { + x: 0.5481458902359009, + y: -0.4470711946487427, + z: -0.3148134648799896, + w: 0.6328644752502441 + } + }, + ownerID: MyAvatar.sessionUUID, + ballID: ballID, + lineID: lineID, + actionID: actionID, + maxDistanceBetweenBallAndStick: ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER }; Entities.addEntity(STICK_PROPERTIES); diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index dd964fb4e0..eab268f9bb 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -32,7 +32,7 @@ range: DEFAULT_RANGE, maxVolume: DEFAULT_VOLUME, disabled: true, - grabbableKey: { wantsTrigger: true }, + grab: { triggerable: true } }; var soundURL = ""; @@ -182,7 +182,7 @@ entity = entityID; _this = this; - var props = Entities.getEntityProperties(entity, [ "userData" ]); + var props = Entities.getEntityProperties(entity, [ "userData", "grab.triggerable" ]); var data = {}; if (props.userData) { data = JSON.parse(props.userData); @@ -194,14 +194,15 @@ changed = true; } } - if (!data.grabbableKey.wantsTrigger) { - data.grabbableKey.wantsTrigger = true; - changed = true; - } if (changed) { debugPrint("applying default values to userData"); Entities.editEntity(entity, { userData: JSON.stringify(data) }); } + + if (!props.grab.triggerable) { + Entities.editEntity(entity, { grab: { triggerable: true } }); + } + this._updateColor(data.disabled); this.updateSettings(); diff --git a/scripts/tutorials/entity_scripts/magneticBlock.js b/scripts/tutorials/entity_scripts/magneticBlock.js index 1ec5f2a6c6..2f073a5f41 100644 --- a/scripts/tutorials/entity_scripts/magneticBlock.js +++ b/scripts/tutorials/entity_scripts/magneticBlock.js @@ -52,32 +52,8 @@ It will behave as the constructor */ preload: function(id) { - /* - We will now override any existing userdata with the grabbable property. - Only retrieving userData - */ - var entityProperties = Entities.getEntityProperties(id, ['userData']); - var userData = { - grabbableKey: {} - }; - // Check if existing userData field exists. - if (entityProperties.userData && entityProperties.userData.length > 0) { - try { - userData = JSON.parse(entityProperties.userData); - if (!userData.grabbableKey) { - userData.grabbableKey = {}; // If by random change there is no grabbableKey in the userData. - } - } catch (e) { - // if user data is not valid json, we will simply overwrite it. - } - } - // Object must be triggerable inorder to bind releaseGrabEvent - userData.grabbableKey.grabbable = true; - // Apply the new properties to entity of id - Entities.editEntity(id, { - userData: JSON.stringify(userData) - }); + Entities.editEntity(id, { grab: { grabbable: true } }); Script.scriptEnding.connect(function() { Script.removeEventHandler(id, "releaseGrab", this.releaseGrab); }); diff --git a/scripts/tutorials/entity_scripts/springHold.js b/scripts/tutorials/entity_scripts/springHold.js index 059ea2cc6f..dbf0ceafeb 100644 --- a/scripts/tutorials/entity_scripts/springHold.js +++ b/scripts/tutorials/entity_scripts/springHold.js @@ -62,11 +62,7 @@ position: originalProps.position, shapeType: originalProps.shapeType, visible: true, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:false - } - }) + grab: { grabbable: false } }; _this.copy = Entities.addEntity(props); } diff --git a/scripts/tutorials/entity_scripts/touch.js b/scripts/tutorials/entity_scripts/touch.js index 1d0586b350..6e1974a4fd 100644 --- a/scripts/tutorials/entity_scripts/touch.js +++ b/scripts/tutorials/entity_scripts/touch.js @@ -142,11 +142,7 @@ position: originalProps.position, shapeType: originalProps.shapeType, visible: true, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:false - } - }) + grab: { grabbable: false } }; _this.copy = Entities.addEntity(props); } diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index 432f7444c4..c7d0425863 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -53,14 +53,10 @@ y: SIZE, z: SIZE }, - userData: JSON.stringify({ - grabbableKey: { - cloneable: true, - grabbable: true, - cloneLifetime: LIFETIME, - cloneLimit: 9999 - } - }), + grab: { grabbable: true }, + cloneable: true, + cloneLifetime: LIFETIME, + cloneLimit: 9999 position: Vec3.sum(MyAvatar.position, Vec3.sum(forwardOffset, forwardVector)), color: newColor(), script: SCRIPT_URL diff --git a/server-console/package-lock.json b/server-console/package-lock.json index 4f12f2fa00..e27c3815f6 100644 --- a/server-console/package-lock.json +++ b/server-console/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@types/node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.2.tgz", - "integrity": "sha512-A6Uv1anbsCvrRDtaUXS2xZ5tlzD+Kg7yMRlSLFDy3z0r7KlGXDzL14vELXIAgpk2aJbU3XeZZQRcEkLkowT92g==", + "version": "8.10.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.29.tgz", + "integrity": "sha512-zbteaWZ2mdduacm0byELwtRyhYE40aK+pAanQk415gr1eRuu67x7QGOLmn8jz5zI8LDK7d0WI/oT6r5Trz4rzQ==", "dev": true }, "abbrev": { @@ -21,10 +21,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, "always-tail": { @@ -32,7 +32,14 @@ "resolved": "https://registry.npmjs.org/always-tail/-/always-tail-0.2.0.tgz", "integrity": "sha1-M5sa9E1QJQqgeg6H7Mw6JOxET/4=", "requires": { - "debug": "0.7.4" + "debug": "~0.7.2" + }, + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "http://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" + } } }, "ansi-regex": { @@ -47,70 +54,48 @@ "dev": true }, "asar": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/asar/-/asar-0.14.2.tgz", - "integrity": "sha512-eKo4ywQDq9dC/0Pu6UJsX4PxNi5ZlC4/NQ1JORUW4xkMRrEWpoLPpkngmQ6K7ZkioVjE2ZafLMmHPAQKMO0BdA==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/asar/-/asar-0.14.3.tgz", + "integrity": "sha512-+hNnVVDmYbv05We/a9knj/98w171+A94A9DNHj+3kXUr3ENTQoSEcfbJRvBBRHyOh4vukBYWujmHvvaMmQoQbg==", "dev": true, "requires": { - "chromium-pickle-js": "0.2.0", - "commander": "2.9.0", - "cuint": "0.2.2", - "glob": "6.0.4", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "mksnapshot": "0.3.1", + "chromium-pickle-js": "^0.2.0", + "commander": "^2.9.0", + "cuint": "^0.2.1", + "glob": "^6.0.4", + "minimatch": "^3.0.3", + "mkdirp": "^0.5.0", + "mksnapshot": "^0.3.0", "tmp": "0.0.28" }, "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, "glob": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "dev": true, "requires": { - "inflight": "1.0.4", - "inherits": "2.0.1", - "minimatch": "3.0.4", - "once": "1.3.3", - "path-is-absolute": "1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.11" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } }, "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "asynckit": { "version": "0.4.0", @@ -129,9 +114,15 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "base64-js": { "version": "1.2.0", @@ -139,14 +130,23 @@ "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, "binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", "dev": true, "requires": { - "buffers": "0.1.1", - "chainsaw": "0.1.0" + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" } }, "bl": { @@ -154,7 +154,7 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", "requires": { - "readable-stream": "2.0.6" + "readable-stream": "~2.0.5" }, "dependencies": { "isarray": { @@ -167,20 +167,20 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.1", - "isarray": "1.0.0", - "process-nextick-args": "1.0.6", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" } } } }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", "dev": true }, "boolbase": { @@ -188,14 +188,44 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { - "hoek": "4.2.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", @@ -219,8 +249,8 @@ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" } }, "caseless": { @@ -234,19 +264,30 @@ "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", "dev": true, "requires": { - "traverse": "0.3.9" + "traverse": ">=0.3.0 <0.4" } }, "cheerio": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.19.0.tgz", - "integrity": "sha1-dy5wFfLuKZZQltcepBdbdas1SSU=", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", "requires": { - "css-select": "1.0.0", - "dom-serializer": "0.1.0", - "entities": "1.1.1", - "htmlparser2": "3.8.3", - "lodash": "3.10.1" + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" } }, "chromium-pickle-js": { @@ -260,9 +301,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "requires": { - "string-width": "1.0.1", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.0.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" } }, "co": { @@ -275,25 +316,22 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz", "integrity": "sha1-9psZLT99keOC5Lcb3bd4eGGasMY=", "requires": { - "number-is-nan": "1.0.0" + "number-is-nan": "^1.0.0" } }, "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", + "dev": true }, "compare-version": { "version": "0.1.2", @@ -308,34 +346,57 @@ "dev": true }, "concat-stream": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", - "integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "inherits": "2.0.1", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" }, "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.1", - "isarray": "1.0.0", - "process-nextick-args": "1.0.6", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" } } } @@ -345,39 +406,21 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.1" - } - } - } - }, "css-select": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.0.0.tgz", - "integrity": "sha1-sRIcpRhI3SZOIkTQWM7iVN7rRLA=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "requires": { - "boolbase": "1.0.0", - "css-what": "1.0.0", - "domutils": "1.4.3", - "nth-check": "1.0.1" + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" } }, "css-what": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-1.0.0.tgz", - "integrity": "sha1-18wt9FGAZm+Z0rFEYmOUaeAPc2w=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=" }, "cuint": { "version": "0.2.2", @@ -386,24 +429,27 @@ "dev": true }, "dashdash": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.13.0.tgz", - "integrity": "sha1-parm/Z2OFWYk6w3ZJZ6xK6JFOFo=", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } + "assert-plus": "^1.0.0" } }, "debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.0.1.tgz", + "integrity": "sha512-K23FHJ/Mt404FSlp6gSZCevIbTMLX0j3fmHhUEhQ3Wq0FMODW3+cUSoLdy1Gx4polAf4t/lphhmHH35BB8cLYw==", + "requires": { + "ms": "^2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } }, "decamelize": { "version": "1.2.0", @@ -416,19 +462,19 @@ "integrity": "sha1-rjvLfjTGWHmt/nfhnDD4ZgK0vbA=", "dev": true, "requires": { - "binary": "0.3.0", - "graceful-fs": "4.1.3", - "mkpath": "0.1.0", - "nopt": "3.0.6", - "q": "1.5.1", - "readable-stream": "1.1.14", + "binary": "^0.3.0", + "graceful-fs": "^4.1.3", + "mkpath": "^0.1.0", + "nopt": "^3.0.1", + "q": "^1.1.2", + "readable-stream": "^1.1.8", "touch": "0.0.3" } }, "deep-extend": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", - "integrity": "sha1-7+QRPQgIX05vlod1mBD4B0aeIlM=", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "delayed-stream": { @@ -441,8 +487,8 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" + "domelementtype": "~1.1.1", + "entities": "~1.1.1" }, "dependencies": { "domelementtype": { @@ -458,95 +504,109 @@ "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" }, "domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "domutils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", - "integrity": "sha1-CGVRN5bGswYDGFDhdVFrr4C3Km8=", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "requires": { - "domelementtype": "1.3.0" + "dom-serializer": "0", + "domelementtype": "1" } }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "0.1.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "electron": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-1.8.4.tgz", - "integrity": "sha512-2f1cx0G3riMFODXFftF5AHXy+oHfhpntZHTDN66Hxtl09gmEr42B3piNEod9MEmw72f75LX2JfeYceqq1PF8cA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-3.0.0.tgz", + "integrity": "sha512-QN9X5vYa4kzJKniwhXlJwioX9qw2fDehdqxN/00KCLz/qnOz/IHLAHGikFjRwfEF2xnkmHxf61F8wn2LePPXXQ==", "dev": true, "requires": { - "@types/node": "8.10.2", - "electron-download": "3.3.0", - "extract-zip": "1.5.0" + "@types/node": "^8.0.24", + "electron-download": "^4.1.0", + "extract-zip": "^1.0.3" } }, "electron-download": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz", - "integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", + "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", "dev": true, "requires": { - "debug": "2.6.9", - "fs-extra": "0.30.0", - "home-path": "1.0.5", - "minimist": "1.2.0", - "nugget": "2.0.1", - "path-exists": "2.1.0", - "rc": "1.1.6", - "semver": "5.5.0", - "sumchecker": "1.3.1" + "debug": "^3.0.0", + "env-paths": "^1.0.0", + "fs-extra": "^4.0.1", + "minimist": "^1.2.0", + "nugget": "^2.0.1", + "path-exists": "^3.0.0", + "rc": "^1.2.1", + "semver": "^5.4.1", + "sumchecker": "^2.0.2" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "2.2.3", - "klaw": "1.3.1", - "path-is-absolute": "1.0.0", - "rimraf": "2.6.2" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true, + "optional": true + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "sumchecker": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz", - "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=", - "dev": true, - "requires": { - "debug": "2.6.9", - "es6-promise": "4.2.4" - } + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true } } }, @@ -556,17 +616,17 @@ "integrity": "sha1-DboCXtM9DkW/j0DG6b487i+YbCg=" }, "electron-osx-sign": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.8.tgz", - "integrity": "sha1-8Ln63e2eHlTsNfqJh3tcbDTHvEA=", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz", + "integrity": "sha1-vk87ibKnWh3F8eckkIGrKSnKOiY=", "dev": true, "requires": { - "bluebird": "3.5.1", - "compare-version": "0.1.2", - "debug": "2.6.9", - "isbinaryfile": "3.0.2", - "minimist": "1.2.0", - "plist": "2.1.0" + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^2.1.0" }, "dependencies": { "debug": { @@ -577,89 +637,75 @@ "requires": { "ms": "2.0.0" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, "electron-packager": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-12.0.0.tgz", - "integrity": "sha1-uC0k14ovIUA7v9FmpbFWmJTVzQw=", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-12.1.2.tgz", + "integrity": "sha512-7UiTNquZqhQm+L0Oqn7bR/7Ry/7zGO/PKwFpSNqHbWxydoN2aNahKyWjOPhcxHCAz+C1uu+tdyRe7wEN0BaJsA==", "dev": true, "requires": { - "asar": "0.14.2", - "debug": "3.1.0", - "electron-download": "4.1.0", - "electron-osx-sign": "0.4.8", - "extract-zip": "1.5.0", - "fs-extra": "5.0.0", - "galactus": "0.2.0", - "get-package-info": "1.0.0", - "nodeify": "1.0.1", - "parse-author": "2.0.0", - "pify": "3.0.0", - "plist": "2.1.0", - "rcedit": "1.0.0", - "resolve": "1.5.0", - "sanitize-filename": "1.6.1", - "semver": "5.5.0", - "yargs-parser": "9.0.2" + "asar": "^0.14.0", + "debug": "^3.0.0", + "electron-download": "^4.1.1", + "electron-osx-sign": "^0.4.1", + "extract-zip": "^1.0.3", + "fs-extra": "^5.0.0", + "galactus": "^0.2.1", + "get-package-info": "^1.0.0", + "nodeify": "^1.0.1", + "parse-author": "^2.0.0", + "pify": "^3.0.0", + "plist": "^2.0.0", + "rcedit": "^1.0.0", + "resolve": "^1.1.6", + "sanitize-filename": "^1.6.0", + "semver": "^5.3.0", + "yargs-parser": "^10.0.0" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "electron-download": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.0.tgz", - "integrity": "sha1-v5MsdG8vh//MCdHdRy8v9rkYeEU=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", + "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", "dev": true, "requires": { - "debug": "2.6.9", - "env-paths": "1.0.0", - "fs-extra": "2.1.2", - "minimist": "1.2.0", - "nugget": "2.0.1", - "path-exists": "3.0.0", - "rc": "1.1.6", - "semver": "5.5.0", - "sumchecker": "2.0.2" + "debug": "^3.0.0", + "env-paths": "^1.0.0", + "fs-extra": "^4.0.1", + "minimist": "^1.2.0", + "nugget": "^2.0.1", + "path-exists": "^3.0.0", + "rc": "^1.2.1", + "semver": "^5.4.1", + "sumchecker": "^2.0.2" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "fs-extra": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", - "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "2.2.3" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } } } @@ -670,63 +716,35 @@ "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "4.0.0", - "universalify": "0.1.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" }, "dependencies": { - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true, - "requires": { - "graceful-fs": "4.1.11" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "optional": true - } - } + "optional": true } } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "nugget": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", - "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", - "dev": true, - "requires": { - "debug": "2.6.9", - "minimist": "1.2.0", - "pretty-bytes": "1.0.4", - "progress-stream": "1.2.0", - "request": "2.85.0", - "single-line-log": "1.1.2", - "throttleit": "0.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -739,41 +757,29 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, - "rcedit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-1.0.0.tgz", - "integrity": "sha512-W7DNa34x/3OgWyDHsI172AG/Lr/lZ+PkavFkHj0QhhkBRcV9QTmRJE1tDKrWkx8XHPSBsmZkNv9OKue6pncLFQ==", - "dev": true + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, - "single-line-log": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", - "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", - "dev": true, - "requires": { - "string-width": "1.0.1" - } - }, - "throttleit": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", - "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true - }, - "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - } } } }, @@ -782,7 +788,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", "integrity": "sha1-6TUyWLqpEIll78QcsO+K3i88+wc=", "requires": { - "once": "1.3.3" + "once": "~1.3.0" } }, "entities": { @@ -802,58 +808,46 @@ "integrity": "sha1-5ntD8+gsluo6WE/+4Ln8MyXYAtk=", "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, - "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", - "dev": true - }, "extend": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=" }, "extract-zip": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz", - "integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", "dev": true, "requires": { - "concat-stream": "1.5.0", - "debug": "0.7.4", - "mkdirp": "0.5.0", + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", "yauzl": "2.4.1" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "minimist": "0.0.8" + "ms": "2.0.0" } } } }, "extsprintf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" }, "fast-json-stable-stringify": { "version": "2.0.0", @@ -866,7 +860,7 @@ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "pend": "1.2.0" + "pend": "~1.2.0" } }, "find-up": { @@ -875,8 +869,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "dependencies": { "path-exists": { @@ -885,28 +879,28 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } } } }, "flora-colossus": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-0.0.2.tgz", - "integrity": "sha1-fRvimh8X+k8isb1hSC+Gw04HuQE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-1.0.0.tgz", + "integrity": "sha1-VHKcNh7ezuAU3UQWeeGjfB13OkU=", "dev": true, "requires": { - "debug": "3.1.0", - "fs-extra": "4.0.3" + "debug": "^3.1.0", + "fs-extra": "^4.0.0" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "fs-extra": { @@ -915,9 +909,9 @@ "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "4.0.0", - "universalify": "0.1.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "jsonfile": { @@ -926,7 +920,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" }, "dependencies": { "graceful-fs": { @@ -939,9 +933,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } } @@ -956,9 +950,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "mime-types": "^2.1.12" }, "dependencies": { "combined-stream": { @@ -966,19 +960,37 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } } } }, "fs-extra": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", - "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "2.2.3", - "klaw": "1.3.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "dependencies": { + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "optional": true + } + } + } } }, "fs.realpath": { @@ -988,23 +1000,23 @@ "dev": true }, "galactus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/galactus/-/galactus-0.2.0.tgz", - "integrity": "sha1-w9Y7pVAkZv5A6mfMaJCFs90kqPw=", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-0.2.1.tgz", + "integrity": "sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk=", "dev": true, "requires": { - "debug": "3.1.0", - "flora-colossus": "0.0.2", - "fs-extra": "4.0.3" + "debug": "^3.1.0", + "flora-colossus": "^1.0.0", + "fs-extra": "^4.0.0" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "fs-extra": { @@ -1013,9 +1025,9 @@ "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "4.0.0", - "universalify": "0.1.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "jsonfile": { @@ -1024,7 +1036,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" }, "dependencies": { "graceful-fs": { @@ -1037,9 +1049,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } } @@ -1050,10 +1062,10 @@ "integrity": "sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw=", "dev": true, "requires": { - "bluebird": "3.5.1", - "debug": "2.6.9", - "lodash.get": "4.4.2", - "read-pkg-up": "2.0.0" + "bluebird": "^3.1.1", + "debug": "^2.2.0", + "lodash.get": "^4.0.0", + "read-pkg-up": "^2.0.0" }, "dependencies": { "debug": { @@ -1071,7 +1083,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "load-json-file": { @@ -1080,25 +1092,19 @@ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { - "pify": "2.3.0" + "pify": "^2.0.0" } }, "read-pkg": { @@ -1107,9 +1113,9 @@ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.3.5", - "path-type": "2.0.0" + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" } }, "read-pkg-up": { @@ -1118,8 +1124,8 @@ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" } }, "strip-bom": { @@ -1136,18 +1142,26 @@ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.4", - "inherits": "2.0.1", - "minimatch": "3.0.4", - "once": "1.3.3", - "path-is-absolute": "1.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "dependencies": { "balanced-match": { @@ -1162,7 +1176,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -1172,7 +1186,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } } } @@ -1182,12 +1196,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.3.tgz", "integrity": "sha1-kgM84RETxB4mKNYf36QLwQ3AFVw=" }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -1199,36 +1207,14 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.3.0", + "har-schema": "^2.0.0" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.1.0" - } - }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" - }, - "home-path": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.5.tgz", - "integrity": "sha1-eIspgVsS1Tus9XVkhHbm+QQdEz8=", - "dev": true - }, "hosted-git-info": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.1.4.tgz", @@ -1236,30 +1222,56 @@ "dev": true }, "htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.3.0", - "domutils": "1.5.1", - "entities": "1.0.0", - "readable-stream": "1.1.14" + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" }, "dependencies": { - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } } }, - "entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, @@ -1268,16 +1280,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.2.2", - "sshpk": "1.7.4" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "indent-string": { @@ -1286,7 +1291,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" }, "dependencies": { "repeating": { @@ -1295,7 +1300,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.1" + "is-finite": "^1.0.0" } } } @@ -1306,8 +1311,8 @@ "integrity": "sha1-bLtFIevVHODsCpNr/XZX736bFyo=", "dev": true, "requires": { - "once": "1.3.3", - "wrappy": "1.0.1" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -1338,7 +1343,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-finite": { @@ -1347,7 +1352,7 @@ "integrity": "sha1-ZDhgPq6+J5OUj/SkJi7I2z1iWXs=", "dev": true, "requires": { - "number-is-nan": "1.0.0" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -1355,7 +1360,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "1.0.0" + "number-is-nan": "^1.0.0" } }, "is-promise": { @@ -1378,13 +1383,17 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true }, "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } }, "isexe": { "version": "2.0.0", @@ -1396,25 +1405,16 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "jodid25519": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "optional": true, - "requires": { - "jsbn": "0.1.0" - } - }, "jsbn": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz", - "integrity": "sha1-ZQmH2g3XT06/WhE3eiqi0nPpff0=", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, "json-schema": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz", - "integrity": "sha1-UDVPGfYDkXxpX3C4Wvp3w7DyNQY=" + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", @@ -1429,30 +1429,34 @@ "jsonfile": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz", - "integrity": "sha1-4lK5mmr5AdPsQfMyWJyQUJp7xgU=" + "integrity": "sha1-4lK5mmr5AdPsQfMyWJyQUJp7xgU=", + "dev": true }, "jsprim": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz", - "integrity": "sha1-8gyQaskqvVjjt5rIvHCkiDJRLaE=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "requires": { - "extsprintf": "1.0.2", - "json-schema": "0.2.2", - "verror": "1.3.6" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" } }, "klaw": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.9" }, "dependencies": { "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true, "optional": true } } @@ -1462,7 +1466,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "load-json-file": { @@ -1471,11 +1475,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, "locate-path": { @@ -1484,8 +1488,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, "dependencies": { "path-exists": { @@ -1496,10 +1500,35 @@ } } }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + "lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" }, "lodash.get": { "version": "4.4.2", @@ -1507,14 +1536,44 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + }, + "lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + }, "loud-rejection": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.3.0.tgz", "integrity": "sha1-8omjkvF9K6rPGU0KZzAEOUQzsRU=", "dev": true, "requires": { - "array-find-index": "1.0.1", - "signal-exit": "2.1.2" + "array-find-index": "^1.0.0", + "signal-exit": "^2.1.2" } }, "map-obj": { @@ -1529,29 +1588,38 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.3.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.3.5", - "object-assign": "4.0.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" } }, "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" }, "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.36.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -1589,220 +1657,21 @@ "requires": { "decompress-zip": "0.3.0", "fs-extra": "0.26.7", - "request": "2.83.0" + "request": "^2.79.0" }, "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, "fs-extra": { "version": "0.26.7", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "jsonfile": "2.2.3", - "klaw": "1.3.1", - "path-is-absolute": "1.0.0", - "rimraf": "2.6.2" + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" } - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" - } - }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" - } - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.2.2", - "sshpk": "1.7.4" - } - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "dev": true, - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "dev": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true } } }, @@ -1817,10 +1686,10 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz", "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==", "requires": { - "growly": "1.3.0", - "semver": "5.5.0", - "shellwords": "0.1.1", - "which": "1.3.0" + "growly": "^1.3.0", + "semver": "^5.4.1", + "shellwords": "^0.1.1", + "which": "^1.3.0" }, "dependencies": { "semver": { @@ -1836,8 +1705,8 @@ "integrity": "sha1-ZKtpp7268DzhB7TwM1yHwLnpGx0=", "dev": true, "requires": { - "is-promise": "1.0.1", - "promise": "1.3.0" + "is-promise": "~1.0.0", + "promise": "~1.3.0" } }, "nopt": { @@ -1846,7 +1715,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1.1.1" + "abbrev": "1" } }, "normalize-package-data": { @@ -1855,10 +1724,10 @@ "integrity": "sha1-jZJPFClg4Xd+f/4XBUNjHMfLAt8=", "dev": true, "requires": { - "hosted-git-info": "2.1.4", - "is-builtin-module": "1.0.0", - "semver": "5.1.0", - "validate-npm-package-license": "3.0.1" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "nth-check": { @@ -1866,7 +1735,7 @@ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "requires": { - "boolbase": "1.0.0" + "boolbase": "~1.0.0" } }, "nugget": { @@ -1875,12 +1744,12 @@ "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", "dev": true, "requires": { - "debug": "2.6.9", - "minimist": "1.2.0", - "pretty-bytes": "1.0.4", - "progress-stream": "1.2.0", - "request": "2.85.0", - "single-line-log": "1.1.2", + "debug": "^2.1.3", + "minimist": "^1.1.0", + "pretty-bytes": "^1.0.2", + "progress-stream": "^1.1.0", + "request": "^2.45.0", + "single-line-log": "^1.1.2", "throttleit": "0.0.2" }, "dependencies": { @@ -1907,9 +1776,9 @@ "integrity": "sha1-wCD1KcUoKt/dIz2R1LGBw9aG3Es=" }, "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.0.1", @@ -1928,7 +1797,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "requires": { - "wrappy": "1.0.1" + "wrappy": "1" } }, "os-homedir": { @@ -1941,7 +1810,7 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { - "lcid": "1.0.0" + "lcid": "^1.0.0" } }, "os-tmpdir": { @@ -1951,12 +1820,12 @@ "dev": true }, "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-try": "1.0.0" + "p-try": "^1.0.0" } }, "p-locate": { @@ -1965,7 +1834,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.2.0" + "p-limit": "^1.1.0" } }, "p-try": { @@ -1980,7 +1849,7 @@ "integrity": "sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=", "dev": true, "requires": { - "author-regex": "1.0.0" + "author-regex": "^1.0.0" } }, "parse-json": { @@ -1989,17 +1858,14 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "1.3.0" + "error-ex": "^1.2.0" } }, "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.0", @@ -2008,9 +1874,9 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-type": { @@ -2019,9 +1885,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.3", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "pend": { @@ -2053,7 +1919,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "plist": { @@ -2064,7 +1930,7 @@ "requires": { "base64-js": "1.2.0", "xmlbuilder": "8.2.2", - "xmldom": "0.1.27" + "xmldom": "0.1.x" } }, "pretty-bytes": { @@ -2073,8 +1939,8 @@ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", "dev": true, "requires": { - "get-stdin": "4.0.1", - "meow": "3.7.0" + "get-stdin": "^4.0.1", + "meow": "^3.1.0" } }, "process-nextick-args": { @@ -2088,8 +1954,8 @@ "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", "dev": true, "requires": { - "speedometer": "0.1.4", - "through2": "0.2.3" + "speedometer": "~0.1.2", + "through2": "~0.2.3" } }, "promise": { @@ -2098,16 +1964,21 @@ "integrity": "sha1-5cyaTIJ45GZP/twBx9qEhCsEAXU=", "dev": true, "requires": { - "is-promise": "1.0.1" + "is-promise": "~1" } }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, "pump": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", "integrity": "sha1-8fFAn7m9EIW721drQ7hOxLXq3Bo=", "requires": { - "end-of-stream": "1.1.0", - "once": "1.3.3" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "punycode": { @@ -2122,31 +1993,37 @@ "dev": true }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "rc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", - "integrity": "sha1-Q2UbdrauU7XIAvEVH6P8OwWZack=", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { - "deep-extend": "0.4.1", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "1.0.4" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" } }, + "rcedit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-1.1.0.tgz", + "integrity": "sha512-JkXJ0IrUcdupLoIx6gE4YcFaMVSGtu7kQf4NJoDJUnfBZGuATmJ2Yal2v55KTltp+WV8dGr7A0RtOzx6jmtM6Q==", + "dev": true + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.3.5", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -2155,19 +2032,20 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" } }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.1", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "redent": { @@ -2176,43 +2054,41 @@ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" } }, "request": { - "version": "2.85.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", - "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.7.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "dependencies": { "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" } } }, @@ -2221,16 +2097,16 @@ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-1.0.2.tgz", "integrity": "sha1-XUBvCBMJ32G0qKqDzVc032Pxi/U=", "requires": { - "throttleit": "1.0.0" + "throttleit": "^1.0.0" } }, "resolve": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.5" } }, "rimraf": { @@ -2239,13 +2115,18 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sanitize-filename": { "version": "1.6.1", @@ -2253,7 +2134,7 @@ "integrity": "sha1-YS2hyWRz+gLczaktzVtKsWSmdyo=", "dev": true, "requires": { - "truncate-utf8-bytes": "1.0.2" + "truncate-utf8-bytes": "^1.0.0" } }, "semver": { @@ -2279,15 +2160,7 @@ "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", "dev": true, "requires": { - "string-width": "1.0.1" - } - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "requires": { - "hoek": "4.2.1" + "string-width": "^1.0.1" } }, "spdx-correct": { @@ -2296,7 +2169,7 @@ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", "dev": true, "requires": { - "spdx-license-ids": "1.2.1" + "spdx-license-ids": "^1.0.2" } }, "spdx-exceptions": { @@ -2311,8 +2184,8 @@ "integrity": "sha1-1SsUtelnB3FECvIlvLVjEirEUvY=", "dev": true, "requires": { - "spdx-exceptions": "1.0.4", - "spdx-license-ids": "1.2.1" + "spdx-exceptions": "^1.0.4", + "spdx-license-ids": "^1.0.0" } }, "spdx-license-ids": { @@ -2328,17 +2201,19 @@ "dev": true }, "sshpk": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.7.4.tgz", - "integrity": "sha1-rXtH3vymHIQV2WQkO2KwzmD7yjg=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { - "asn1": "0.2.3", - "assert-plus": "0.2.0", - "dashdash": "1.13.0", - "ecc-jsbn": "0.1.1", - "jodid25519": "1.0.2", - "jsbn": "0.1.0", - "tweetnacl": "0.14.3" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" } }, "string-width": { @@ -2346,9 +2221,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz", "integrity": "sha1-ySEptvHX9SrPmvQkom44ZKBc6wo=", "requires": { - "code-point-at": "1.0.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -2356,17 +2231,12 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.0.0" + "ansi-regex": "^2.0.0" } }, "strip-bom": { @@ -2375,7 +2245,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } }, "strip-indent": { @@ -2384,13 +2254,13 @@ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "get-stdin": "4.0.1" + "get-stdin": "^4.0.1" } }, "strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "sumchecker": { @@ -2399,7 +2269,7 @@ "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", "dev": true, "requires": { - "debug": "2.6.9" + "debug": "^2.2.0" }, "dependencies": { "debug": { @@ -2410,12 +2280,6 @@ "requires": { "ms": "2.0.0" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, @@ -2424,9 +2288,9 @@ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz", "integrity": "sha1-pqgFU9ilTHPeHQrg553ncDVgXh0=", "requires": { - "mkdirp": "0.5.1", - "pump": "1.0.1", - "tar-stream": "1.5.1" + "mkdirp": "^0.5.0", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" } }, "tar-stream": { @@ -2434,10 +2298,10 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.1.tgz", "integrity": "sha1-UWx00b6j4THMC5NIkpyag/CirRE=", "requires": { - "bl": "1.1.2", - "end-of-stream": "1.1.0", - "readable-stream": "2.0.6", - "xtend": "4.0.1" + "bl": "^1.0.0", + "end-of-stream": "^1.0.0", + "readable-stream": "^2.0.0", + "xtend": "^4.0.0" }, "dependencies": { "isarray": { @@ -2450,12 +2314,12 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.1", - "isarray": "1.0.0", - "process-nextick-args": "1.0.6", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" } } } @@ -2471,8 +2335,8 @@ "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", "dev": true, "requires": { - "readable-stream": "1.1.14", - "xtend": "2.1.2" + "readable-stream": "~1.1.9", + "xtend": "~2.1.1" }, "dependencies": { "xtend": { @@ -2481,7 +2345,7 @@ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", "dev": true, "requires": { - "object-keys": "0.4.0" + "object-keys": "~0.4.0" } } } @@ -2492,7 +2356,7 @@ "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.1" } }, "touch": { @@ -2501,7 +2365,7 @@ "integrity": "sha1-Ua7z1ElXHU8oel2Hyci0kYGg2x0=", "dev": true, "requires": { - "nopt": "1.0.10" + "nopt": "~1.0.10" }, "dependencies": { "nopt": { @@ -2510,17 +2374,18 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1.1.1" + "abbrev": "1" } } } }, "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "punycode": "1.4.1" + "psl": "^1.1.24", + "punycode": "^1.4.1" } }, "traverse": { @@ -2541,7 +2406,7 @@ "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", "dev": true, "requires": { - "utf8-byte-length": "1.0.4" + "utf8-byte-length": "^1.0.1" } }, "tunnel-agent": { @@ -2549,13 +2414,13 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.3.tgz", - "integrity": "sha1-PaOC9nDyXe1417PReSEZvKC3Ey0=", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, "typedarray": { @@ -2567,8 +2432,7 @@ "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", - "dev": true + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" }, "utf8-byte-length": { "version": "1.0.4", @@ -2582,9 +2446,9 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "validate-npm-package-license": { "version": "3.0.1", @@ -2592,16 +2456,18 @@ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "dev": true, "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.2" + "spdx-correct": "~1.0.0", + "spdx-expression-parse": "~1.0.0" } }, "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "extsprintf": "1.0.2" + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" } }, "which": { @@ -2609,7 +2475,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "window-size": { @@ -2622,7 +2488,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.0.0.tgz", "integrity": "sha1-fTD4+HP5pbvDpk2ryNF34HGuQm8=", "requires": { - "string-width": "1.0.1" + "string-width": "^1.0.1" } }, "wrappy": { @@ -2657,13 +2523,30 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "requires": { - "camelcase": "2.1.1", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "os-locale": "1.4.0", - "string-width": "1.0.1", - "window-size": "0.1.4", - "y18n": "3.2.1" + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } } }, "yauzl": { @@ -2672,7 +2555,7 @@ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", "dev": true, "requires": { - "fd-slicer": "1.0.1" + "fd-slicer": "~1.0.1" } } } diff --git a/server-console/package.json b/server-console/package.json index b0d181c65a..6824d1d9cf 100644 --- a/server-console/package.json +++ b/server-console/package.json @@ -8,8 +8,8 @@ "" ], "devDependencies": { - "electron-packager": "^12.0.0", - "electron": "1.8.4" + "electron": "^3.0.0", + "electron-packager": "^12.1.2" }, "repository": { "type": "git", @@ -23,14 +23,15 @@ "packager": "node packager.js" }, "dependencies": { - "always-tail": "0.2.0", - "cheerio": "^0.19.0", + "always-tail": "^0.2.0", + "cheerio": "^0.22.0", + "debug": "^4.0.1", "electron-log": "1.1.1", "extend": "^3.0.0", "fs-extra": "^6.0.0", "node-notifier": "^5.2.1", "os-homedir": "^1.0.1", - "request": "^2.85.0", + "request": "^2.88.0", "request-progress": "1.0.2", "tar-fs": "^1.12.0", "yargs": "^3.30.0" diff --git a/server-console/resources/console-notification.png b/server-console/resources/console-notification.png index 0fd22b8900..d506a3d2e8 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 3255d43f60..53589b60b0 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 6a75dad5bf..5356afb93e 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/src/main.js b/server-console/src/main.js index c26938745b..dc3fbd4333 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -247,15 +247,12 @@ process.on('uncaughtException', function(err) { log.error(err.stack); }); -var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { - // Someone tried to run a second instance, focus the window (if there is one) - return true; -}); +const gotTheLock = app.requestSingleInstanceLock() -if (shouldQuit) { - log.warn("Another instance of the Sandbox is already running - this instance will quit."); - app.exit(0); - return; +if (!gotTheLock) { + log.warn("Another instance of the Sandbox is already running - this instance will quit."); + app.exit(0); + return; } // Check command line arguments to see how to find binaries @@ -338,13 +335,15 @@ const HifiNotificationType = hfNotifications.NotificationType; var pendingNotifications = {} var notificationState = NotificationState.UNNOTIFIED; -function setNotificationState (notificationType, pending = true) { - pendingNotifications[notificationType] = pending; - notificationState = NotificationState.UNNOTIFIED; - for (var key in pendingNotifications) { - if (pendingNotifications[key]) { - notificationState = NotificationState.NOTIFIED; - break; +function setNotificationState (notificationType, pending = undefined) { + if (pending !== undefined) { + pendingNotifications[notificationType] = pending; + notificationState = NotificationState.UNNOTIFIED; + for (var key in pendingNotifications) { + if (pendingNotifications[key]) { + notificationState = NotificationState.NOTIFIED; + break; + } } } updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); @@ -568,7 +567,42 @@ function updateLabels(serverState) { labels.people.icon = pendingNotifications[HifiNotificationType.PEOPLE] ? menuNotificationIcon : null; labels.wallet.icon = pendingNotifications[HifiNotificationType.WALLET] ? menuNotificationIcon : null; labels.marketplace.icon = pendingNotifications[HifiNotificationType.MARKETPLACE] ? menuNotificationIcon : null; - + var onlineUsers = trayNotifications.getOnlineUsers(); + delete labels.people.submenu; + if (onlineUsers) { + for (var name in onlineUsers) { + if(labels.people.submenu == undefined) { + labels.people.submenu = []; + } + labels.people.submenu.push({ + label: name, + enabled: (onlineUsers[name].location != undefined), + click: function (item) { + setNotificationState(HifiNotificationType.PEOPLE, false); + if(onlineUsers[item.label] && onlineUsers[item.label].location) { + StartInterface("hifi://" + onlineUsers[item.label].location.root.name + onlineUsers[item.label].location.path); + } + } + }); + } + } + var currentStories = trayNotifications.getCurrentStories(); + delete labels.goto.submenu; + if (currentStories) { + for (var location in currentStories) { + if(labels.goto.submenu == undefined) { + labels.goto.submenu = []; + } + labels.goto.submenu.push({ + label: "event in " + location, + location: location, + click: function (item) { + setNotificationState(HifiNotificationType.GOTO, false); + StartInterface("hifi://" + item.location + currentStories[item.location].path); + } + }); + } + } } function updateTrayMenu(serverState) { @@ -840,10 +874,6 @@ function onContentLoaded() { hasShownUpdateNotification = true; } }); - notifier.on('click', function(notifierObject, options) { - log.debug("Got click", options.url); - shell.openExternal(options.url); - }); } deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); @@ -919,6 +949,8 @@ app.on('ready', function() { trayNotifications.startPolling(); } updateTrayMenu(ProcessGroupStates.STOPPED); - - maybeInstallDefaultContentSet(onContentLoaded); + + if (isServerInstalled()) { + maybeInstallDefaultContentSet(onContentLoaded); + } }); diff --git a/server-console/src/modules/hf-notifications.js b/server-console/src/modules/hf-notifications.js index 464d268c5e..8a812625b4 100644 --- a/server-console/src/modules/hf-notifications.js +++ b/server-console/src/modules/hf-notifications.js @@ -5,6 +5,8 @@ const process = require('process'); const hfApp = require('./hf-app'); const path = require('path'); const AccountInfo = require('./hf-acctinfo').AccountInfo; +const url = require('url'); +const shell = require('electron').shell; const GetBuildInfo = hfApp.getBuildInfo; const buildInfo = GetBuildInfo(); const osType = os.type(); @@ -73,11 +75,17 @@ HifiNotification.prototype = { text = this.data + " of your connections are online." } message = "Click to open PEOPLE."; - url="hifiapp:PEOPLE" + url="hifiapp:PEOPLE"; } else { - text = this.data.username + " is available in " + this.data.location.root.name + "."; - message = "Click to join them."; - url="hifi://" + this.data.location.root.name + this.data.location.path; + if (this.data.location) { + text = this.data.username + " is available in " + this.data.location.root.name + "."; + message = "Click to join them."; + url="hifi://" + this.data.location.root.name + this.data.location.path; + } else { + text = this.data.username + " is online."; + message = "Click to open PEOPLE."; + url="hifiapp:PEOPLE"; + } } break; @@ -136,7 +144,8 @@ HifiNotification.prototype = { function HifiNotifications(config, menuNotificationCallback) { this.config = config; this.menuNotificationCallback = menuNotificationCallback; - this.onlineUsers = new Set([]); + this.onlineUsers = {}; + this.currentStories = {}; this.storiesSince = new Date(this.config.get("storiesNotifySince", "1970-01-01T00:00:00.000Z")); this.peopleSince = new Date(this.config.get("peopleNotifySince", "1970-01-01T00:00:00.000Z")); this.walletSince = new Date(this.config.get("walletNotifySince", "1970-01-01T00:00:00.000Z")); @@ -147,8 +156,13 @@ function HifiNotifications(config, menuNotificationCallback) { var _menuNotificationCallback = menuNotificationCallback; notifier.on('click', function (notifierObject, options) { - StartInterface(options.url); - _menuNotificationCallback(options.notificationType, false); + const optUrl = url.parse(options.url); + if ((optUrl.protocol === "hifi:") || (optUrl.protocol === "hifiapp:")) { + StartInterface(options.url); + _menuNotificationCallback(options.notificationType, false); + } else { + shell.openExternal(options.url); + } }); } @@ -213,6 +227,12 @@ HifiNotifications.prototype = { clearInterval(this.marketplacePollTimer); } }, + getOnlineUsers: function () { + return this.onlineUsers; + }, + getCurrentStories: function () { + return this.currentStories; + }, _showNotification: function () { var _this = this; @@ -225,7 +245,7 @@ HifiNotifications.prototype = { // previous notification immediately when a // new one is submitted _this.pendingNotifications.shift(); - if(_this.pendingNotifications.length > 0) { + if (_this.pendingNotifications.length > 0) { _this._showNotification(); } }); @@ -289,7 +309,6 @@ HifiNotifications.prototype = { finished(false); return; } - console.log(content); if (!content.total_entries) { finished(true, token); return; @@ -313,7 +332,6 @@ HifiNotifications.prototype = { notifyData = content.data.updates; break; } - notifyData.forEach(function (notifyDataEntry) { _this._addNotification(new HifiNotification(notifyType, notifyDataEntry)); }); @@ -346,7 +364,7 @@ HifiNotifications.prototype = { 'include_actions=announcement', 'restriction=open,hifi', 'require_online=true', - 'per_page=1' + 'per_page=' + MAX_NOTIFICATION_ITEMS ]; var url = METAVERSE_SERVER_URL + STORIES_URL + '?' + options.join('&'); // call a second time to determine if there are no more stories and we should @@ -357,7 +375,34 @@ HifiNotifications.prototype = { 'bearer': token } }, function (error, data) { - _this._pollToDisableHighlight(NotificationType.GOTO, error, data); + if (error || !data.body) { + console.log("Error: unable to get " + url); + finished(false); + return; + } + var content = JSON.parse(data.body); + if (!content || content.status != 'success') { + console.log("Error: unable to get " + url); + finished(false); + return; + } + + if (!content.total_entries) { + finished(true, token); + return; + } + if (!content.total_entries) { + _this.menuNotificationCallback(NotificationType.GOTO, false); + } + _this.currentStories = {}; + content.user_stories.forEach(function (story) { + // only show a single instance of each story location + // in the menu. This may cause issues with domains + // where the story locations are significantly different + // for each story. + _this.currentStories[story.place_name] = story; + }); + _this.menuNotificationCallback(NotificationType.GOTO); }); } }); @@ -400,20 +445,19 @@ HifiNotifications.prototype = { console.log("Error: unable to get " + url); return false; } - console.log(content); if (!content.total_entries) { _this.menuNotificationCallback(NotificationType.PEOPLE, false); - _this.onlineUsers = new Set([]); + _this.onlineUsers = {}; return; } - var currentUsers = new Set([]); + var currentUsers = {}; var newUsers = new Set([]); content.data.users.forEach(function (user) { - currentUsers.add(user.username); - if (!_this.onlineUsers.has(user.username)) { + currentUsers[user.username] = user; + if (!(user.username in _this.onlineUsers)) { newUsers.add(user); - _this.onlineUsers.add(user.username); + _this.onlineUsers[user.username] = user; } }); _this.onlineUsers = currentUsers; diff --git a/tests-manual/controllers/CMakeLists.txt b/tests-manual/controllers/CMakeLists.txt index ce1c150ed4..03043c79f2 100644 --- a/tests-manual/controllers/CMakeLists.txt +++ b/tests-manual/controllers/CMakeLists.txt @@ -11,14 +11,8 @@ setup_memory_debugger() # link in the shared libraries link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers) - -if (WIN32) - add_dependency_external_projects(OpenVR) - find_package(OpenVR REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) -endif() if (WIN32) + target_openvr() target_link_libraries(${TARGET_NAME} Winmm.lib) add_dependency_external_projects(wasapi) endif() diff --git a/tests-manual/gpu-textures/CMakeLists.txt b/tests-manual/gpu-textures/CMakeLists.txt index 84f5027411..907690748a 100644 --- a/tests-manual/gpu-textures/CMakeLists.txt +++ b/tests-manual/gpu-textures/CMakeLists.txt @@ -4,7 +4,7 @@ setup_hifi_project(Quick Gui Script) setup_memory_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") link_hifi_libraries( - shared task networking gl + shared shaders task networking gl ktx gpu octree ${PLATFORM_GL_BACKEND} ) diff --git a/tests-manual/gpu-textures/src/TestTextures.cpp b/tests-manual/gpu-textures/src/TestTextures.cpp index 701e60fab8..5d5ddce6fa 100644 --- a/tests-manual/gpu-textures/src/TestTextures.cpp +++ b/tests-manual/gpu-textures/src/TestTextures.cpp @@ -81,8 +81,10 @@ TexturesTest::TexturesTest() { connect(&stats, &TextureTestStats::prevTexture, this, &TexturesTest::onPrevTexture); connect(&stats, &TextureTestStats::maxTextureMemory, this, &TexturesTest::onMaxTextureMemory); { - auto VS = gpu::Shader::createVertex({ vertexShaderSource, {} }); - auto PS = gpu::Shader::createPixel({ fragmentShaderSource, {} }); + shader::Source vertexSource; + + auto VS = gpu::Shader::createVertex(shader::Source::generate(vertexShaderSource)); + auto PS = gpu::Shader::createPixel(shader::Source::generate(fragmentShaderSource)); auto program = gpu::Shader::createProgram(VS, PS); // If the pipeline did not exist, make it auto state = std::make_shared(); diff --git a/tests-manual/gpu/CMakeLists.txt b/tests-manual/gpu/CMakeLists.txt index 30218f3f97..8fd0316c05 100644 --- a/tests-manual/gpu/CMakeLists.txt +++ b/tests-manual/gpu/CMakeLists.txt @@ -5,7 +5,7 @@ setup_memory_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") link_hifi_libraries( shared task networking gl - ktx gpu procedural octree image + ktx shaders gpu procedural octree image graphics model-networking fbx animation script-engine render render-utils ${PLATFORM_GL_BACKEND} diff --git a/tests-manual/gpu/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp index 538bb0a973..9253f8bc91 100644 --- a/tests-manual/gpu/src/TestFbx.cpp +++ b/tests-manual/gpu/src/TestFbx.cpp @@ -100,12 +100,12 @@ bool TestFbx::isReady() const { void TestFbx::parseFbx(const QByteArray& fbxData) { QVariantHash mapping; - FBXGeometry* fbx = readFBX(fbxData, mapping); + HFMModel* hfmModel = readFBX(fbxData, mapping); size_t totalVertexCount = 0; size_t totalIndexCount = 0; size_t totalPartCount = 0; size_t highestIndex = 0; - for (const auto& mesh : fbx->meshes) { + for (const auto& mesh : hfmModel->meshes) { size_t vertexCount = mesh.vertices.size(); totalVertexCount += mesh.vertices.size(); highestIndex = std::max(highestIndex, vertexCount); @@ -123,7 +123,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { std::vector parts; parts.reserve(totalPartCount); _partCount = totalPartCount; - for (const auto& mesh : fbx->meshes) { + for (const auto& mesh : hfmModel->meshes) { baseVertex = vertices.size(); vec3 color; @@ -133,7 +133,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { partIndirect.firstIndex = (uint)indices.size(); partIndirect.baseInstance = (uint)parts.size(); _partTransforms.push_back(mesh.modelTransform); - auto material = fbx->materials[part.materialID]; + auto material = hfmModel->materials[part.materialID]; color = material.diffuseColor; for (auto index : part.quadTrianglesIndices) { indices.push_back(index); @@ -163,7 +163,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { _vertexBuffer->append(vertices); _indexBuffer->append(indices); _indirectBuffer->append(parts); - delete fbx; + delete hfmModel; } void TestFbx::renderTest(size_t testId, RenderArgs* args) { diff --git a/tests-manual/gpu/src/TestFbx.h b/tests-manual/gpu/src/TestFbx.h index 391fff1091..8056af21ec 100644 --- a/tests-manual/gpu/src/TestFbx.h +++ b/tests-manual/gpu/src/TestFbx.h @@ -11,7 +11,7 @@ #include -class FBXGeometry; +class HFMModel; class TestFbx : public GpuTestBase { size_t _partCount { 0 }; diff --git a/tests-manual/qml/qml/MacQml.qml b/tests-manual/qml/qml/MacQml.qml new file mode 100644 index 0000000000..bb7e3a0dff --- /dev/null +++ b/tests-manual/qml/qml/MacQml.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 1.2 +import QtWebEngine 1.5 + +Item { + width: 640 + height: 480 + + Rectangle { + width: 5 + height: 5 + color: "red" + ColorAnimation on color { loops: Animation.Infinite; from: "red"; to: "yellow"; duration: 1000 } + } + + + WebEngineView { + id: root + url: "https://google.com/" + x: 6; y: 6; + width: parent.width * 0.8 + height: parent.height * 0.8 + + } +} diff --git a/tests-manual/qml/src/MacQml.cpp b/tests-manual/qml/src/MacQml.cpp new file mode 100644 index 0000000000..9c5f91041e --- /dev/null +++ b/tests-manual/qml/src/MacQml.cpp @@ -0,0 +1,60 @@ +#include "MacQml.h" + +#include + +#include + +#include + +using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; + +void MacQml::update() { + auto rootItem =_surface->getRootItem(); + float now = sinf(secTimestampNow()); + rootItem->setProperty("level", fabs(now)); + rootItem->setProperty("muted", now > 0.0f); + rootItem->setProperty("statsValue", rand()); + + // Fetch any new textures + TextureAndFence newTextureAndFence; + if (_surface->fetchTexture(newTextureAndFence)) { + if (_texture != 0) { + auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _discardLamdba(_texture, readFence); + } + _texture = newTextureAndFence.first; + _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); + } +} + +void MacQml::init() { + Parent::init(); + _glf.glGenFramebuffers(1, &_fbo); + _surface.reset(new hifi::qml::OffscreenSurface()); + //QUrl url =getTestResource("qml/main.qml"); + QUrl url = getTestResource("qml/MacQml.qml"); + hifi::qml::QmlContextObjectCallback callback =[](QQmlContext* context, QQuickItem* item) { + }; + _surface->load(url, callback); + _surface->resize(_window->size()); + _surface->resume(); + +} + +void MacQml::draw() { + auto size = _window->geometry().size(); + if (_texture) { + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); + _glf.glBlitFramebuffer( + // src coordinates + 0, 0, size.width(), size.height(), + // dst coordinates + 0, 0, size.width(), size.height(), + // blit mask and filter + GL_COLOR_BUFFER_BIT, GL_NEAREST); + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + } +} diff --git a/tests-manual/qml/src/MacQml.h b/tests-manual/qml/src/MacQml.h new file mode 100644 index 0000000000..50f71cb72e --- /dev/null +++ b/tests-manual/qml/src/MacQml.h @@ -0,0 +1,16 @@ +#include "TestCase.h" + +#include + +class MacQml : public TestCase { + using Parent = TestCase; +public: + GLuint _texture{ 0 }; + QmlPtr _surface; + GLuint _fbo{ 0 }; + + MacQml(const QWindow* window) : Parent(window) {} + void update() override; + void init() override; + void draw() override; +}; diff --git a/tests-manual/qml/src/StressWeb.cpp b/tests-manual/qml/src/StressWeb.cpp new file mode 100644 index 0000000000..71293feb9a --- /dev/null +++ b/tests-manual/qml/src/StressWeb.cpp @@ -0,0 +1,131 @@ +#include "StressWeb.h" + +#include + +#include + +using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; + +static const int DEFAULT_MAX_FPS = 10; +static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" }; +static const char* URL_PROPERTY{ "url" }; + +QString StressWeb::getSourceUrl(bool video) { + static const std::vector SOURCE_URLS{ + "https://www.reddit.com/wiki/random", + "https://en.wikipedia.org/wiki/Wikipedia:Random", + "https://slashdot.org/", + }; + + static const std::vector VIDEO_SOURCE_URLS{ + "https://www.youtube.com/watch?v=gDXwhHm4GhM", + "https://www.youtube.com/watch?v=Ch_hoYPPeGc", + }; + + const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS; + auto index = rand() % sourceUrls.size(); + return sourceUrls[index]; +} + + + +void StressWeb::buildSurface(QmlInfo& qmlInfo, bool video) { + ++_surfaceCount; + auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f)); + auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs); + qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow(); + qmlInfo.texture = 0; + qmlInfo.surface.reset(new hifi::qml::OffscreenSurface()); + qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) { + item->setProperty(URL_PROPERTY, getSourceUrl(video)); + }); + qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS); + qmlInfo.surface->resize(_qmlSize); + qmlInfo.surface->resume(); +} + +void StressWeb::destroySurface(QmlInfo& qmlInfo) { + auto& surface = qmlInfo.surface; + auto& currentTexture = qmlInfo.texture; + if (currentTexture) { + auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _discardLamdba(currentTexture, readFence); + } + auto webView = surface->getRootItem(); + if (webView) { + // stop loading + QMetaObject::invokeMethod(webView, "stop"); + webView->setProperty(URL_PROPERTY, "about:blank"); + } + surface->pause(); + surface.reset(); +} + +void StressWeb::update() { + auto now = usecTimestampNow(); + // Fetch any new textures + for (size_t x = 0; x < DIVISIONS_X; ++x) { + for (size_t y = 0; y < DIVISIONS_Y; ++y) { + auto& qmlInfo = _surfaces[x][y]; + if (!qmlInfo.surface) { + if (now < _createStopTime && randFloat() > 0.99f) { + buildSurface(qmlInfo, x == 0 && y == 0); + } else { + continue; + } + } + + if (now > qmlInfo.lifetime) { + destroySurface(qmlInfo); + continue; + } + + auto& surface = qmlInfo.surface; + auto& currentTexture = qmlInfo.texture; + + TextureAndFence newTextureAndFence; + if (surface->fetchTexture(newTextureAndFence)) { + if (currentTexture != 0) { + auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _discardLamdba(currentTexture, readFence); + } + currentTexture = newTextureAndFence.first; + _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); + } + } + } +} + +void StressWeb::init() { + Parent::init(); + _createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND); + _glf.glGenFramebuffers(1, &_fbo); +} + +void StressWeb::draw() { + auto size = _window->geometry().size(); + auto incrementX = size.width() / DIVISIONS_X; + auto incrementY = size.height() / DIVISIONS_Y; + + for (uint32_t x = 0; x < DIVISIONS_X; ++x) { + for (uint32_t y = 0; y < DIVISIONS_Y; ++y) { + auto& qmlInfo = _surfaces[x][y]; + if (!qmlInfo.surface || !qmlInfo.texture) { + continue; + } + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0); + _glf.glBlitFramebuffer( + // src coordinates + 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1, + // dst coordinates + incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1), + // blit mask and filter + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + } + _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +} diff --git a/tests-manual/qml/src/StressWeb.h b/tests-manual/qml/src/StressWeb.h new file mode 100644 index 0000000000..a68e34d0c1 --- /dev/null +++ b/tests-manual/qml/src/StressWeb.h @@ -0,0 +1,34 @@ +#include "TestCase.h" + +#include + +#include + +#define DIVISIONS_X 5 +#define DIVISIONS_Y 5 + +class StressWeb : public TestCase { + using Parent = TestCase; +public: + using QmlPtr = QSharedPointer; + + struct QmlInfo { + QmlPtr surface; + GLuint texture{ 0 }; + uint64_t lifetime{ 0 }; + }; + + size_t _surfaceCount{ 0 }; + uint64_t _createStopTime{ 0 }; + const QSize _qmlSize{ 640, 480 }; + std::array, DIVISIONS_X> _surfaces; + GLuint _fbo{ 0 }; + + StressWeb(const QWindow* window) : Parent(window) {} + static QString getSourceUrl(bool video); + void buildSurface(QmlInfo& qmlInfo, bool video); + void destroySurface(QmlInfo& qmlInfo); + void update() override; + void init() override; + void draw() override; +}; diff --git a/tests-manual/qml/src/TestCase.cpp b/tests-manual/qml/src/TestCase.cpp new file mode 100644 index 0000000000..534de71e51 --- /dev/null +++ b/tests-manual/qml/src/TestCase.cpp @@ -0,0 +1,25 @@ +#include "TestCase.h" + +#include +#include + +void TestCase::destroy() { +} +void TestCase::update() { +} + +void TestCase::init() { + _glf.initializeOpenGLFunctions(); + _discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda(); +} + +QUrl TestCase::getTestResource(const QString& relativePath) { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../")) + "/"; + qDebug() << "Resources Path: " << dir; + } + return QUrl::fromLocalFile(dir + relativePath); +} diff --git a/tests-manual/qml/src/TestCase.h b/tests-manual/qml/src/TestCase.h new file mode 100644 index 0000000000..191eecb408 --- /dev/null +++ b/tests-manual/qml/src/TestCase.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +class TestCase { +public: + using QmlPtr = QSharedPointer; + using Builder = std::function; + TestCase(const QWindow* window) : _window(window) {} + virtual void init(); + virtual void destroy(); + virtual void update(); + virtual void draw() = 0; + static QUrl getTestResource(const QString& relativePath); + +protected: + QOpenGLFunctions_4_1_Core _glf; + const QWindow* _window; + std::function _discardLamdba; +}; diff --git a/tests-manual/qml/src/main.cpp b/tests-manual/qml/src/main.cpp index d70bb52dde..1d98ebf8c8 100644 --- a/tests-manual/qml/src/main.cpp +++ b/tests-manual/qml/src/main.cpp @@ -43,6 +43,11 @@ #include #include #include +#include +#include + +#include "TestCase.h" +#include "MacQml.h" namespace gl { extern void initModuleGl(); @@ -67,53 +72,37 @@ QUrl getTestResource(const QString& relativePath) { return QUrl::fromLocalFile(dir + relativePath); } -#define DIVISIONS_X 5 -#define DIVISIONS_Y 5 - using QmlPtr = QSharedPointer; using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; -struct QmlInfo { - QmlPtr surface; - GLuint texture{ 0 }; - uint64_t lifetime{ 0 }; -}; - class TestWindow : public QWindow { public: - TestWindow(); + TestWindow(const TestCase::Builder& caseBuilder); private: QOpenGLContext _glContext; OffscreenGLCanvas _sharedContext; - std::array, DIVISIONS_X> _surfaces; + TestCase* _testCase{ nullptr }; QOpenGLFunctions_4_1_Core _glf; - std::function _discardLamdba; QSize _size; - size_t _surfaceCount{ 0 }; - GLuint _fbo{ 0 }; - const QSize _qmlSize{ 640, 480 }; bool _aboutToQuit{ false }; - uint64_t _createStopTime; void initGl(); - void updateSurfaces(); - void buildSurface(QmlInfo& qmlInfo, bool allowVideo); - void destroySurface(QmlInfo& qmlInfo); void resizeWindow(const QSize& size); void draw(); void resizeEvent(QResizeEvent* ev) override; }; -TestWindow::TestWindow() { +TestWindow::TestWindow(const TestCase::Builder& builder) { Setting::init(); + + _testCase = builder(this); setSurfaceType(QSurface::OpenGLSurface); qmlRegisterType("Hifi", 1, 0, "TestItem"); show(); - _createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND); resize(QSize(800, 600)); @@ -129,162 +118,84 @@ TestWindow::TestWindow() { }); } +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context(); +OffscreenGLCanvas* _chromiumShareContext{ nullptr}; void TestWindow::initGl() { _glContext.setFormat(format()); + + auto globalShareContext = qt_gl_global_share_context(); + if (globalShareContext) { + _glContext.setShareContext(globalShareContext); + globalShareContext->makeCurrent(this); + gl::Context::setupDebugLogging(globalShareContext); + globalShareContext->doneCurrent(); + } + if (!_glContext.create() || !_glContext.makeCurrent(this)) { qFatal("Unable to intialize Window GL context"); } + gl::Context::setupDebugLogging(&_glContext); gl::initModuleGl(); _glf.initializeOpenGLFunctions(); - _glf.glGenFramebuffers(1, &_fbo); if (!_sharedContext.create(&_glContext) || !_sharedContext.makeCurrent()) { qFatal("Unable to intialize Shared GL context"); } hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext()); - _discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda(); + + if (!globalShareContext) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(&_glContext); + if (!_chromiumShareContext->makeCurrent()) { + qFatal("Unable to make chromium shared context current"); + } + + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + _chromiumShareContext->doneCurrent(); + } + + // Restore the GL widget context + if (!_glContext.makeCurrent(this)) { + qFatal("Unable to make window context current"); + } + + _testCase->init(); } void TestWindow::resizeWindow(const QSize& size) { _size = size; } -static const int DEFAULT_MAX_FPS = 10; -static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" }; -static const char* URL_PROPERTY{ "url" }; - -QString getSourceUrl(bool video) { - static const std::vector SOURCE_URLS{ - "https://www.reddit.com/wiki/random", - "https://en.wikipedia.org/wiki/Wikipedia:Random", - "https://slashdot.org/", - }; - - static const std::vector VIDEO_SOURCE_URLS{ - "https://www.youtube.com/watch?v=gDXwhHm4GhM", - "https://www.youtube.com/watch?v=Ch_hoYPPeGc", - }; - - const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS; - auto index = rand() % sourceUrls.size(); - return sourceUrls[index]; -} - -void TestWindow::buildSurface(QmlInfo& qmlInfo, bool video) { - ++_surfaceCount; - auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f)); - auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs); - qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow(); - qmlInfo.texture = 0; - qmlInfo.surface.reset(new hifi::qml::OffscreenSurface()); - qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) { - item->setProperty(URL_PROPERTY, getSourceUrl(video)); - }); - qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS); - qmlInfo.surface->resize(_qmlSize); - qmlInfo.surface->resume(); -} - -void TestWindow::destroySurface(QmlInfo& qmlInfo) { - auto& surface = qmlInfo.surface; - auto& currentTexture = qmlInfo.texture; - if (currentTexture) { - auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); - _discardLamdba(currentTexture, readFence); - } - auto webView = surface->getRootItem(); - if (webView) { - // stop loading - QMetaObject::invokeMethod(webView, "stop"); - webView->setProperty(URL_PROPERTY, "about:blank"); - } - surface->pause(); - surface.reset(); -} - -void TestWindow::updateSurfaces() { - auto now = usecTimestampNow(); - // Fetch any new textures - for (size_t x = 0; x < DIVISIONS_X; ++x) { - for (size_t y = 0; y < DIVISIONS_Y; ++y) { - auto& qmlInfo = _surfaces[x][y]; - if (!qmlInfo.surface) { - if (now < _createStopTime && randFloat() > 0.99f) { - buildSurface(qmlInfo, x == 0 && y == 0); - } else { - continue; - } - } - - if (now > qmlInfo.lifetime) { - destroySurface(qmlInfo); - continue; - } - - auto& surface = qmlInfo.surface; - auto& currentTexture = qmlInfo.texture; - - TextureAndFence newTextureAndFence; - if (surface->fetchTexture(newTextureAndFence)) { - if (currentTexture != 0) { - auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); - _discardLamdba(currentTexture, readFence); - } - currentTexture = newTextureAndFence.first; - _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); - } - } - } -} - void TestWindow::draw() { if (_aboutToQuit) { return; } - + // Attempting to draw before we're visible and have a valid size will // produce GL errors. if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) { return; } - + static std::once_flag once; std::call_once(once, [&] { initGl(); }); - + if (!_glContext.makeCurrent(this)) { return; } - - updateSurfaces(); - - auto size = this->geometry().size(); - auto incrementX = size.width() / DIVISIONS_X; - auto incrementY = size.height() / DIVISIONS_Y; + + _testCase->update(); + + auto size = geometry().size(); _glf.glViewport(0, 0, size.width(), size.height()); _glf.glClearColor(1, 0, 0, 1); _glf.glClear(GL_COLOR_BUFFER_BIT); - for (uint32_t x = 0; x < DIVISIONS_X; ++x) { - for (uint32_t y = 0; y < DIVISIONS_Y; ++y) { - auto& qmlInfo = _surfaces[x][y]; - if (!qmlInfo.surface || !qmlInfo.texture) { - continue; - } - _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); - _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0); - _glf.glBlitFramebuffer( - // src coordinates - 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1, - // dst coordinates - incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1), - // blit mask and filter - GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - } - _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + _testCase->draw(); + _glContext.swapBuffers(this); } @@ -292,19 +203,15 @@ void TestWindow::resizeEvent(QResizeEvent* ev) { resizeWindow(ev->size()); } -int main(int argc, char** argv) { - QSurfaceFormat format; - format.setDepthBufferSize(24); - format.setStencilBufferSize(8); +int main(int argc, char** argv) { + auto format = getDefaultOpenGLSurfaceFormat(); format.setVersion(4, 1); - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); - format.setOption(QSurfaceFormat::DebugContext); QSurfaceFormat::setDefaultFormat(format); - // setFormat(format); QGuiApplication app(argc, argv); - TestWindow window; + TestCase::Builder builder = [](const QWindow* window)->TestCase*{ return new MacQml(window); }; + TestWindow window(builder); return app.exec(); } diff --git a/tests-manual/render-texture-load/src/GLIHelpers.cpp b/tests-manual/render-texture-load/src/GLIHelpers.cpp index be39e7f1c8..abdd648485 100644 --- a/tests-manual/render-texture-load/src/GLIHelpers.cpp +++ b/tests-manual/render-texture-load/src/GLIHelpers.cpp @@ -10,6 +10,7 @@ #include #include +#if 0 #include #include #include @@ -68,3 +69,4 @@ gpu::TexturePointer processTexture(const QString& sourceFile) { // FIXME load the actual KTX texture return gpu::TexturePointer(); } +#endif \ No newline at end of file diff --git a/tests-manual/render-texture-load/src/GLIHelpers.h b/tests-manual/render-texture-load/src/GLIHelpers.h index a5d4f71bb3..b9332d7ba2 100644 --- a/tests-manual/render-texture-load/src/GLIHelpers.h +++ b/tests-manual/render-texture-load/src/GLIHelpers.h @@ -12,9 +12,8 @@ #include #include +#if 0 // Work around for a bug in the MSVC compiler that chokes when you use GLI and Qt headers together. -#define gli glm - #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-variable" @@ -46,3 +45,5 @@ gpu::TexturePointer processTexture(const QString& file); #endif + +#endif diff --git a/tests-manual/render-utils/CMakeLists.txt b/tests-manual/render-utils/CMakeLists.txt index be75c53f2e..9f575ee8ca 100644 --- a/tests-manual/render-utils/CMakeLists.txt +++ b/tests-manual/render-utils/CMakeLists.txt @@ -8,7 +8,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() # link in the shared libraries -link_hifi_libraries(render-utils gl gpu shared ${PLATFORM_GL_BACKEND}) +link_hifi_libraries(render-utils shaders gl gpu shared ${PLATFORM_GL_BACKEND}) target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT}) if (WIN32) diff --git a/tests-manual/ui/qml/ControlsGalleryWindow.qml b/tests-manual/ui/qml/ControlsGalleryWindow.qml new file mode 100644 index 0000000000..32fd62da36 --- /dev/null +++ b/tests-manual/ui/qml/ControlsGalleryWindow.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import QtQuick.Window 2.3 +import QtQuick.Controls 1.4 +import '../../../scripts/developer/tests' as Tests + +ApplicationWindow { + width: 640 + height: 480 + visible: true + + Tests.ControlsGallery { + anchors.fill: parent + } +} diff --git a/tests-manual/ui/qml/Palettes.qml b/tests-manual/ui/qml/Palettes.qml deleted file mode 100644 index 2bdf6eba8b..0000000000 --- a/tests-manual/ui/qml/Palettes.qml +++ /dev/null @@ -1,150 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -Rectangle { - color: "teal" - height: 512 - width: 192 - SystemPalette { id: sp; colorGroup: SystemPalette.Active } - SystemPalette { id: spi; colorGroup: SystemPalette.Inactive } - SystemPalette { id: spd; colorGroup: SystemPalette.Disabled } - - Column { - anchors.margins: 8 - anchors.fill: parent - spacing: 8 - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "base" } - Rectangle { height: parent.height; width: 16; color: sp.base } - Rectangle { height: parent.height; width: 16; color: spi.base } - Rectangle { height: parent.height; width: 16; color: spd.base } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "alternateBase" } - Rectangle { height: parent.height; width: 16; color: sp.alternateBase } - Rectangle { height: parent.height; width: 16; color: spi.alternateBase } - Rectangle { height: parent.height; width: 16; color: spd.alternateBase } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "dark" } - Rectangle { height: parent.height; width: 16; color: sp.dark } - Rectangle { height: parent.height; width: 16; color: spi.dark } - Rectangle { height: parent.height; width: 16; color: spd.dark } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "mid" } - Rectangle { height: parent.height; width: 16; color: sp.mid } - Rectangle { height: parent.height; width: 16; color: spi.mid } - Rectangle { height: parent.height; width: 16; color: spd.mid } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "mid light" } - Rectangle { height: parent.height; width: 16; color: sp.midlight } - Rectangle { height: parent.height; width: 16; color: spi.midlight } - Rectangle { height: parent.height; width: 16; color: spd.midlight } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "light" } - Rectangle { height: parent.height; width: 16; color: sp.light} - Rectangle { height: parent.height; width: 16; color: spi.light} - Rectangle { height: parent.height; width: 16; color: spd.light} - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "shadow" } - Rectangle { height: parent.height; width: 16; color: sp.shadow} - Rectangle { height: parent.height; width: 16; color: spi.shadow} - Rectangle { height: parent.height; width: 16; color: spd.shadow} - } - Item { - height: 16 - width:parent.width - } - - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "text" } - Rectangle { height: parent.height; width: 16; color: sp.text } - Rectangle { height: parent.height; width: 16; color: spi.text } - Rectangle { height: parent.height; width: 16; color: spd.text } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "window" } - Rectangle { height: parent.height; width: 16; color: sp.window } - Rectangle { height: parent.height; width: 16; color: spi.window } - Rectangle { height: parent.height; width: 16; color: spd.window } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "window text" } - Rectangle { height: parent.height; width: 16; color: sp.windowText } - Rectangle { height: parent.height; width: 16; color: spi.windowText } - Rectangle { height: parent.height; width: 16; color: spd.windowText } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "button" } - Rectangle { height: parent.height; width: 16; color: sp.button } - Rectangle { height: parent.height; width: 16; color: spi.button } - Rectangle { height: parent.height; width: 16; color: spd.button } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "buttonText" } - Rectangle { height: parent.height; width: 16; color: sp.buttonText } - Rectangle { height: parent.height; width: 16; color: spi.buttonText } - Rectangle { height: parent.height; width: 16; color: spd.buttonText } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "highlight" } - Rectangle { height: parent.height; width: 16; color: sp.highlight } - Rectangle { height: parent.height; width: 16; color: spi.highlight } - Rectangle { height: parent.height; width: 16; color: spd.highlight } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "highlighted text" } - Rectangle { height: parent.height; width: 16; color: sp.highlightedText} - Rectangle { height: parent.height; width: 16; color: spi.highlightedText} - Rectangle { height: parent.height; width: 16; color: spd.highlightedText} - } - } -} diff --git a/tests-manual/ui/qml/ScrollingGraph.qml b/tests-manual/ui/qml/ScrollingGraph.qml deleted file mode 100644 index 55523a23f4..0000000000 --- a/tests-manual/ui/qml/ScrollingGraph.qml +++ /dev/null @@ -1,111 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -Rectangle { - id: root - property int size: 64 - width: size - height: size - color: 'black' - property int controlId: 0 - property real value: 0.5 - property int scrollWidth: 1 - property real min: 0.0 - property real max: 1.0 - property bool log: false - property real range: max - min - property color lineColor: 'yellow' - property bool bar: false - property real lastHeight: -1 - property string label: "" - - function update() { - value = Controller.getValue(controlId); - if (log) { - var log = Math.log(10) / Math.log(Math.abs(value)); - var sign = Math.sign(value); - value = log * sign; - } - canvas.requestPaint(); - } - - function drawHeight() { - if (value < min) { - return 0; - } - if (value > max) { - return height; - } - return ((value - min) / range) * height; - } - - Timer { - interval: 50; running: true; repeat: true - onTriggered: root.update() - } - - Canvas { - id: canvas - anchors.fill: parent - antialiasing: false - - Text { - anchors.top: parent.top - text: root.label - color: 'white' - } - - Text { - anchors.right: parent.right - anchors.top: parent.top - text: root.max - color: 'white' - } - - Text { - anchors.right: parent.right - anchors.bottom: parent.bottom - text: root.min - color: 'white' - } - - function scroll() { - var ctx = canvas.getContext('2d'); - var image = ctx.getImageData(0, 0, canvas.width, canvas.height); - ctx.beginPath(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height) - ctx.restore() - } - - onPaint: { - scroll(); - var ctx = canvas.getContext('2d'); - ctx.save(); - var currentHeight = root.drawHeight(); - if (root.lastHeight == -1) { - root.lastHeight = currentHeight - } - -// var x = canvas.width - root.drawWidth; -// var y = canvas.height - drawHeight; -// ctx.fillStyle = root.color -// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1) -// ctx.fill(); -// ctx.restore() - - - ctx.beginPath(); - ctx.lineWidth = 1 - ctx.strokeStyle = root.lineColor - ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) - ctx.stroke() - ctx.restore() - root.lastHeight = currentHeight - } - } -} - - diff --git a/tests-manual/ui/qml/StubMenu.qml b/tests-manual/ui/qml/StubMenu.qml deleted file mode 100644 index fd0298988a..0000000000 --- a/tests-manual/ui/qml/StubMenu.qml +++ /dev/null @@ -1,730 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import "../../../interface/resources/qml/hifi" - -Menu { - property var menuOption: MenuOption {} - Item { - Action { - id: login; - text: menuOption.login - } - Action { - id: update; - text: "Update"; - enabled: false - } - Action { - id: crashReporter; - text: "Crash Reporter..."; - enabled: false - } - Action { - id: help; - text: menuOption.help - onTriggered: Application.showHelp() - } - Action { - id: aboutApp; - text: menuOption.aboutApp - } - Action { - id: quit; - text: menuOption.quit - } - - ExclusiveGroup { id: renderResolutionGroup } - Action { - id: renderResolutionOne; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionOne; - checkable: true; - checked: true - } - Action { - id: renderResolutionTwoThird; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionTwoThird; - checkable: true - } - Action { - id: renderResolutionHalf; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionHalf; - checkable: true - } - Action { - id: renderResolutionThird; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionThird; - checkable: true - } - Action { - id: renderResolutionQuarter; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionQuarter; - checkable: true - } - - ExclusiveGroup { id: ambientLightGroup } - Action { - id: renderAmbientLightGlobal; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLightGlobal; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight0; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight0; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight1; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight1; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight2; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight2; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight3; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight3; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight4; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight4; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight5; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight5; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight6; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight6; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight7; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight7; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight8; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight8; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight9; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight9; - checkable: true; - checked: true - } - Action { - id: preferences - shortcut: StandardKey.Preferences - text: menuOption.preferences - onTriggered: dialogsManager.editPreferences() - } - - } - - Menu { - title: "File" - MenuItem { - action: login - } - MenuItem { - action: update - } - MenuItem { - action: help - } - MenuItem { - action: crashReporter - } - MenuItem { - action: aboutApp - } - MenuItem { - action: quit - } - } - - Menu { - title: "Edit" - MenuItem { - text: "Undo" } - MenuItem { - text: "Redo" } - MenuItem { - text: menuOption.runningScripts - } - MenuItem { - text: menuOption.loadScript - } - MenuItem { - text: menuOption.loadScriptURL - } - MenuItem { - text: menuOption.stopAllScripts - } - MenuItem { - text: menuOption.reloadAllScripts - } - MenuItem { - text: menuOption.scriptEditor - } - MenuItem { - text: menuOption.console_ - } - MenuItem { - text: menuOption.reloadContent - } - MenuItem { - text: menuOption.packageModel - } - } - - Menu { - title: "Audio" - MenuItem { - text: menuOption.muteAudio; - checkable: true - } - MenuItem { - text: menuOption.audioTools; - checkable: true - } - } - Menu { - title: "Avatar" - // Avatar > Attachments... - MenuItem { - text: menuOption.attachments - } - Menu { - title: "Size" - // Avatar > Size > Increase - MenuItem { - text: menuOption.increaseAvatarSize - } - // Avatar > Size > Decrease - MenuItem { - text: menuOption.decreaseAvatarSize - } - // Avatar > Size > Reset - MenuItem { - text: menuOption.resetAvatarSize - } - } - // Avatar > Reset Sensors - MenuItem { - text: menuOption.resetSensors - } - } - Menu { - title: "Display" - } - Menu { - title: "View" - ExclusiveGroup { - id: cameraModeGroup - } - - MenuItem { - text: menuOption.firstPerson; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.thirdPerson; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.fullscreenMirror; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.independentMode; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.cameraEntityMode; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuSeparator{} - MenuItem { - text: menuOption.miniMirror; - checkable: true - } - } - Menu { - title: "Navigate" - MenuItem { - text: "Home" } - MenuItem { - text: menuOption.addressBar - } - MenuItem { - text: "Directory" } - MenuItem { - text: menuOption.copyAddress - } - MenuItem { - text: menuOption.copyPath - } - } - Menu { - title: "Settings" - MenuItem { - text: "Advanced Menus" } - MenuItem { - text: "Developer Menus" } - MenuItem { - text: menuOption.preferences - } - MenuItem { - text: "Avatar..." } - MenuItem { - text: "Audio..." } - MenuItem { - text: "LOD..." } - MenuItem { - text: menuOption.inputMenu - } - } - Menu { - title: "Developer" - Menu { - title: "Render" - MenuItem { - text: menuOption.atmosphere; - checkable: true - } - MenuItem { - text: menuOption.worldAxes; - checkable: true - } - MenuItem { - text: menuOption.debugAmbientOcclusion; - checkable: true - } - MenuItem { - text: menuOption.antialiasing; - checkable: true - } - MenuItem { - text: menuOption.stars; - checkable: true - } - Menu { - title: menuOption.renderAmbientLight - MenuItem { - action: renderAmbientLightGlobal; } - MenuItem { - action: renderAmbientLight0; } - MenuItem { - action: renderAmbientLight1; } - MenuItem { - action: renderAmbientLight2; } - MenuItem { - action: renderAmbientLight3; } - MenuItem { - action: renderAmbientLight4; } - MenuItem { - action: renderAmbientLight5; } - MenuItem { - action: renderAmbientLight6; } - MenuItem { - action: renderAmbientLight7; } - MenuItem { - action: renderAmbientLight8; } - MenuItem { - action: renderAmbientLight9; } - } - MenuItem { - text: menuOption.throttleFPSIfNotFocus; - checkable: true - } - Menu { - title: menuOption.renderResolution - MenuItem { - action: renderResolutionOne - } - MenuItem { - action: renderResolutionTwoThird - } - MenuItem { - action: renderResolutionHalf - } - MenuItem { - action: renderResolutionThird - } - MenuItem { - action: renderResolutionQuarter - } - } - MenuItem { - text: menuOption.lodTools - } - } - Menu { - title: "Assets" - MenuItem { - text: menuOption.uploadAsset - } - MenuItem { - text: menuOption.assetMigration - } - } - Menu { - title: "Avatar" - Menu { - title: "Face Tracking" - MenuItem { - text: menuOption.noFaceTracking; - checkable: true - } - MenuItem { - text: menuOption.faceshift; - checkable: true - } - MenuItem { - text: menuOption.useCamera; - checkable: true - } - MenuSeparator{} - MenuItem { - text: menuOption.binaryEyelidControl; - checkable: true - } - MenuItem { - text: menuOption.coupleEyelids; - checkable: true - } - MenuItem { - text: menuOption.useAudioForMouth; - checkable: true - } - MenuItem { - text: menuOption.velocityFilter; - checkable: true - } - MenuItem { - text: menuOption.calibrateCamera - } - MenuSeparator{} - MenuItem { - text: menuOption.muteFaceTracking; - checkable: true - } - MenuItem { - text: menuOption.autoMuteAudio; - checkable: true - } - } - Menu { - title: "Eye Tracking" - MenuItem { - text: menuOption.sMIEyeTracking; - checkable: true - } - Menu { - title: "Calibrate" - MenuItem { - text: menuOption.onePointCalibration - } - MenuItem { - text: menuOption.threePointCalibration - } - MenuItem { - text: menuOption.fivePointCalibration - } - } - MenuItem { - text: menuOption.simulateEyeTracking; - checkable: true - } - } - MenuItem { - text: menuOption.avatarReceiveStats; - checkable: true - } - MenuItem { - text: menuOption.renderBoundingCollisionShapes; - checkable: true - } - MenuItem { - text: menuOption.renderLookAtVectors; - checkable: true - } - MenuItem { - text: menuOption.renderLookAtTargets; - checkable: true - } - MenuItem { - text: menuOption.renderFocusIndicator; - checkable: true - } - MenuItem { - text: menuOption.showWhosLookingAtMe; - checkable: true - } - MenuItem { - text: menuOption.fixGaze; - checkable: true - } - MenuItem { - text: menuOption.animDebugDrawDefaultPose; - checkable: true - } - MenuItem { - text: menuOption.animDebugDrawAnimPose; - checkable: true - } - MenuItem { - text: menuOption.animDebugDrawPosition; - checkable: true - } - MenuItem { - text: menuOption.meshVisible; - checkable: true - } - MenuItem { - text: menuOption.disableEyelidAdjustment; - checkable: true - } - MenuItem { - text: menuOption.turnWithHead; - checkable: true - } - MenuItem { - text: menuOption.keyboardMotorControl; - checkable: true - } - MenuItem { - text: menuOption.scriptedMotorControl; - checkable: true - } - MenuItem { - text: menuOption.enableCharacterController; - checkable: true - } - } - Menu { - title: "Hands" - MenuItem { - text: menuOption.displayHandTargets; - checkable: true - } - MenuItem { - text: menuOption.lowVelocityFilter; - checkable: true - } - Menu { - title: "Leap Motion" - MenuItem { - text: menuOption.leapMotionOnHMD; - checkable: true - } - } - } - Menu { - title: "Entities" - MenuItem { - text: menuOption.octreeStats - } - MenuItem { - text: menuOption.showRealtimeEntityStats; - checkable: true - } - } - Menu { - title: "Network" - MenuItem { - text: menuOption.reloadContent - } - MenuItem { - text: menuOption.disableNackPackets; - checkable: true - } - MenuItem { - text: menuOption.disableActivityLogger; - checkable: true - } - MenuItem { - text: menuOption.cachesSize - } - MenuItem { - text: menuOption.diskCacheEditor - } - MenuItem { - text: menuOption.showDSConnectTable - } - MenuItem { - text: menuOption.bandwidthDetails - } - } - Menu { - title: "Timing" - Menu { - title: "Performance Timer" - MenuItem { - text: menuOption.displayDebugTimingDetails; - checkable: true - } - MenuItem { - text: menuOption.onlyDisplayTopTen; - checkable: true - } - MenuItem { - text: menuOption.expandUpdateTiming; - checkable: true - } - MenuItem { - text: menuOption.expandMyAvatarTiming; - checkable: true - } - MenuItem { - text: menuOption.expandMyAvatarSimulateTiming; - checkable: true - } - MenuItem { - text: menuOption.expandOtherAvatarTiming; - checkable: true - } - MenuItem { - text: menuOption.expandPaintGLTiming; - checkable: true - } - } - MenuItem { - text: menuOption.frameTimer; - checkable: true - } - MenuItem { - text: menuOption.runTimingTests - } - MenuItem { - text: menuOption.pipelineWarnings; - checkable: true - } - MenuItem { - text: menuOption.logExtraTimings; - checkable: true - } - MenuItem { - text: menuOption.suppressShortTimings; - checkable: true - } - } - Menu { - title: "Audio" - MenuItem { - text: menuOption.audioNoiseReduction; - checkable: true - } - MenuItem { - text: menuOption.echoServerAudio; - checkable: true - } - MenuItem { - text: menuOption.echoLocalAudio; - checkable: true - } - MenuItem { - text: menuOption.muteEnvironment - } - Menu { - title: "Audio" - MenuItem { - text: menuOption.audioScope; - checkable: true - } - MenuItem { - text: menuOption.audioScopePause; - checkable: true - } - Menu { - title: "Display Frames" - ExclusiveGroup { - id: audioScopeFramesGroup - } - MenuItem { - exclusiveGroup: audioScopeFramesGroup; - text: menuOption.audioScopeFiveFrames; - checkable: true - } - MenuItem { - exclusiveGroup: audioScopeFramesGroup; - text: menuOption.audioScopeTwentyFrames; - checkable: true - } - MenuItem { - exclusiveGroup: audioScopeFramesGroup; - text: menuOption.audioScopeFiftyFrames; - checkable: true - } - } - MenuItem { - text: menuOption.audioNetworkStats - } - } - } - Menu { - title: "Physics" - MenuItem { - text: menuOption.physicsShowOwned; - checkable: true - } - MenuItem { - text: menuOption.physicsShowHulls; - checkable: true - } - } - MenuItem { - text: menuOption.displayCrashOptions; - checkable: true - } - MenuItem { - text: menuOption.crashInterface - } - MenuItem { - text: menuOption.log - } - MenuItem { - text: menuOption.stats; - checkable: true - } - } -} diff --git a/tests-manual/ui/qml/Stubs.qml b/tests-manual/ui/qml/Stubs.qml deleted file mode 100644 index 8c1465d54c..0000000000 --- a/tests-manual/ui/qml/Stubs.qml +++ /dev/null @@ -1,346 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -// Stubs for the global service objects set by Interface.cpp when creating the UI -// This is useful for testing inside Qt creator where these services don't actually exist. -Item { - - Item { - objectName: "offscreenFlags" - property bool navigationFocused: false - } - - Item { - objectName: "urlHandler" - function fixupUrl(url) { return url; } - function canHandleUrl(url) { return false; } - function handleUrl(url) { return true; } - } - - Item { - objectName: "Account" - function isLoggedIn() { return true; } - function getUsername() { return "Jherico"; } - } - - Item { - objectName: "GL" - property string vendor: "" - } - - Item { - objectName: "ApplicationCompositor" - property bool reticleOverDesktop: true - } - - Item { - objectName: "Controller" - function getRecommendedOverlayRect() { - return Qt.rect(0, 0, 1920, 1080); - } - } - - Item { - objectName: "Preferences" - // List of categories obtained by logging categories as they are added in Interface in Preferences::addPreference(). - property var categories: [ - "Avatar Basics", "Snapshots", "Scripts", "Privacy", "Level of Detail Tuning", "Avatar Tuning", "Avatar Camera", - "Audio", "Octree", "HMD", "Sixense Controllers", "Graphics" - ] - } - - Item { - objectName: "ScriptDiscoveryService" - //property var scriptsModelFilter: scriptsModel - signal scriptCountChanged() - property var _runningScripts:[ - { name: "wireFrameTest.js", url: "foo/wireframetest.js", path: "foo/wireframetest.js", local: true }, - { name: "edit.js", url: "foo/edit.js", path: "foo/edit.js", local: false }, - { name: "listAllScripts.js", url: "foo/listAllScripts.js", path: "foo/listAllScripts.js", local: false }, - { name: "users.js", url: "foo/users.js", path: "foo/users.js", local: false }, - ] - - function getRunning() { - return _runningScripts; - } - } - - Item { - objectName: "HMD" - property bool active: false - } - - Item { - id: menuHelper - objectName: "MenuHelper" - - Component { - id: modelMaker - ListModel { } - } - - function toModel(menu, parent) { - if (!parent) { parent = menuHelper } - var result = modelMaker.createObject(parent); - if (menu.type !== MenuItemType.Menu) { - console.warn("Not a menu: " + menu); - return result; - } - - var items = menu.items; - for (var i = 0; i < items.length; ++i) { - var item = items[i]; - switch (item.type) { - case 2: - result.append({"name": item.title, "item": item}) - break; - case 1: - result.append({"name": item.text, "item": item}) - break; - case 0: - result.append({"name": "", "item": item}) - break; - } - } - return result; - } - - } - - Item { - objectName: "Desktop" - - property string _OFFSCREEN_ROOT_OBJECT_NAME: "desktopRoot"; - property string _OFFSCREEN_DIALOG_OBJECT_NAME: "topLevelWindow"; - - - function findChild(item, name) { - for (var i = 0; i < item.children.length; ++i) { - if (item.children[i].objectName === name) { - return item.children[i]; - } - } - return null; - } - - function findParent(item, name) { - while (item) { - if (item.objectName === name) { - return item; - } - item = item.parent; - } - return null; - } - - function findDialog(item) { - item = findParent(item, _OFFSCREEN_DIALOG_OBJECT_NAME); - return item; - } - - function closeDialog(item) { - item = findDialog(item); - if (item) { - item.visible = false - } else { - console.warn("Could not find top level dialog") - } - } - - function getDesktop(item) { - while (item) { - if (item.desktopRoot) { - break; - } - item = item.parent; - } - return item - } - - function raise(item) { - var desktop = getDesktop(item); - if (desktop) { - desktop.raise(item); - } - } - } - - Menu { - id: root - objectName: "rootMenu" - - Menu { - title: "Audio" - } - - Menu { - title: "Avatar" - } - - Menu { - title: "Display" - ExclusiveGroup { id: displayMode } - Menu { - title: "More Stuff" - - Menu { title: "Empty" } - - MenuItem { - text: "Do Nothing" - onTriggered: console.log("Nothing") - } - } - MenuItem { - text: "Oculus" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "OpenVR" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "OSVR" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "2D Screen" - exclusiveGroup: displayMode - checkable: true - checked: true - } - MenuItem { - text: "3D Screen (Active)" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "3D Screen (Passive)" - exclusiveGroup: displayMode - checkable: true - } - } - - Menu { - title: "View" - Menu { - title: "Camera Mode" - ExclusiveGroup { id: cameraMode } - MenuItem { - exclusiveGroup: cameraMode - text: "First Person"; - onTriggered: console.log(text + " checked " + checked) - checkable: true - checked: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Third Person"; - onTriggered: console.log(text) - checkable: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Independent Mode"; - onTriggered: console.log(text) - checkable: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Entity Mode"; - onTriggered: console.log(text) - enabled: false - checkable: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Fullscreen Mirror"; - onTriggered: console.log(text) - checkable: true - } - } - } - - Menu { - title: "Edit" - - MenuItem { - text: "Undo" - shortcut: "Ctrl+Z" - enabled: false - onTriggered: console.log(text) - } - - MenuItem { - text: "Redo" - shortcut: "Ctrl+Shift+Z" - enabled: false - onTriggered: console.log(text) - } - - MenuSeparator { } - - MenuItem { - text: "Cut" - shortcut: "Ctrl+X" - onTriggered: console.log(text) - } - - MenuItem { - text: "Copy" - shortcut: "Ctrl+C" - onTriggered: console.log(text) - } - - MenuItem { - text: "Paste" - shortcut: "Ctrl+V" - visible: false - onTriggered: console.log("Paste") - } - } - - Menu { - title: "Navigate" - } - - Menu { - title: "Market" - } - - Menu { - title: "Settings" - } - - Menu { - title: "Developer" - } - - Menu { - title: "Quit" - } - - Menu { - title: "File" - - Action { - id: login - text: "Login" - } - - Action { - id: quit - text: "Quit" - shortcut: "Ctrl+Q" - onTriggered: Qt.quit(); - } - - MenuItem { action: quit } - MenuItem { action: login } - } - } - -} - diff --git a/tests-manual/ui/qml/TestControllers.qml b/tests-manual/ui/qml/TestControllers.qml deleted file mode 100644 index e9a7fb49e5..0000000000 --- a/tests-manual/ui/qml/TestControllers.qml +++ /dev/null @@ -1,160 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -import "controller" -import "controls" as HifiControls -import "styles" - -HifiControls.VrDialog { - id: root - HifiConstants { id: hifi } - title: "Controller Test" - resizable: true - contentImplicitWidth: clientArea.implicitWidth - contentImplicitHeight: clientArea.implicitHeight - backgroundColor: "beige" - - property var actions: Controller.Actions - property var standard: Controller.Standard - property var hydra: null - property var testMapping: null - property bool testMappingEnabled: false - property var xbox: null - - function buildMapping() { - testMapping = Controller.newMapping(); - testMapping.fromQml(standard.RY).invert().toQml(actions.Pitch); - testMapping.fromQml(function(){ - return Math.sin(Date.now() / 250); - }).toQml(actions.Yaw); - //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); - // Step yaw takes a number of degrees - testMapping.fromQml(standard.LB).pulse(0.10).invert().scale(40.0).toQml(actions.StepYaw); - testMapping.fromQml(standard.RB).pulse(0.10).scale(15.0).toQml(actions.StepYaw); - testMapping.fromQml(standard.RX).scale(15.0).toQml(actions.StepYaw); - } - - function toggleMapping() { - testMapping.enable(!testMappingEnabled); - testMappingEnabled = !testMappingEnabled; - } - - Component.onCompleted: { - var xboxRegex = /^GamePad/; - var hydraRegex = /^Hydra/; - for (var prop in Controller.Hardware) { - if(xboxRegex.test(prop)) { - root.xbox = Controller.Hardware[prop] - print("found xbox") - continue - } - if (hydraRegex.test(prop)) { - root.hydra = Controller.Hardware[prop] - print("found hydra") - continue - } - } - } - - Column { - id: clientArea - spacing: 12 - x: root.clientX - y: root.clientY - - Row { - spacing: 8 - - Button { - text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping") - onClicked: { - - if (!root.testMapping) { - root.buildMapping() - } else { - root.toggleMapping(); - } - } - } - } - - Row { - Standard { device: root.standard; label: "Standard"; width: 180 } - } - - Row { - spacing: 8 - Xbox { device: root.xbox; label: "XBox"; width: 180 } - Hydra { device: root.hydra; width: 180 } - } - - Row { - spacing: 4 - ScrollingGraph { - controlId: Controller.Actions.Yaw - label: "Yaw" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.YawLeft - label: "Yaw Left" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.YawRight - label: "Yaw Right" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.StepYaw - label: "StepYaw" - min: -20.0 - max: 20.0 - size: 64 - } - } - - Row { - ScrollingGraph { - controlId: Controller.Actions.TranslateZ - label: "TranslateZ" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.Forward - label: "Forward" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.Backward - label: "Backward" - min: -2.0 - max: 2.0 - size: 64 - } - - } - } -} // dialog - - - - - diff --git a/tests-manual/ui/qml/TestDialog.qml b/tests-manual/ui/qml/TestDialog.qml deleted file mode 100644 index e6675b7282..0000000000 --- a/tests-manual/ui/qml/TestDialog.qml +++ /dev/null @@ -1,94 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.3 -import "controls" - -VrDialog { - title: "Test Dialog" - id: testDialog - objectName: "TestDialog" - width: 512 - height: 512 - animationDuration: 200 - - onEnabledChanged: { - if (enabled) { - edit.forceActiveFocus(); - } - } - - Item { - id: clientArea - // The client area - anchors.fill: parent - anchors.margins: parent.margins - anchors.topMargin: parent.topMargin - - Rectangle { - property int d: 100 - id: square - objectName: "testRect" - width: d - height: d - anchors.centerIn: parent - color: "red" - NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } - } - - - TextEdit { - id: edit - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - clip: true - text: "test edit" - anchors.top: parent.top - anchors.topMargin: 12 - } - - Button { - x: 128 - y: 192 - text: "Test" - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - onClicked: { - console.log("Click"); - - if (square.visible) { - square.visible = false - } else { - square.visible = true - } - } - } - - Button { - id: customButton2 - y: 192 - text: "Move" - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - onClicked: { - onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0 - } - } - - Keys.onPressed: { - console.log("Key " + event.key); - switch (event.key) { - case Qt.Key_Q: - if (Qt.ControlModifier == event.modifiers) { - event.accepted = true; - break; - } - } - } - } -} diff --git a/tests-manual/ui/qml/TestMenu.qml b/tests-manual/ui/qml/TestMenu.qml deleted file mode 100644 index fe8a26e234..0000000000 --- a/tests-manual/ui/qml/TestMenu.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import Hifi 1.0 - -// Currently for testing a pure QML replacement menu -Item { - Menu { - objectName: "rootMenu"; - } -} diff --git a/tests-manual/ui/qml/TestRoot.qml b/tests-manual/ui/qml/TestRoot.qml deleted file mode 100644 index bd38c696bf..0000000000 --- a/tests-manual/ui/qml/TestRoot.qml +++ /dev/null @@ -1,43 +0,0 @@ -import Hifi 1.0 -import QtQuick 2.3 -import QtQuick.Controls 1.3 -// Import local folder last so that our own control customizations override -// the built in ones -import "controls" - -Root { - id: root - anchors.fill: parent - onParentChanged: { - forceActiveFocus(); - } - Button { - id: messageBox - anchors.right: createDialog.left - anchors.rightMargin: 24 - anchors.bottom: parent.bottom - anchors.bottomMargin: 24 - text: "Message" - onClicked: { - console.log("Foo") - root.information("a") - console.log("Bar") - } - } - Button { - id: createDialog - anchors.right: parent.right - anchors.rightMargin: 24 - anchors.bottom: parent.bottom - anchors.bottomMargin: 24 - text: "Create" - onClicked: { - root.loadChild("MenuTest.qml"); - } - } - - Keys.onPressed: { - console.log("Key press root") - } -} - diff --git a/tests-manual/ui/qml/controlDemo/ButtonPage.qml b/tests-manual/ui/qml/controlDemo/ButtonPage.qml deleted file mode 100644 index 0ed7e2d6ad..0000000000 --- a/tests-manual/ui/qml/controlDemo/ButtonPage.qml +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 - -ScrollView { - id: page - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - Item { - id: content - - width: Math.max(page.viewport.width, grid.implicitWidth + 2 * grid.rowSpacing) - height: Math.max(page.viewport.height, grid.implicitHeight + 2 * grid.columnSpacing) - - GridLayout { - id: grid - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: grid.rowSpacing - anchors.rightMargin: grid.rowSpacing - anchors.topMargin: grid.columnSpacing - - columns: page.width < page.height ? 1 : 2 - - GroupBox { - title: "Button" - Layout.fillWidth: true - Layout.columnSpan: grid.columns - RowLayout { - anchors.fill: parent - Button { text: "OK"; isDefault: true } - Button { text: "Cancel" } - Item { Layout.fillWidth: true } - Button { - text: "Attach" - menu: Menu { - MenuItem { text: "Image" } - MenuItem { text: "Document" } - } - } - } - } - - GroupBox { - title: "CheckBox" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - CheckBox { text: "E-mail"; checked: true } - CheckBox { text: "Calendar"; checked: true } - CheckBox { text: "Contacts" } - } - } - - GroupBox { - title: "RadioButton" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - ExclusiveGroup { id: radioGroup } - RadioButton { text: "Portrait"; exclusiveGroup: radioGroup } - RadioButton { text: "Landscape"; exclusiveGroup: radioGroup } - RadioButton { text: "Automatic"; exclusiveGroup: radioGroup; checked: true } - } - } - - GroupBox { - title: "Switch" - Layout.fillWidth: true - Layout.columnSpan: grid.columns - ColumnLayout { - anchors.fill: parent - RowLayout { - Label { text: "Wi-Fi"; Layout.fillWidth: true } - Switch { checked: true } - } - RowLayout { - Label { text: "Bluetooth"; Layout.fillWidth: true } - Switch { checked: false } - } - } - } - } - } -} diff --git a/tests-manual/ui/qml/controlDemo/InputPage.qml b/tests-manual/ui/qml/controlDemo/InputPage.qml deleted file mode 100644 index cb1878d023..0000000000 --- a/tests-manual/ui/qml/controlDemo/InputPage.qml +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 - -ScrollView { - id: page - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - Item { - id: content - - width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) - height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) - - ColumnLayout { - id: column - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: column.spacing - - GroupBox { - title: "TextField" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - TextField { placeholderText: "..."; Layout.fillWidth: true; z: 1 } - TextField { placeholderText: "Password"; echoMode: TextInput.Password; Layout.fillWidth: true } - } - } - - GroupBox { - title: "ComboBox" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - ComboBox { - model: Qt.fontFamilies() - Layout.fillWidth: true - } - ComboBox { - editable: true - model: ListModel { - id: listModel - ListElement { text: "Apple" } - ListElement { text: "Banana" } - ListElement { text: "Coconut" } - ListElement { text: "Orange" } - } - onAccepted: { - if (find(currentText) === -1) { - listModel.append({text: editText}) - currentIndex = find(editText) - } - } - Layout.fillWidth: true - } - } - } - - GroupBox { - title: "SpinBox" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - SpinBox { value: 99; Layout.fillWidth: true; z: 1 } - SpinBox { decimals: 2; Layout.fillWidth: true } - } - } - } - } -} diff --git a/tests-manual/ui/qml/controlDemo/ProgressPage.qml b/tests-manual/ui/qml/controlDemo/ProgressPage.qml deleted file mode 100644 index a1fa596f79..0000000000 --- a/tests-manual/ui/qml/controlDemo/ProgressPage.qml +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 - -ScrollView { - id: page - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - Item { - id: content - - width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) - height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) - - ColumnLayout { - id: column - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: column.spacing - - GroupBox { - title: "ProgressBar" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - ProgressBar { indeterminate: true; Layout.fillWidth: true } - ProgressBar { value: slider.value; Layout.fillWidth: true } - } - } - - GroupBox { - title: "Slider" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - Slider { id: slider; value: 0.5; Layout.fillWidth: true } - } - } - - GroupBox { - title: "BusyIndicator" - Layout.fillWidth: true - BusyIndicator { running: true } - } - } - } -} diff --git a/tests-manual/ui/qml/controlDemo/main.qml b/tests-manual/ui/qml/controlDemo/main.qml deleted file mode 100644 index 168b9fb291..0000000000 --- a/tests-manual/ui/qml/controlDemo/main.qml +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 1.2 -import "qml/UI.js" as UI -import "qml" -//import "/Users/bdavis/Git/hifi/interface/resources/qml" - -Item { - anchors.fill: parent - visible: true - //title: "Qt Quick Controls Gallery" - - MessageDialog { - id: aboutDialog - icon: StandardIcon.Information - title: "About" - text: "Qt Quick Controls Gallery" - informativeText: "This example demonstrates most of the available Qt Quick Controls." - } - - Action { - id: copyAction - text: "&Copy" - shortcut: StandardKey.Copy - iconName: "edit-copy" - enabled: (!!activeFocusItem && !!activeFocusItem["copy"]) - onTriggered: activeFocusItem.copy() - } - - Action { - id: cutAction - text: "Cu&t" - shortcut: StandardKey.Cut - iconName: "edit-cut" - enabled: (!!activeFocusItem && !!activeFocusItem["cut"]) - onTriggered: activeFocusItem.cut() - } - - Action { - id: pasteAction - text: "&Paste" - shortcut: StandardKey.Paste - iconName: "edit-paste" - enabled: (!!activeFocusItem && !!activeFocusItem["paste"]) - onTriggered: activeFocusItem.paste() - } - -// toolBar: ToolBar { -// RowLayout { -// anchors.fill: parent -// anchors.margins: spacing -// Label { -// text: UI.label -// } -// Item { Layout.fillWidth: true } -// CheckBox { -// id: enabler -// text: "Enabled" -// checked: true -// } -// } -// } - -// menuBar: MenuBar { -// Menu { -// title: "&File" -// MenuItem { -// text: "E&xit" -// shortcut: StandardKey.Quit -// onTriggered: Qt.quit() -// } -// } -// Menu { -// title: "&Edit" -// visible: tabView.currentIndex == 2 -// MenuItem { action: cutAction } -// MenuItem { action: copyAction } -// MenuItem { action: pasteAction } -// } -// Menu { -// title: "&Help" -// MenuItem { -// text: "About..." -// onTriggered: aboutDialog.open() -// } -// } -// } - - TabView { - id: tabView - - anchors.fill: parent - anchors.margins: UI.margin - tabPosition: UI.tabPosition - - Layout.minimumWidth: 360 - Layout.minimumHeight: 360 - Layout.preferredWidth: 480 - Layout.preferredHeight: 640 - - Tab { - title: "Buttons" - ButtonPage { - enabled: enabler.checked - } - } - Tab { - title: "Progress" - ProgressPage { - enabled: enabler.checked - } - } - Tab { - title: "Input" - InputPage { - enabled: enabler.checked - } - } - } -} diff --git a/tests-manual/ui/qml/main.qml b/tests-manual/ui/qml/main.qml deleted file mode 100644 index 607bd624a1..0000000000 --- a/tests-manual/ui/qml/main.qml +++ /dev/null @@ -1,461 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import Qt.labs.settings 1.0 - -import "../../../interface/resources/qml" -//import "../../../interface/resources/qml/windows" -import "../../../interface/resources/qml/windows" -import "../../../interface/resources/qml/dialogs" -import "../../../interface/resources/qml/hifi" -import "../../../interface/resources/qml/hifi/dialogs" -import "../../../interface/resources/qml/styles-uit" - -ApplicationWindow { - id: appWindow - objectName: "MainWindow" - visible: true - width: 1280 - height: 800 - title: qsTr("Scratch App") - toolBar: Row { - id: testButtons - anchors { margins: 8; left: parent.left; top: parent.top } - spacing: 8 - property int count: 0 - - property var tabs: []; - property var urls: []; - property var toolbar; - property var lastButton; - - Button { - text: HMD.active ? "Disable HMD" : "Enable HMD" - onClicked: HMD.active = !HMD.active - } - - Button { - text: desktop.hmdHandMouseActive ? "Disable HMD HandMouse" : "Enable HMD HandMouse" - onClicked: desktop.hmdHandMouseActive = !desktop.hmdHandMouseActive - } - - // Window visibility - Button { - text: "toggle desktop" - onClicked: desktop.togglePinned() - } - - Button { - text: "Create Toolbar" - onClicked: testButtons.toolbar = desktop.getToolbar("com.highfidelity.interface.toolbar.system"); - } - - Button { - text: "Toggle Toolbar Direction" - onClicked: testButtons.toolbar.horizontal = !testButtons.toolbar.horizontal - } - - Button { - readonly property var icons: [ - "edit-01.svg", - "model-01.svg", - "cube-01.svg", - "sphere-01.svg", - "light-01.svg", - "text-01.svg", - "web-01.svg", - "zone-01.svg", - "particle-01.svg", - ] - property int iconIndex: 0 - readonly property string toolIconUrl: "../../../../../scripts/system/assets/images/tools/" - text: "Create Button" - onClicked: { - var name = icons[iconIndex]; - var url = toolIconUrl + name; - iconIndex = (iconIndex + 1) % icons.length; - var button = testButtons.lastButton = testButtons.toolbar.addButton({ - imageURL: url, - objectName: name, - subImage: { - y: 50, - }, - alpha: 0.9 - }); - - button.clicked.connect(function(){ - console.log("Clicked on button " + button.imageURL + " alpha " + button.alpha) - }); - } - } - - Button { - text: "Toggle Button Visible" - onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible - } - - // Error alerts - /* - Button { - // Message without title. - text: "Show Error" - onClicked: { - var messageBox = desktop.messageBox({ - text: "Diagnostic cycle will be complete in 30 seconds", - icon: hifi.icons.critical, - }); - messageBox.selected.connect(function(button) { - console.log("You clicked " + button) - }) - } - } - Button { - // detailedText is not currently used anywhere in Interface but it is easier to leave in and style good enough. - text: "Show Long Error" - onClicked: { - desktop.messageBox({ - informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ", - text: "Baloney", - icon: hifi.icons.warning, - detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a" - }); - } - } - */ - - // query - /* - // There is no such desktop.queryBox() function; may need to update test to cover QueryDialog.qml? - Button { - text: "Show Query" - onClicked: { - var queryBox = desktop.queryBox({ - text: "Have you stopped beating your wife?", - placeholderText: "Are you sure?", - // icon: hifi.icons.critical, - }); - queryBox.selected.connect(function(result) { - console.log("User responded with " + result); - }); - - queryBox.canceled.connect(function() { - console.log("User cancelled query box "); - }) - } - } - */ - - // Browser - /* - Button { - text: "Open Browser" - onClicked: builder.createObject(desktop); - property var builder: Component { - Browser {} - } - } - */ - - - // file dialog - /* - - Button { - text: "Open Directory" - property var builder: Component { - FileDialog { selectDirectory: true } - } - - onClicked: { - var fileDialog = builder.createObject(desktop); - fileDialog.canceled.connect(function(){ - console.log("Cancelled") - }) - fileDialog.selectedFile.connect(function(file){ - console.log("Selected " + file) - }) - } - } - Button { - text: "Open File" - property var builder: Component { - FileDialog { - title: "Open File" - filter: "All Files (*.*)" - //filter: "HTML files (*.html);;Other(*.png)" - } - } - - onClicked: { - var fileDialog = builder.createObject(desktop); - fileDialog.canceled.connect(function(){ - console.log("Cancelled") - }) - fileDialog.selectedFile.connect(function(file){ - console.log("Selected " + file) - }) - } - } - */ - - // tabs - /* - Button { - text: "Add Tab" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.addWebTab({ source: "Foo" }); - desktop.toolWindow.showTabForUrl("Foo", true); - } - } - - Button { - text: "Add Tab 2" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.addWebTab({ source: "Foo 2" }); - desktop.toolWindow.showTabForUrl("Foo 2", true); - } - } - - Button { - text: "Add Tab 3" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.addWebTab({ source: "Foo 3" }); - desktop.toolWindow.showTabForUrl("Foo 3", true); - } - } - - Button { - text: "Destroy Tab" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.removeTabForUrl("Foo"); - } - } - */ - - // Hifi specific stuff - /* - Button { - // Shows the dialog with preferences sections but not each section's preference items - // because Preferences.preferencesByCategory() method is not stubbed out. - text: "Settings > General..." - property var builder: Component { - GeneralPreferencesDialog { } - } - onClicked: { - var runningScripts = builder.createObject(desktop); - } - } - - Button { - text: "Running Scripts" - property var builder: Component { - RunningScripts { } - } - onClicked: { - var runningScripts = builder.createObject(desktop); - } - } - - Button { - text: "Attachments" - property var builder: Component { - AttachmentsDialog { } - } - onClicked: { - var attachmentsDialog = builder.createObject(desktop); - } - } - Button { - // Replicates message box that pops up after selecting new avatar. Includes title. - text: "Confirm Avatar" - onClicked: { - var messageBox = desktop.messageBox({ - title: "Set Avatar", - text: "Would you like to use 'Albert' for your avatar?", - icon: hifi.icons.question, // Test question icon - //icon: hifi.icons.information, // Test informaton icon - //icon: hifi.icons.warning, // Test warning icon - //icon: hifi.icons.critical, // Test critical icon - //icon: hifi.icons.none, // Test no icon - buttons: OriginalDialogs.StandardButton.Ok + OriginalDialogs.StandardButton.Cancel, - defaultButton: OriginalDialogs.StandardButton.Ok - }); - messageBox.selected.connect(function(button) { - console.log("You clicked " + button) - }) - } - } - */ - // bookmarks - /* - Button { - text: "Bookmark Location" - onClicked: { - desktop.inputDialog({ - title: "Bookmark Location", - icon: hifi.icons.placemark, - label: "Name" - }); - } - } - Button { - text: "Delete Bookmark" - onClicked: { - desktop.inputDialog({ - title: "Delete Bookmark", - icon: hifi.icons.placemark, - label: "Select the bookmark to delete", - items: ["Bookmark A", "Bookmark B", "Bookmark C"] - }); - } - } - Button { - text: "Duplicate Bookmark" - onClicked: { - desktop.messageBox({ - title: "Duplicate Bookmark", - icon: hifi.icons.warning, - text: "The bookmark name you entered alread exists in yoru list.", - informativeText: "Would you like to overwrite it?", - buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, - defaultButton: OriginalDialogs.StandardButton.Yes - }); - } - } - */ - - } - - - HifiConstants { id: hifi } - - Desktop { - id: desktop - anchors.fill: parent - - //rootMenu: StubMenu { id: rootMenu } - //Component.onCompleted: offscreenWindow = appWindow - - /* - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: desktop.popupMenu(Qt.vector2d(mouseX, mouseY)); - } - */ - - Browser { - url: "http://s3.amazonaws.com/DreamingContent/testUiDelegates.html" - } - - - Window { - id: blue - closable: true - visible: true - resizable: true - destroyOnHidden: false - title: "Blue" - - width: 100; height: 100 - x: 1280 / 2; y: 720 / 2 - Settings { - category: "TestWindow.Blue" - property alias x: blue.x - property alias y: blue.y - property alias width: blue.width - property alias height: blue.height - } - - Rectangle { - anchors.fill: parent - visible: true - color: "blue" - Component.onDestruction: console.log("Blue destroyed") - } - } - - Window { - id: green - closable: true - visible: true - resizable: true - title: "Green" - destroyOnHidden: false - - width: 100; height: 100 - x: 1280 / 2; y: 720 / 2 - Settings { - category: "TestWindow.Green" - property alias x: green.x - property alias y: green.y - property alias width: green.width - property alias height: green.height - } - - Rectangle { - anchors.fill: parent - visible: true - color: "green" - Component.onDestruction: console.log("Green destroyed") - } - } - -/* - Rectangle { width: 100; height: 100; x: 100; y: 100; color: "#00f" } - - Window { - id: green - alwaysOnTop: true - frame: HiddenFrame{} - hideBackground: true - closable: true - visible: true - resizable: false - x: 1280 / 2; y: 720 / 2 - width: 100; height: 100 - Rectangle { - color: "#0f0" - width: green.width; - height: green.height; - } - } - */ - -/* - Window { - id: yellow - closable: true - visible: true - resizable: true - x: 100; y: 100 - width: 100; height: 100 - Rectangle { - anchors.fill: parent - visible: true - color: "yellow" - } - } -*/ - } - - Action { - id: openBrowserAction - text: "Open Browser" - shortcut: "Ctrl+Shift+X" - onTriggered: { - builder.createObject(desktop); - } - property var builder: Component { - ModelBrowserDialog{} - } - } -} - - - - diff --git a/tests-manual/ui/qmlscratch.pro b/tests-manual/ui/qmlscratch.pro index 5c9b91ee52..3287180d26 100644 --- a/tests-manual/ui/qmlscratch.pro +++ b/tests-manual/ui/qmlscratch.pro @@ -4,34 +4,10 @@ QT += gui qml quick xml webengine widgets CONFIG += c++11 -SOURCES += src/main.cpp \ - ../../libraries/ui/src/FileDialogHelper.cpp - -HEADERS += \ - ../../libraries/ui/src/FileDialogHelper.h +SOURCES += src/main.cpp # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = ../../interface/resources/qml - DISTFILES += \ - qml/*.qml \ - ../../interface/resources/QtWebEngine/UIDelegates/original/*.qml \ - ../../interface/resources/QtWebEngine/UIDelegates/*.qml \ - ../../interface/resources/qml/*.qml \ - ../../interface/resources/qml/controls/*.qml \ - ../../interface/resources/qml/controls-uit/*.qml \ - ../../interface/resources/qml/dialogs/*.qml \ - ../../interface/resources/qml/dialogs/fileDialog/*.qml \ - ../../interface/resources/qml/dialogs/preferences/*.qml \ - ../../interface/resources/qml/dialogs/messageDialog/*.qml \ - ../../interface/resources/qml/desktop/*.qml \ - ../../interface/resources/qml/menus/*.qml \ - ../../interface/resources/qml/styles/*.qml \ - ../../interface/resources/qml/styles-uit/*.qml \ - ../../interface/resources/qml/windows/*.qml \ - ../../interface/resources/qml/hifi/*.qml \ - ../../interface/resources/qml/hifi/toolbars/*.qml \ - ../../interface/resources/qml/hifi/dialogs/*.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/*.qml \ - ../../interface/resources/qml/hifi/overlays/*.qml + qml/*.qml diff --git a/tests-manual/ui/src/main.cpp b/tests-manual/ui/src/main.cpp index 312b5f3823..a5061f4d01 100644 --- a/tests-manual/ui/src/main.cpp +++ b/tests-manual/ui/src/main.cpp @@ -3,88 +3,31 @@ #include #include -#include "../../../libraries/ui/src/FileDialogHelper.h" - - -class Preference : public QObject { - Q_OBJECT - Q_PROPERTY(QString category READ getCategory() CONSTANT) - Q_PROPERTY(QString name READ getName() CONSTANT) - Q_PROPERTY(Type type READ getType() CONSTANT) - Q_ENUMS(Type) -public: - enum Type { - Editable, - Browsable, - Spinner, - Checkbox, - }; - - Preference(QObject* parent = nullptr) : QObject(parent) {} - - Preference(const QString& category, const QString& name, QObject* parent = nullptr) - : QObject(parent), _category(category), _name(name) { } - const QString& getCategory() const { return _category; } - const QString& getName() const { return _name; } - virtual Type getType() { return Editable; } - -protected: - const QString _category; - const QString _name; -}; - -class Reticle : public QObject { - Q_OBJECT - Q_PROPERTY(QPoint position READ getPosition CONSTANT) -public: - - Reticle(QObject* parent) : QObject(parent) { - } - - QPoint getPosition() { - if (!_window) { - return QPoint(0, 0); - } - return _window->mapFromGlobal(QCursor::pos()); - } - - void setWindow(QWindow* window) { - _window = window; - } - -private: - QWindow* _window{nullptr}; -}; - QString getRelativeDir(const QString& relativePath = ".") { - QDir path(__FILE__); path.cdUp(); + QDir path(__FILE__); + path.cdUp(); + path.cdUp(); auto result = path.absoluteFilePath(relativePath); result = path.cleanPath(result) + "/"; return result; } -QString getTestQmlDir() { - return getRelativeDir("../qml"); +QString getResourcesDir() { + return getRelativeDir("../../interface/resources"); } -QString getInterfaceQmlDir() { - return getRelativeDir("/"); +QString getQmlDir() { + return getRelativeDir("../../interface/resources/qml"); } - -void setChild(QQmlApplicationEngine& engine, const char* name) { - for (auto obj : engine.rootObjects()) { - auto child = obj->findChild(QString(name)); - if (child) { - engine.rootContext()->setContextProperty(name, child); - return; - } - } - qWarning() << "Could not find object named " << name; +QString getScriptsDir() { + return getRelativeDir("../../scripts"); } void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath, bool insert = false) { QString resolvedPath = getRelativeDir(relativePath); + + qDebug() << "adding import path: " << QDir::toNativeSeparators(resolvedPath); engine.addImportPath(resolvedPath); } @@ -93,44 +36,24 @@ int main(int argc, char *argv[]) { app.setOrganizationName("Some Company"); app.setOrganizationDomain("somecompany.com"); app.setApplicationName("Amazing Application"); - QDir::setCurrent(getRelativeDir("..")); - QtWebEngine::initialize(); - qmlRegisterType("Hifi", 1, 0, "Preference"); + auto scriptsDir = getScriptsDir(); + auto resourcesDir = getResourcesDir(); QQmlApplicationEngine engine; + addImportPath(engine, "."); addImportPath(engine, "qml"); - addImportPath(engine, "../../interface/resources/qml"); - addImportPath(engine, "../../interface/resources"); - engine.load(QUrl(QStringLiteral("qml/Stubs.qml"))); + addImportPath(engine, resourcesDir); + addImportPath(engine, resourcesDir + "/qml"); + addImportPath(engine, scriptsDir); + addImportPath(engine, scriptsDir + "/developer/tests"); - setChild(engine, "offscreenFlags"); - setChild(engine, "Account"); - setChild(engine, "ApplicationCompositor"); - setChild(engine, "Controller"); - setChild(engine, "Desktop"); - setChild(engine, "ScriptDiscoveryService"); - setChild(engine, "HMD"); - setChild(engine, "GL"); - setChild(engine, "MenuHelper"); - setChild(engine, "Preferences"); - setChild(engine, "urlHandler"); - engine.rootContext()->setContextProperty("DebugQML", true); - engine.rootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); + QFontDatabase::addApplicationFont(resourcesDir + "/fonts/FiraSans-Regular.ttf"); + QFontDatabase::addApplicationFont(resourcesDir + "/fonts/FiraSans-SemiBold.ttf"); + QFontDatabase::addApplicationFont(resourcesDir + "/fonts/hifi-glyphs.ttf"); - //engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml"))); - engine.load(QUrl(QStringLiteral("qml/main.qml"))); - for (QObject* rootObject : engine.rootObjects()) { - if (rootObject->objectName() == "MainWindow") { - Reticle* reticle = new Reticle(rootObject); - reticle->setWindow((QWindow*)rootObject); - engine.rootContext()->setContextProperty("Reticle", reticle); - engine.rootContext()->setContextProperty("Window", rootObject); - break; - } - } + auto url = getRelativeDir(".") + "qml/ControlsGalleryWindow.qml"; + + engine.load(url); return app.exec(); } - -#include "main.moc" - diff --git a/tests/animation/src/AnimInverseKinematicsTests.cpp b/tests/animation/src/AnimInverseKinematicsTests.cpp index f5d3597f56..910770bb0d 100644 --- a/tests/animation/src/AnimInverseKinematicsTests.cpp +++ b/tests/animation/src/AnimInverseKinematicsTests.cpp @@ -28,8 +28,8 @@ const glm::quat identity = glm::quat(); const glm::quat quaterTurnAroundZ = glm::angleAxis(0.5f * PI, zAxis); -void makeTestFBXJoints(FBXGeometry& geometry) { - FBXJoint joint; +void makeTestFBXJoints(HFMModel& hfmModel) { + HFMJoint joint; joint.isFree = false; joint.freeLineage.clear(); joint.parentIndex = -1; @@ -60,29 +60,29 @@ void makeTestFBXJoints(FBXGeometry& geometry) { joint.name = "A"; joint.parentIndex = -1; joint.translation = origin; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); joint.name = "B"; joint.parentIndex = 0; joint.translation = xAxis; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); joint.name = "C"; joint.parentIndex = 1; joint.translation = xAxis; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); joint.name = "D"; joint.parentIndex = 2; joint.translation = xAxis; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); // compute each joint's transform - for (int i = 1; i < (int)geometry.joints.size(); ++i) { - FBXJoint& j = geometry.joints[i]; + for (int i = 1; i < (int)hfmModel.joints.size(); ++i) { + HFMJoint& j = hfmModel.joints[i]; int parentIndex = j.parentIndex; // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) - j.transform = geometry.joints[parentIndex].transform * + j.transform = hfmModel.joints[parentIndex].transform * glm::translate(j.translation) * j.preTransform * glm::mat4_cast(j.preRotation * j.rotation * j.postRotation) * @@ -96,12 +96,12 @@ void AnimInverseKinematicsTests::testSingleChain() { AnimContext context(false, false, false, glm::mat4(), glm::mat4()); - FBXGeometry geometry; - makeTestFBXJoints(geometry); + HFMModel hfmModel; + makeTestFBXJoints(hfmModel); // create a skeleton and doll AnimPose offset; - AnimSkeleton::Pointer skeletonPtr = std::make_shared(geometry); + AnimSkeleton::Pointer skeletonPtr = std::make_shared(hfmModel); AnimInverseKinematics ikDoll("doll"); ikDoll.setSkeleton(skeletonPtr); @@ -119,7 +119,7 @@ void AnimInverseKinematicsTests::testSingleChain() { poses.push_back(pose); pose.trans() = xAxis; - for (int i = 1; i < (int)geometry.joints.size(); ++i) { + for (int i = 1; i < (int)hfmModel.joints.size(); ++i) { poses.push_back(pose); } ikDoll.loadPoses(poses); diff --git a/tests/recording/src/main.cpp b/tests/recording/src/main.cpp index 1b4e5adf6d..6181906ff0 100644 --- a/tests/recording/src/main.cpp +++ b/tests/recording/src/main.cpp @@ -1,8 +1,28 @@ +// +// main.cpp +// tests/recording/src +// +// Created by Bradley Austin Davis on 11/06/15. +// 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 +// + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + #include #include #include #include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + #ifdef Q_OS_WIN32 #include #endif diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index 08678c1c26..a2bba2a425 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -3,7 +3,17 @@ macro (setup_testcase_dependencies) # link in the shared libraries link_hifi_libraries(shared test-utils gpu shaders gl ${PLATFORM_GL_BACKEND}) + #target_spirv() + + find_package(spirv_cross_core REQUIRED) + find_package(spirv_cross_reflect REQUIRED) + find_package(spirv_cross_glsl REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE $) + target_include_directories(${TARGET_NAME} PRIVATE $ENV{VULKAN_SDK}/Include) + + package_libraries_for_deployment() endmacro () setup_hifi_testcase(Gui) + diff --git a/tests/shaders/src/ShaderTests.cpp b/tests/shaders/src/ShaderTests.cpp index 03dc034cd0..56b92ed047 100644 --- a/tests/shaders/src/ShaderTests.cpp +++ b/tests/shaders/src/ShaderTests.cpp @@ -31,22 +31,39 @@ #include #include +#define RUNTIME_SHADER_COMPILE_TEST 1 + +#if RUNTIME_SHADER_COMPILE_TEST +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define VCPKG_LIB_DIR "D:/hifi/vcpkg/hifi/installed/x64-windows/debug/lib/" +#else +#define VCPKG_LIB_DIR "D:/hifi/vcpkg/hifi/installed/x64-windows/lib/" +#endif + +#pragma comment(lib, VCPKG_LIB_DIR "shaderc_combined.lib") +#pragma comment(lib, VCPKG_LIB_DIR "spirv-cross-core.lib") +#pragma comment(lib, VCPKG_LIB_DIR "spirv-cross-glsl.lib") +#pragma comment(lib, VCPKG_LIB_DIR "spirv-cross-reflect.lib") + +#endif + QTEST_MAIN(ShaderTests) -#pragma optimize("", off) void ShaderTests::initTestCase() { - _window = new QWindow(); - _window->setSurfaceType(QSurface::SurfaceType::OpenGLSurface); - _context = new ::gl::Context(_window); + _context = new ::gl::OffscreenContext(); getDefaultOpenGLSurfaceFormat(); _context->create(); if (!_context->makeCurrent()) { qFatal("Unable to make test GL context current"); } - QOpenGLContextWrapper(_context->qglContext()).makeCurrent(_window); - if (!_context->makeCurrent()) { - qFatal("Unable to make test GL context current"); - } + QOpenGLContextWrapper(_context->qglContext()).makeCurrent(_context->getWindow()); gl::initModuleGl(); if (!_context->makeCurrent()) { qFatal("Unable to make test GL context current"); @@ -62,6 +79,8 @@ void ShaderTests::cleanupTestCase() { qDebug() << "Done"; } +#if RUNTIME_SHADER_COMPILE_TEST + template QStringList toQStringList(const C& c) { QStringList result; @@ -80,7 +99,7 @@ std::unordered_set toStringSet(const C& c, F f) { return result; } -template +template bool isSubset(const C& parent, const C& child) { for (const auto& v : child) { if (0 == parent.count(v)) { @@ -90,6 +109,7 @@ bool isSubset(const C& parent, const C& child) { return true; } +#if 0 gpu::Shader::ReflectionMap mergeReflection(const std::initializer_list& list) { gpu::Shader::ReflectionMap result; std::unordered_map> usedLocationsByType; @@ -120,65 +140,7 @@ gpu::Shader::ReflectionMap mergeReflection(const std::initializer_list -std::unordered_map invertMap(const std::unordered_map& map) { - std::unordered_map result; - for (const auto& entry : map) { - result[entry.second] = entry.first; - } - return result; -} - -static void verifyBindings(const gpu::Shader::Source& source) { - const auto reflection = source.getReflection(); - for (const auto& entry : reflection) { - const auto& map = entry.second; - const auto reverseMap = invertMap(map); - if (map.size() != reverseMap.size()) { - QFAIL("Bindings are not unique"); - } - } - -} - - -static void verifyInterface(const gpu::Shader::Source& vertexSource, const gpu::Shader::Source& fragmentSource) { - if (0 == fragmentSource.getReflection().count(gpu::Shader::BindingType::INPUT)) { - return; - } - auto fragIn = fragmentSource.getReflection().at(gpu::Shader::BindingType::INPUT); - if (0 == vertexSource.getReflection().count(gpu::Shader::BindingType::OUTPUT)) { - qDebug() << "No vertex output for fragment input"; - //QFAIL("No vertex output for fragment input"); - return; - } - auto vout = vertexSource.getReflection().at(gpu::Shader::BindingType::OUTPUT); - auto vrev = invertMap(vout); - static const std::string IN_STEREO_SIDE_STRING = "_inStereoSide"; - for (const auto entry : fragIn) { - const auto& name = entry.first; - // The presence of "_inStereoSide" in fragment shaders is a bug due to the way we do reflection - // and use preprocessor macros in the shaders - if (name == IN_STEREO_SIDE_STRING) { - continue; - } - if (0 == vout.count(name)) { - qDebug() << "Vertex output missing"; - //QFAIL("Vertex output missing"); - continue; - } - const auto& inLocation = entry.second; - const auto& outLocation = vout.at(name); - if (inLocation != outLocation) { - qDebug() << "Mismatch in vertex / fragment interface"; - //QFAIL("Mismatch in vertex / fragment interface"); - continue; - } - } -} - -template +template bool compareBindings(const C& actual, const gpu::Shader::LocationMap& expected) { if (actual.size() != expected.size()) { auto actualNames = toStringSet(actual, [](const auto& v) { return v.name; }); @@ -192,124 +154,363 @@ bool compareBindings(const C& actual, const gpu::Shader::LocationMap& expected) return true; } -void ShaderTests::testShaderLoad() { - std::set usedShaders; - uint32_t maxShader = 0; - try { - -#if 0 - uint32_t testPrograms[] = { - shader::render_utils::program::parabola, - shader::INVALID_PROGRAM, - }; -#else - const auto& testPrograms = shader::all_programs; #endif - size_t index = 0; - while (shader::INVALID_PROGRAM != testPrograms[index]) { - auto programId = testPrograms[index]; - ++index; +#endif - uint32_t vertexId = shader::getVertexId(programId); - uint32_t fragmentId = shader::getFragmentId(programId); - usedShaders.insert(vertexId); - usedShaders.insert(fragmentId); - maxShader = std::max(maxShader, std::max(fragmentId, vertexId)); - auto vertexSource = gpu::Shader::getShaderSource(vertexId); - QVERIFY(!vertexSource.getCode().empty()); - verifyBindings(vertexSource); - auto fragmentSource = gpu::Shader::getShaderSource(fragmentId); - QVERIFY(!fragmentSource.getCode().empty()); - verifyBindings(fragmentSource); +template +std::unordered_map invertMap(const std::unordered_map& map) { + std::unordered_map result; + for (const auto& entry : map) { + result[entry.second] = entry.first; + } + if (result.size() != map.size()) { + throw std::runtime_error("Map inversion failure, result size does not match input size"); + } + return result; +} + +static void verifyInterface(const gpu::Shader::Source& vertexSource, + const gpu::Shader::Source& fragmentSource, + shader::Dialect dialect, + shader::Variant variant) { + const auto& fragmentReflection = fragmentSource.getReflection(dialect, variant); + if (fragmentReflection.inputs.empty()) { + return; + } + + const auto& vertexReflection = vertexSource.getReflection(dialect, variant); + const auto& fragIn = fragmentReflection.inputs; + if (vertexReflection.outputs.empty()) { + throw std::runtime_error("No vertex outputs for fragment inputs"); + } + + const auto& vout = vertexReflection.outputs; + auto vrev = invertMap(vout); + for (const auto entry : fragIn) { + const auto& name = entry.first; + if (0 == vout.count(name)) { + throw std::runtime_error("Vertex outputs missing"); + } + const auto& inLocation = entry.second; + const auto& outLocation = vout.at(name); + if (inLocation != outLocation) { + throw std::runtime_error("Mismatch in vertex / fragment interface"); + } + } +} + +static void verifyInterface(const gpu::Shader::Source& vertexSource, const gpu::Shader::Source& fragmentSource) { + for (const auto& dialect : shader::allDialects()) { + for (const auto& variant : shader::allVariants()) { + verifyInterface(vertexSource, fragmentSource, dialect, variant); + } + } +} + +#if RUNTIME_SHADER_COMPILE_TEST + +void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources) { + glslCompilerResources->maxLights = 32; + glslCompilerResources->maxClipPlanes = 6; + glslCompilerResources->maxTextureUnits = 32; + glslCompilerResources->maxTextureCoords = 32; + glslCompilerResources->maxVertexAttribs = 64; + glslCompilerResources->maxVertexUniformComponents = 4096; + glslCompilerResources->maxVaryingFloats = 64; + glslCompilerResources->maxVertexTextureImageUnits = 32; + glslCompilerResources->maxCombinedTextureImageUnits = 80; + glslCompilerResources->maxTextureImageUnits = 32; + glslCompilerResources->maxFragmentUniformComponents = 4096; + glslCompilerResources->maxDrawBuffers = 32; + glslCompilerResources->maxVertexUniformVectors = 128; + glslCompilerResources->maxVaryingVectors = 8; + glslCompilerResources->maxFragmentUniformVectors = 16; + glslCompilerResources->maxVertexOutputVectors = 16; + glslCompilerResources->maxFragmentInputVectors = 15; + glslCompilerResources->minProgramTexelOffset = -8; + glslCompilerResources->maxProgramTexelOffset = 7; + glslCompilerResources->maxClipDistances = 8; + glslCompilerResources->maxComputeWorkGroupCountX = 65535; + glslCompilerResources->maxComputeWorkGroupCountY = 65535; + glslCompilerResources->maxComputeWorkGroupCountZ = 65535; + glslCompilerResources->maxComputeWorkGroupSizeX = 1024; + glslCompilerResources->maxComputeWorkGroupSizeY = 1024; + glslCompilerResources->maxComputeWorkGroupSizeZ = 64; + glslCompilerResources->maxComputeUniformComponents = 1024; + glslCompilerResources->maxComputeTextureImageUnits = 16; + glslCompilerResources->maxComputeImageUniforms = 8; + glslCompilerResources->maxComputeAtomicCounters = 8; + glslCompilerResources->maxComputeAtomicCounterBuffers = 1; + glslCompilerResources->maxVaryingComponents = 60; + glslCompilerResources->maxVertexOutputComponents = 64; + glslCompilerResources->maxGeometryInputComponents = 64; + glslCompilerResources->maxGeometryOutputComponents = 128; + glslCompilerResources->maxFragmentInputComponents = 128; + glslCompilerResources->maxImageUnits = 8; + glslCompilerResources->maxCombinedImageUnitsAndFragmentOutputs = 8; + glslCompilerResources->maxCombinedShaderOutputResources = 8; + glslCompilerResources->maxImageSamples = 0; + glslCompilerResources->maxVertexImageUniforms = 0; + glslCompilerResources->maxTessControlImageUniforms = 0; + glslCompilerResources->maxTessEvaluationImageUniforms = 0; + glslCompilerResources->maxGeometryImageUniforms = 0; + glslCompilerResources->maxFragmentImageUniforms = 8; + glslCompilerResources->maxCombinedImageUniforms = 8; + glslCompilerResources->maxGeometryTextureImageUnits = 16; + glslCompilerResources->maxGeometryOutputVertices = 256; + glslCompilerResources->maxGeometryTotalOutputComponents = 1024; + glslCompilerResources->maxGeometryUniformComponents = 1024; + glslCompilerResources->maxGeometryVaryingComponents = 64; + glslCompilerResources->maxTessControlInputComponents = 128; + glslCompilerResources->maxTessControlOutputComponents = 128; + glslCompilerResources->maxTessControlTextureImageUnits = 16; + glslCompilerResources->maxTessControlUniformComponents = 1024; + glslCompilerResources->maxTessControlTotalOutputComponents = 4096; + glslCompilerResources->maxTessEvaluationInputComponents = 128; + glslCompilerResources->maxTessEvaluationOutputComponents = 128; + glslCompilerResources->maxTessEvaluationTextureImageUnits = 16; + glslCompilerResources->maxTessEvaluationUniformComponents = 1024; + glslCompilerResources->maxTessPatchComponents = 120; + glslCompilerResources->maxPatchVertices = 32; + glslCompilerResources->maxTessGenLevel = 64; + glslCompilerResources->maxViewports = 16; + glslCompilerResources->maxVertexAtomicCounters = 0; + glslCompilerResources->maxTessControlAtomicCounters = 0; + glslCompilerResources->maxTessEvaluationAtomicCounters = 0; + glslCompilerResources->maxGeometryAtomicCounters = 0; + glslCompilerResources->maxFragmentAtomicCounters = 8; + glslCompilerResources->maxCombinedAtomicCounters = 8; + glslCompilerResources->maxAtomicCounterBindings = 1; + glslCompilerResources->maxVertexAtomicCounterBuffers = 0; + glslCompilerResources->maxTessControlAtomicCounterBuffers = 0; + glslCompilerResources->maxTessEvaluationAtomicCounterBuffers = 0; + glslCompilerResources->maxGeometryAtomicCounterBuffers = 0; + glslCompilerResources->maxFragmentAtomicCounterBuffers = 1; + glslCompilerResources->maxCombinedAtomicCounterBuffers = 1; + glslCompilerResources->maxAtomicCounterBufferSize = 16384; + glslCompilerResources->maxTransformFeedbackBuffers = 4; + glslCompilerResources->maxTransformFeedbackInterleavedComponents = 64; + glslCompilerResources->maxCullDistances = 8; + glslCompilerResources->maxCombinedClipAndCullDistances = 8; + glslCompilerResources->maxSamples = 4; + glslCompilerResources->limits.nonInductiveForLoops = 1; + glslCompilerResources->limits.whileLoops = 1; + glslCompilerResources->limits.doWhileLoops = 1; + glslCompilerResources->limits.generalUniformIndexing = 1; + glslCompilerResources->limits.generalAttributeMatrixVectorIndexing = 1; + glslCompilerResources->limits.generalVaryingIndexing = 1; + glslCompilerResources->limits.generalSamplerIndexing = 1; + glslCompilerResources->limits.generalVariableIndexing = 1; + glslCompilerResources->limits.generalConstantMatrixVectorIndexing = 1; +} + +void writeSpirv(const std::string& filename, const std::vector& spirv) { + std::ofstream o(filename, std::ios::trunc | std::ios::binary); + for (const auto& word : spirv) { + o.write((const char*)&word, sizeof(word)); + } + o.close(); +} + +void write(const std::string& filename, const std::string& text) { + std::ofstream o(filename, std::ios::trunc); + o.write(text.c_str(), text.size()); + o.close(); +} + +bool endsWith(const std::string& s, const std::string& f) { + auto end = s.substr(s.size() - f.size()); + return (end == f); +} + +EShLanguage getShaderStage(const std::string& shaderName) { + static const std::string VERT_EXT{ ".vert" }; + static const std::string FRAG_EXT{ ".frag" }; + static const std::string GEOM_EXT{ ".geom" }; + static const size_t EXT_SIZE = VERT_EXT.size(); + if (shaderName.size() < EXT_SIZE) { + throw std::runtime_error("Invalid shader name"); + } + std::string ext = shaderName.substr(shaderName.size() - EXT_SIZE); + if (ext == VERT_EXT) { + return EShLangVertex; + } else if (ext == FRAG_EXT) { + return EShLangFragment; + } else if (ext == GEOM_EXT) { + return EShLangGeometry; + } + throw std::runtime_error("Invalid shader name"); +} + +void rebuildSource(shader::Dialect dialect, shader::Variant variant, const shader::Source& source) { + try { + using namespace glslang; + static const EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); + auto shaderName = source.name; + const std::string baseOutName = "d:/shaders/" + shaderName; + auto stage = getShaderStage(shaderName); + + TShader shader(stage); + std::vector strings; + const auto& dialectVariantSource = source.dialectSources.at(dialect).variantSources.at(variant); + strings.push_back(dialectVariantSource.scribe.c_str()); + shader.setStrings(strings.data(), (int)strings.size()); + shader.setEnvInput(EShSourceGlsl, stage, EShClientOpenGL, 450); + shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_1); + shader.setEnvTarget(EShTargetSpv, EShTargetSpv_1_3); + + bool success = shader.parse(&glslCompilerResources, 450, false, messages); + if (!success) { + qWarning() << "Failed to parse shader " << shaderName.c_str(); + qWarning() << shader.getInfoLog(); + qWarning() << shader.getInfoDebugLog(); + throw std::runtime_error("Wrong"); + } + + // Create and link a shader program containing the single shader + glslang::TProgram program; + program.addShader(&shader); + if (!program.link(messages)) { + qWarning() << "Failed to compile shader " << shaderName.c_str(); + qWarning() << program.getInfoLog(); + qWarning() << program.getInfoDebugLog(); + throw std::runtime_error("Wrong"); + } + + // Output the SPIR-V code from the shader program + std::vector spirv; + glslang::GlslangToSpv(*program.getIntermediate(stage), spirv); + + spvtools::SpirvTools core(SPV_ENV_VULKAN_1_1); + spvtools::Optimizer opt(SPV_ENV_VULKAN_1_1); + + auto outputLambda = [](spv_message_level_t, const char*, const spv_position_t&, const char* m) { qWarning() << m; }; + core.SetMessageConsumer(outputLambda); + opt.SetMessageConsumer(outputLambda); + + if (!core.Validate(spirv)) { + throw std::runtime_error("invalid spirv"); + } + writeSpirv(baseOutName + ".spv", spirv); + + opt.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass()) + .RegisterPass(spvtools::CreateStrengthReductionPass()) + .RegisterPass(spvtools::CreateEliminateDeadConstantPass()) + .RegisterPass(spvtools::CreateEliminateDeadFunctionsPass()) + .RegisterPass(spvtools::CreateUnifyConstantPass()); + + std::vector optspirv; + if (!opt.Run(spirv.data(), spirv.size(), &optspirv)) { + throw std::runtime_error("bad optimize run"); + } + writeSpirv(baseOutName + ".opt.spv", optspirv); + + std::string disassembly; + if (!core.Disassemble(optspirv, &disassembly)) { + throw std::runtime_error("bad disassembly"); + } + + write(baseOutName + ".spv.txt", disassembly); + } catch (const std::runtime_error& error) { + qWarning() << error.what(); + } +} +#endif + +void validateDialectVariantSource(const shader::DialectVariantSource& source) { + if (source.scribe.empty()) { + throw std::runtime_error("Missing scribe source"); + } + + if (source.spirv.empty()) { + throw std::runtime_error("Missing SPIRV"); + } + + if (source.glsl.empty()) { + throw std::runtime_error("Missing GLSL"); + } +} + +void validaDialectSource(const shader::DialectSource& dialectSource) { + for (const auto& variant : shader::allVariants()) { + validateDialectVariantSource(dialectSource.variantSources.find(variant)->second); + } +} + +void validateSource(const shader::Source& shader) { + if (shader.id == shader::INVALID_SHADER) { + throw std::runtime_error("Missing stored shader ID"); + } + + if (shader.name.empty()) { + throw std::runtime_error("Missing shader name"); + } + + static const auto& dialects = shader::allDialects(); + for (const auto dialect : dialects) { + if (!shader.dialectSources.count(dialect)) { + throw std::runtime_error("Missing platform shader"); + } + const auto& platformShader = shader.dialectSources.find(dialect)->second; + validaDialectSource(platformShader); + } +} + +void ShaderTests::testShaderLoad() { + try { + const auto& shaderIds = shader::allShaders(); + + // For debugging compile or link failures on individual programs, enable the following block and change the array values + // Be sure to end with the sentinal value of shader::INVALID_PROGRAM + const auto& programIds = shader::allPrograms(); + + for (auto shaderId : shaderIds) { + validateSource(shader::Source::get(shaderId)); + } + + { + std::unordered_set programUsedShaders; +#pragma omp parallel for + for (auto programId : programIds) { + auto vertexId = shader::getVertexId(programId); + shader::Source::get(vertexId); + programUsedShaders.insert(vertexId); + auto fragmentId = shader::getFragmentId(programId); + shader::Source::get(fragmentId); + programUsedShaders.insert(fragmentId); + } + + for (const auto& shaderId : shaderIds) { + if (programUsedShaders.count(shaderId)) { + continue; + } + const auto& shader = shader::Source::get(shaderId); + qDebug() << "Unused shader found" << shader.name.c_str(); + } + } + + // Traverse all programs again to do program level tests + for (const auto& programId : programIds) { + auto vertexId = shader::getVertexId(programId); + const auto& vertexSource = shader::Source::get(vertexId); + auto fragmentId = shader::getFragmentId(programId); + const auto& fragmentSource = shader::Source::get(fragmentId); verifyInterface(vertexSource, fragmentSource); - auto expectedBindings = mergeReflection({ vertexSource, fragmentSource }); - auto program = gpu::Shader::createProgram(programId); auto glBackend = std::static_pointer_cast(_gpuContext->getBackend()); auto glshader = gpu::gl::GLShader::sync(*glBackend, *program); + if (!glshader) { - qWarning() << "Failed to compile or link vertex " << vertexId << " fragment " << fragmentId; + qWarning() << "Failed to compile or link vertex " << vertexSource.name.c_str() << " fragment " + << fragmentSource.name.c_str(); QFAIL("Program link error"); } - - QVERIFY(glshader != nullptr); - for (const auto& shaderObject : glshader->_shaderObjects) { - const auto& program = shaderObject.glprogram; - - // Uniforms - { - auto uniforms = gl::Uniform::load(program); - const auto& uniformRemap = shaderObject.uniformRemap; - auto expectedUniforms = expectedBindings[gpu::Shader::BindingType::UNIFORM]; - if (!compareBindings(uniforms, expectedUniforms)) { - qDebug() << "Uniforms mismatch"; - } - for (const auto& uniform : uniforms) { - if (0 != expectedUniforms.count(uniform.name)) { - auto expectedLocation = expectedUniforms[uniform.name]; - if (0 != uniformRemap.count(expectedLocation)) { - expectedLocation = uniformRemap.at(expectedLocation); - } - QVERIFY(expectedLocation == uniform.binding); - } - } - } - - // Textures - { - auto textures = gl::Uniform::loadTextures(program); - auto expiredBegin = std::remove_if(textures.begin(), textures.end(), [&](const gl::Uniform& uniform) -> bool { - return uniform.name == "transformObjectBuffer"; - }); - textures.erase(expiredBegin, textures.end()); - - const auto expectedTextures = expectedBindings[gpu::Shader::BindingType::TEXTURE]; - if (!compareBindings(textures, expectedTextures)) { - qDebug() << "Textures mismatch"; - } - for (const auto& texture : textures) { - if (0 != expectedTextures.count(texture.name)) { - const auto& location = texture.binding; - const auto& expectedUnit = expectedTextures.at(texture.name); - GLint actualUnit = -1; - glGetUniformiv(program, location, &actualUnit); - QVERIFY(expectedUnit == actualUnit); - } - } - } - - // UBOs - { - auto ubos = gl::UniformBlock::load(program); - auto expectedUbos = expectedBindings[gpu::Shader::BindingType::UNIFORM_BUFFER]; - if (!compareBindings(ubos, expectedUbos)) { - qDebug() << "UBOs mismatch"; - } - for (const auto& ubo : ubos) { - if (0 != expectedUbos.count(ubo.name)) { - QVERIFY(expectedUbos[ubo.name] == ubo.binding); - } - } - } - - // FIXME add storage buffer validation - } } } catch (const std::runtime_error& error) { QFAIL(error.what()); } - for (uint32_t i = 1; i <= maxShader; ++i) { - auto used = usedShaders.count(i); - if (0 != usedShaders.count(i)) { - continue; - } - auto reflectionJson = shader::loadShaderReflection(i); - auto name = QJsonDocument::fromJson(reflectionJson.c_str()).object()["name"].toString(); - qDebug() << "Unused shader" << name; - } - qDebug() << "Completed all shaders"; } diff --git a/tests/shaders/src/ShaderTests.h b/tests/shaders/src/ShaderTests.h index d109341c1f..fd361d9e60 100644 --- a/tests/shaders/src/ShaderTests.h +++ b/tests/shaders/src/ShaderTests.h @@ -23,9 +23,8 @@ private slots: void testShaderLoad(); private: - QWindow* _window{ nullptr }; - gl::Context* _context{ nullptr }; + gl::OffscreenContext* _context{ nullptr }; gpu::ContextPointer _gpuContext; }; -#endif // hifi_ViewFruxtumTests_h +#endif // hifi_ViewFruxtumTests_h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 9b36180bc2..6ec9125ce8 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,11 +1,6 @@ # add the tool directories -add_subdirectory(scribe) -set_target_properties(scribe PROPERTIES FOLDER "Tools") - -add_subdirectory(shreflect) -set_target_properties(shreflect PROPERTIES FOLDER "Tools") - find_npm() + if (NPM_EXECUTABLE) add_subdirectory(jsdoc) set_target_properties(jsdoc PROPERTIES FOLDER "Tools") diff --git a/tools/auto-tester/AppDataHighFidelity/Interface.json b/tools/auto-tester/AppDataHighFidelity/Interface.json new file mode 100644 index 0000000000..ca91a092c8 --- /dev/null +++ b/tools/auto-tester/AppDataHighFidelity/Interface.json @@ -0,0 +1,285 @@ +{ + "AddressManager/address": "hifi://localhost/1.07449e-05,0,-0.0174923/0,0,0,1", + "Audio/Desktop/INPUT": "Microphone (Realtek Audio)", + "Audio/Desktop/OUTPUT": "Speakers / Headphones (Realtek Audio)", + "Audio/VR/INPUT": "Microphone (Realtek Audio)", + "Audio/VR/OUTPUT": "Speakers / Headphones (Realtek Audio)", + "Avatar/Avatar/fullAvatarURL": "", + "Avatar/Debug Draw Animation": false, + "Avatar/Debug Draw Base of Support": false, + "Avatar/Debug Draw Default Pose": false, + "Avatar/Debug Draw Position": false, + "Avatar/Disable Eyelid Adjustment": false, + "Avatar/Draw Mesh": true, + "Avatar/Enable Default Motor Control": true, + "Avatar/Enable Inverse Kinematics": true, + "Avatar/Enable LookAt Snapping": true, + "Avatar/Enable Scripted Motor Control": true, + "Avatar/Face Tracking/Auto Mute Microphone": false, + "Avatar/Face Tracking/Binary Eyelid Control": true, + "Avatar/Face Tracking/Couple Eyelids": true, + "Avatar/Face Tracking/Mute Face Tracking": true, + "Avatar/Face Tracking/None": false, + "Avatar/Face Tracking/Use Audio for Mouth": true, + "Avatar/Face Tracking/Use Camera": true, + "Avatar/Face Tracking/Velocity Filter": true, + "Avatar/Fix Gaze (no saccade)": false, + "Avatar/Show Bounding Collision Shapes": false, + "Avatar/Show Detailed Collision": false, + "Avatar/Show IK Chains": false, + "Avatar/Show IK Constraints": false, + "Avatar/Show IK Targets": false, + "Avatar/Show My Eye Vectors": false, + "Avatar/Show Other Eye Vectors": false, + "Avatar/Show Receive Stats": false, + "Avatar/Show SensorToWorld Matrix": false, + "Avatar/Toggle Hips Following": false, + "Avatar/Turn using Head": false, + "Avatar/animGraphURL": "", + "Avatar/attachmentData/size": 0, + "Avatar/avatarEntityData/size": 0, + "Avatar/collisionSoundURL": "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav", + "Avatar/displayName": "", + "Avatar/dominantHand": "right", + "Avatar/flyingHMD": false, + "Avatar/fullAvatarModelName": "Default", + "Avatar/fullAvatarURL": "", + "Avatar/headPitch": 0, + "Avatar/pitchSpeed": 75, + "Avatar/scale": 1, + "Avatar/useSnapTurn": true, + "Avatar/userHeight": 1.7549999952316284, + "Avatar/yawSpeed": 100, + "Developer/Avatar/Debug Draw Animation": false, + "Developer/Avatar/Debug Draw Base of Support": false, + "Developer/Avatar/Debug Draw Default Pose": false, + "Developer/Avatar/Debug Draw Position": false, + "Developer/Avatar/Disable Eyelid Adjustment": false, + "Developer/Avatar/Draw Mesh": true, + "Developer/Avatar/Enable Default Motor Control": true, + "Developer/Avatar/Enable Inverse Kinematics": true, + "Developer/Avatar/Enable LookAt Snapping": true, + "Developer/Avatar/Enable Scripted Motor Control": true, + "Developer/Avatar/Face Tracking/Auto Mute Microphone": false, + "Developer/Avatar/Face Tracking/Binary Eyelid Control": true, + "Developer/Avatar/Face Tracking/Couple Eyelids": true, + "Developer/Avatar/Face Tracking/Mute Face Tracking": true, + "Developer/Avatar/Face Tracking/None": false, + "Developer/Avatar/Face Tracking/Use Audio for Mouth": true, + "Developer/Avatar/Face Tracking/Use Camera": true, + "Developer/Avatar/Face Tracking/Velocity Filter": true, + "Developer/Avatar/Fix Gaze (no saccade)": false, + "Developer/Avatar/Show Bounding Collision Shapes": false, + "Developer/Avatar/Show Detailed Collision": false, + "Developer/Avatar/Show IK Chains": false, + "Developer/Avatar/Show IK Constraints": false, + "Developer/Avatar/Show IK Targets": false, + "Developer/Avatar/Show My Eye Vectors": false, + "Developer/Avatar/Show Other Eye Vectors": false, + "Developer/Avatar/Show Receive Stats": false, + "Developer/Avatar/Show SensorToWorld Matrix": false, + "Developer/Avatar/Toggle Hips Following": false, + "Developer/Avatar/Turn using Head": false, + "Developer/Debug defaultScripts.js": false, + "Developer/Display Crash Options": true, + "Developer/Enable Speech Control API": false, + "Developer/Entities/Show Realtime Entity Stats": false, + "Developer/Hands/Show Hand Targets": false, + "Developer/Network/Disable Activity Logger": false, + "Developer/Network/Send wrong DS connect version": false, + "Developer/Network/Send wrong protocol version": false, + "Developer/Physics/Highlight Simulation Ownership": false, + "Developer/Physics/Show Bullet Bounding Boxes": false, + "Developer/Physics/Show Bullet Collision": false, + "Developer/Physics/Show Bullet Constraint Limits": false, + "Developer/Physics/Show Bullet Constraints": false, + "Developer/Physics/Show Bullet Contact Points": false, + "Developer/Picking/Force Coarse Picking": false, + "Developer/Render/Ambient Occlusion": false, + "Developer/Render/Compute Blendshapes": true, + "Developer/Render/Decimate Textures": false, + "Developer/Render/Default Skybox": true, + "Developer/Render/Enable Sparse Texture Management": true, + "Developer/Render/Maximum Texture Memory/1024 MB": false, + "Developer/Render/Maximum Texture Memory/2048 MB": false, + "Developer/Render/Maximum Texture Memory/256 MB": false, + "Developer/Render/Maximum Texture Memory/4 MB": false, + "Developer/Render/Maximum Texture Memory/4096 MB": false, + "Developer/Render/Maximum Texture Memory/512 MB": false, + "Developer/Render/Maximum Texture Memory/6144 MB": false, + "Developer/Render/Maximum Texture Memory/64 MB": false, + "Developer/Render/Maximum Texture Memory/8192 MB": false, + "Developer/Render/Maximum Texture Memory/Automatic Texture Memory": true, + "Developer/Render/OpenVR Threaded Submit": true, + "Developer/Render/Scale Resolution/1": true, + "Developer/Render/Scale Resolution/1/2": false, + "Developer/Render/Scale Resolution/1/3": false, + "Developer/Render/Scale Resolution/1/4": false, + "Developer/Render/Scale Resolution/2/3": false, + "Developer/Render/Shadows": true, + "Developer/Render/Temporal Antialiasing (FXAA if disabled)": true, + "Developer/Render/Throttle FPS If Not Focus": true, + "Developer/Render/World Axes": false, + "Developer/Show Animation Stats": false, + "Developer/Show Overlays": true, + "Developer/Show Statistics": false, + "Developer/Timing/Log Extra Timing Details": false, + "Developer/Timing/Log Render Pipeline Warnings": false, + "Developer/Timing/Performance Timer/Display Timing Details": false, + "Developer/Timing/Performance Timer/Expand /myAvatar": false, + "Developer/Timing/Performance Timer/Expand /myAvatar/simulation": false, + "Developer/Timing/Performance Timer/Expand /otherAvatar": false, + "Developer/Timing/Performance Timer/Expand /paintGL": false, + "Developer/Timing/Performance Timer/Expand /physics": false, + "Developer/Timing/Performance Timer/Expand /simulation": false, + "Developer/Timing/Performance Timer/Expand /update": false, + "Developer/Timing/Performance Timer/Only Display Top Ten": true, + "Developer/Timing/Show Timer": false, + "Developer/Timing/Suppress Timings Less than 10ms": false, + "Developer/UI/Desktop Tablet Becomes Toolbar": true, + "Developer/UI/HMD Tablet Becomes Toolbar": false, + "Developer/Verbose Logging": false, + "Display/3D TV - Interleaved": false, + "Display/3D TV - Side by Side Stereo": false, + "Display/Desktop": true, + "Display/Fullscreen": false, + "Display/Oculus Rift": false, + "Display/Oculus Rift (Simulator)": false, + "Edit/Allow Selecting of Large Models": true, + "Edit/Allow Selecting of Lights": true, + "Edit/Allow Selecting of Small Models": true, + "Edit/Auto Focus on Select": false, + "Edit/Create Entities As Grabbable (except Zones, Particles, and Lights)": true, + "Edit/Ease Orientation on Focus": false, + "Edit/Show Lights and Particle Systems in Create Mode": true, + "Edit/Show Zones in Create Mode": true, + "Entities/Show Realtime Entity Stats": false, + "Face Tracking/Auto Mute Microphone": false, + "Face Tracking/Binary Eyelid Control": true, + "Face Tracking/Couple Eyelids": true, + "Face Tracking/Mute Face Tracking": true, + "Face Tracking/None": false, + "Face Tracking/Use Audio for Mouth": true, + "Face Tracking/Use Camera": true, + "Face Tracking/Velocity Filter": true, + "Hands/Show Hand Targets": false, + "Leap Motion/desktopHeightOffset": 0.20000000298023224, + "Leap Motion/enabled": false, + "Leap Motion/sensorLocation": "Desktop", + "Maximum Texture Memory/1024 MB": false, + "Maximum Texture Memory/2048 MB": false, + "Maximum Texture Memory/256 MB": false, + "Maximum Texture Memory/4 MB": false, + "Maximum Texture Memory/4096 MB": false, + "Maximum Texture Memory/512 MB": false, + "Maximum Texture Memory/6144 MB": false, + "Maximum Texture Memory/64 MB": false, + "Maximum Texture Memory/8192 MB": false, + "Maximum Texture Memory/Automatic Texture Memory": true, + "Network/Disable Activity Logger": false, + "Network/Send wrong DS connect version": false, + "Network/Send wrong protocol version": false, + "Perception Neuron/enabled": false, + "Perception Neuron/serverAddress": "localhost", + "Perception Neuron/serverPort": 7001, + "Performance Timer/Display Timing Details": false, + "Performance Timer/Expand /myAvatar": false, + "Performance Timer/Expand /myAvatar/simulation": false, + "Performance Timer/Expand /otherAvatar": false, + "Performance Timer/Expand /paintGL": false, + "Performance Timer/Expand /physics": false, + "Performance Timer/Expand /simulation": false, + "Performance Timer/Expand /update": false, + "Performance Timer/Only Display Top Ten": true, + "Physics/Highlight Simulation Ownership": false, + "Physics/Show Bullet Bounding Boxes": false, + "Physics/Show Bullet Collision": false, + "Physics/Show Bullet Constraint Limits": false, + "Physics/Show Bullet Constraints": false, + "Physics/Show Bullet Contact Points": false, + "Picking/Force Coarse Picking": false, + "Render/Ambient Occlusion": false, + "Render/Compute Blendshapes": true, + "Render/Decimate Textures": false, + "Render/Default Skybox": true, + "Render/Enable Sparse Texture Management": true, + "Render/Maximum Texture Memory/1024 MB": false, + "Render/Maximum Texture Memory/2048 MB": false, + "Render/Maximum Texture Memory/256 MB": false, + "Render/Maximum Texture Memory/4 MB": false, + "Render/Maximum Texture Memory/4096 MB": false, + "Render/Maximum Texture Memory/512 MB": false, + "Render/Maximum Texture Memory/6144 MB": false, + "Render/Maximum Texture Memory/64 MB": false, + "Render/Maximum Texture Memory/8192 MB": false, + "Render/Maximum Texture Memory/Automatic Texture Memory": true, + "Render/OpenVR Threaded Submit": true, + "Render/Scale Resolution/1": true, + "Render/Scale Resolution/1/2": false, + "Render/Scale Resolution/1/3": false, + "Render/Scale Resolution/1/4": false, + "Render/Scale Resolution/2/3": false, + "Render/Shadows": true, + "Render/Temporal Antialiasing (FXAA if disabled)": true, + "Render/Throttle FPS If Not Focus": true, + "Render/World Axes": false, + "RunningScripts": [ + "file:///~//defaultScripts.js" + ], + "SDL2/enabled": true, + "Scale Resolution/1": true, + "Scale Resolution/1/2": false, + "Scale Resolution/1/3": false, + "Scale Resolution/1/4": false, + "Scale Resolution/2/3": false, + "Settings/Ask To Reset Settings on Start": false, + "Settings/Developer Menu": false, + "TabletSounds": "@Variant(\u0000\u0000\u0000\u000b\u0000\u0000\u0000\u0005\u0000\u0000\u0000(\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000B\u0000u\u0000t\u0000t\u0000o\u0000n\u00000\u00006\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000(\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000B\u0000u\u0000t\u0000t\u0000o\u0000n\u00000\u00004\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000(\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000B\u0000u\u0000t\u0000t\u0000o\u0000n\u00000\u00007\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000\"\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000T\u0000a\u0000b\u00000\u00001\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000\"\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000T\u0000a\u0000b\u00000\u00002\u0000.\u0000w\u0000a\u0000v)", + "Timing/Log Extra Timing Details": false, + "Timing/Log Render Pipeline Warnings": false, + "Timing/Performance Timer/Display Timing Details": false, + "Timing/Performance Timer/Expand /myAvatar": false, + "Timing/Performance Timer/Expand /myAvatar/simulation": false, + "Timing/Performance Timer/Expand /otherAvatar": false, + "Timing/Performance Timer/Expand /paintGL": false, + "Timing/Performance Timer/Expand /physics": false, + "Timing/Performance Timer/Expand /simulation": false, + "Timing/Performance Timer/Expand /update": false, + "Timing/Performance Timer/Only Display Top Ten": true, + "Timing/Show Timer": false, + "Timing/Suppress Timings Less than 10ms": false, + "UI/Desktop Tablet Becomes Toolbar": true, + "UI/HMD Tablet Becomes Toolbar": false, + "UserActivityLoggerDisabled": false, + "View/Center Player In View": true, + "View/Enter First Person Mode in HMD": true, + "View/Entity Mode": false, + "View/First Person": true, + "View/Independent Mode": false, + "View/Mirror": false, + "View/Third Person": false, + "WindowGeometry": "@Rect(0 0 1920 1080)", + "WindowRoot.Windows/height": 706, + "WindowRoot.Windows/width": 480, + "WindowState": 0, + "activeDisplayPlugin": "Desktop", + "autoFocusOnSelect": true, + "cameraEaseOnFocus": true, + "desktopLODDecreaseFPS": 30.000001907348633, + "dynamicJitterBuffersEnabled": true, + "firstRun": false, + "hifi.ktx.cache_version": 1, + "hmdLODDecreaseFPS": 34, + "io.highfidelity.attachPoints": "{}", + "io.highfidelity.isEditing": false, + "loginDialogPoppedUp": false, + "sessionRunTime": 42, + "showLightsAndParticlesInEditMode": true, + "showZonesInEditMode": true, + "staticJitterBufferFrames": 1, + "toolbar/com.highfidelity.interface.toolbar.system/desktopHeight": 1059, + "toolbar/com.highfidelity.interface.toolbar.system/x": 655, + "toolbar/com.highfidelity.interface.toolbar.system/y": 953, + "toolbar/constrainToolbarToCenterX": true, + "wallet/autoLogout": true +} diff --git a/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin b/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin new file mode 100644 index 0000000000..65c971ea79 Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin differ diff --git a/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json b/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json new file mode 100644 index 0000000000..9976036f8e --- /dev/null +++ b/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json @@ -0,0 +1,861 @@ +{ + "Anime boy": { + "attachments": [ + ], + "avatarEntites": [ + { + "properties": { + "acceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 6.915350914001465, + "ageAsText": "0 hours 0 minutes 6 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "x": -0.10961885005235672, + "y": -0.19444090127944946, + "z": -0.15760529041290283 + }, + "center": { + "x": 2.6226043701171875e-06, + "y": -0.13999652862548828, + "z": -0.04999971389770508 + }, + "dimensions": { + "x": 0.21924294531345367, + "y": 0.10888873785734177, + "z": 0.2152111530303955 + }, + "tfl": { + "x": 0.10962409526109695, + "y": -0.0855521634221077, + "z": 0.057605862617492676 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-06-06T17:27:53Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "x": 0.21924294531345367, + "y": 0.07768379896879196, + "z": 0.2055898904800415 + }, + "dynamic": false, + "editionNumber": 15, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{5d20c775-a0d7-4163-b158-4e0a784a4625}", + "ignoreForCollisions": false, + "itemArtist": "jyoum", + "itemCategories": "Wearables", + "itemDescription": "Wear these, and others will respect your authoritah.", + "itemLicense": "", + "itemName": "Aviators", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1528306178314655, + "lastEditedBy": "{439a2669-4626-487f-9dcf-2d15e77c69a2}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "x": 2.6226043701171875e-06, + "y": -0.13999652862548828, + "z": -0.04999971389770508 + }, + "localRotation": { + "w": 0.9969173073768616, + "x": -0.07845909893512726, + "y": 0, + "z": 0 + }, + "locked": false, + "marketplaceID": "40d879ec-93f0-4b4a-8c58-dd6349bdb058", + "modelURL": "http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx", + "name": "", + "naturalDimensions": { + "x": 0.1660931408405304, + "y": 0.05885136127471924, + "z": 0.15574991703033447 + }, + "naturalPosition": { + "x": 0, + "y": 1.6633577346801758, + "z": 0.048884183168411255 + }, + "originalTextures": "{\n \"aviator:Eyewear2F\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Diffuse.png\",\n \"aviator:Eyewear2F1\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Specular.png\"\n}\n", + "owningAvatarID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}", + "parentID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}", + "parentJointIndex": 66, + "position": { + "x": 2.6226043701171875e-06, + "y": -0.13999652862548828, + "z": -0.04999971389770508 + }, + "queryAACube": { + "scale": 0.9313028454780579, + "x": -1.4091639518737793, + "y": -10.133878707885742, + "z": 1.9983724355697632 + }, + "registrationPoint": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 2, + "texturesSize": 1310720, + "verticesCount": 982 + }, + "restitution": 0.5, + "rotation": { + "w": 0.9969173073768616, + "x": -0.07845909893512726, + "y": 0, + "z": 0 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + } + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst", + "version": 3 + }, + "Anime girl": { + "attachments": [ + ], + "avatarEntites": [ + { + "properties": { + "acceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 19.66267967224121, + "ageAsText": "0 hours 0 minutes 19 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "x": -0.10536206513643265, + "y": -0.16647332906723022, + "z": -0.12632352113723755 + }, + "center": { + "x": 0, + "y": -0.12999999523162842, + "z": -0.030000001192092896 + }, + "dimensions": { + "x": 0.2107241302728653, + "y": 0.07294666767120361, + "z": 0.1926470398902893 + }, + "tfl": { + "x": 0.10536206513643265, + "y": -0.09352666139602661, + "z": 0.06632351875305176 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-06-05T00:10:37Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "x": 0.2107241302728653, + "y": 0.07294666767120361, + "z": 0.1926470398902893 + }, + "dynamic": false, + "editionNumber": 5, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{1586b83a-2af7-4532-9bfb-82fe3f5d5ce9}", + "ignoreForCollisions": false, + "itemArtist": "moam_00", + "itemCategories": "Wearables", + "itemDescription": "Perfect for side-glancin'.", + "itemLicense": "", + "itemName": "Blacker Fem Glasses", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1528157470041658, + "lastEditedBy": "{425df1a8-289b-42fc-819c-c3b2a12d7165}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "x": 0, + "y": -0.12999999523162842, + "z": -0.029999999329447746 + }, + "localRotation": { + "w": 1, + "x": -2.2351741790771484e-08, + "y": 3.4924596548080444e-10, + "z": 3.725290298461914e-09 + }, + "locked": false, + "marketplaceID": "06781d12-9139-48f4-ac2a-417dde090981", + "modelURL": "http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx", + "name": "Female Glasses 3 by Mario Andrade", + "naturalDimensions": { + "x": 0.16209548711776733, + "y": 0.05611282214522362, + "z": 0.14819003641605377 + }, + "naturalPosition": { + "x": 0, + "y": -7.636845111846924e-08, + "z": 0 + }, + "originalTextures": "{\n \"file49\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Mixed_AO.jpg\",\n \"file81\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Metallic.jpg\",\n \"file84\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Roughness.jpg\",\n \"file86\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Base_Color.jpg\",\n \"file87\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Normal_DirectX.jpg\"\n}\n", + "owningAvatarID": "{1277f725-fbb4-478b-ae79-1241fd90e508}", + "parentID": "{1277f725-fbb4-478b-ae79-1241fd90e508}", + "parentJointIndex": 66, + "position": { + "x": 0, + "y": -0.12999999523162842, + "z": -0.029999999329447746 + }, + "queryAACube": { + "scale": 0.8840523958206177, + "x": -2.6587564945220947, + "y": -10.162277221679688, + "z": -0.9548344016075134 + }, + "registrationPoint": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 5, + "texturesSize": 0, + "verticesCount": 1156 + }, + "restitution": 0.5, + "rotation": { + "w": 1, + "x": -2.2351741790771484e-08, + "y": 3.4924596548080444e-10, + "z": 3.725290298461914e-09 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + } + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst", + "version": 3 + }, + "Last Legends: Male": { + "attachments": [ + ], + "avatarEntites": [ + { + "properties": { + "acceleration": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 321.8835144042969, + "ageAsText": "0 hours 5 minutes 21 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "blue": -0.03950843587517738, + "green": 0.20785385370254517, + "red": -0.04381325840950012, + "x": -0.04381325840950012, + "y": 0.20785385370254517, + "z": -0.03950843587517738 + }, + "center": { + "blue": 0, + "green": 0.23000000417232513, + "red": 0, + "x": 0, + "y": 0.23000000417232513, + "z": 0 + }, + "dimensions": { + "blue": 0.07901687175035477, + "green": 0.044292300939559937, + "red": 0.08762651681900024, + "x": 0.08762651681900024, + "y": 0.044292300939559937, + "z": 0.07901687175035477 + }, + "tfl": { + "blue": 0.03950843587517738, + "green": 0.2521461546421051, + "red": 0.04381325840950012, + "x": 0.04381325840950012, + "y": 0.2521461546421051, + "z": 0.03950843587517738 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-07-26T23:56:46Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "blue": 0.07229919731616974, + "green": 0.06644226610660553, + "red": 0.03022606298327446, + "x": 0.03022606298327446, + "y": 0.06644226610660553, + "z": 0.07229919731616974 + }, + "dynamic": false, + "editionNumber": 58, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{03053239-bb37-4c51-a013-a1772baaeed5}", + "ignoreForCollisions": false, + "itemArtist": "jyoum", + "itemCategories": "Wearables", + "itemDescription": "A cool scifi watch for your avatar!", + "itemLicense": "", + "itemName": "Scifi Watch", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1532649569894305, + "lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "blue": 0, + "green": 0.23000000417232513, + "red": 0, + "x": 0, + "y": 0.23000000417232513, + "z": 0 + }, + "localRotation": { + "w": 0.5910986065864563, + "x": -0.48726415634155273, + "y": -0.4088630974292755, + "z": 0.49599072337150574 + }, + "locked": false, + "marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90", + "modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx", + "name": "Scifi Watch by Jimi", + "naturalDimensions": { + "blue": 0.055614765733480453, + "green": 0.0511094331741333, + "red": 0.023250818252563477, + "x": 0.023250818252563477, + "y": 0.0511094331741333, + "z": 0.055614765733480453 + }, + "naturalPosition": { + "blue": -0.06031447649002075, + "green": 1.4500460624694824, + "red": 0.6493338942527771, + "x": 0.6493338942527771, + "y": 1.4500460624694824, + "z": -0.06031447649002075 + }, + "originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n", + "owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentJointIndex": 16, + "position": { + "blue": 0, + "green": 0.23000000417232513, + "red": 0, + "x": 0, + "y": 0.23000000417232513, + "z": 0 + }, + "queryAACube": { + "scale": 0.3082179129123688, + "x": 495.7716979980469, + "y": 498.345703125, + "z": 498.52044677734375 + }, + "registrationPoint": { + "blue": 0.5, + "green": 0.5, + "red": 0.5, + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 5, + "texturesSize": 786432, + "verticesCount": 273 + }, + "restitution": 0.5, + "rotation": { + "w": 0.5910986065864563, + "x": -0.48726415634155273, + "y": -0.4088630974292755, + "z": 0.49599072337150574 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + }, + { + "properties": { + "acceleration": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 308.8044128417969, + "ageAsText": "0 hours 5 minutes 8 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "blue": -0.2340194433927536, + "green": -0.07067721337080002, + "red": -0.17002610862255096, + "x": -0.17002610862255096, + "y": -0.07067721337080002, + "z": -0.2340194433927536 + }, + "center": { + "blue": -0.039825439453125, + "green": 0.02001953125, + "red": 0.0001678466796875, + "x": 0.0001678466796875, + "y": 0.02001953125, + "z": -0.039825439453125 + }, + "dimensions": { + "blue": 0.3883880078792572, + "green": 0.18139348924160004, + "red": 0.34038791060447693, + "x": 0.34038791060447693, + "y": 0.18139348924160004, + "z": 0.3883880078792572 + }, + "tfl": { + "blue": 0.1543685644865036, + "green": 0.11071627587080002, + "red": 0.17036180198192596, + "x": 0.17036180198192596, + "y": 0.11071627587080002, + "z": 0.1543685644865036 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-07-26T23:56:46Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "blue": 0.38838762044906616, + "green": 0.16981728374958038, + "red": 0.33466479182243347, + "x": 0.33466479182243347, + "y": 0.16981728374958038, + "z": 0.38838762044906616 + }, + "dynamic": false, + "editionNumber": 18, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{1bf231ce-3913-4c53-be3c-b1f4094dac51}", + "ignoreForCollisions": false, + "itemArtist": "jyoum", + "itemCategories": "Wearables", + "itemDescription": "A stylish and classic piece of headwear for your avatar.", + "itemLicense": "", + "itemName": "Fedora", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1532649698129709, + "lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "blue": -0.039825439453125, + "green": 0.02001953125, + "red": 0.0001678466796875, + "x": 0.0001678466796875, + "y": 0.02001953125, + "z": -0.039825439453125 + }, + "localRotation": { + "w": 0.9998477101325989, + "x": -9.898545982878204e-09, + "y": 5.670873406415922e-07, + "z": 0.017452405765652657 + }, + "locked": false, + "marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc", + "modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx", + "name": "", + "naturalDimensions": { + "blue": 0.320981502532959, + "green": 0.14034485816955566, + "red": 0.2765824794769287, + "x": 0.2765824794769287, + "y": 0.14034485816955566, + "z": 0.320981502532959 + }, + "naturalPosition": { + "blue": 0.022502630949020386, + "green": 1.7460365295410156, + "red": 0.000143393874168396, + "x": 0.000143393874168396, + "y": 1.7460365295410156, + "z": 0.022502630949020386 + }, + "originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n", + "owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentJointIndex": 66, + "position": { + "blue": -0.039825439453125, + "green": 0.02001953125, + "red": 0.0001678466796875, + "x": 0.0001678466796875, + "y": 0.02001953125, + "z": -0.039825439453125 + }, + "queryAACube": { + "scale": 1.6202316284179688, + "x": 495.21051025390625, + "y": 498.5577697753906, + "z": 497.6370849609375 + }, + "registrationPoint": { + "blue": 0.5, + "green": 0.5, + "red": 0.5, + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 2, + "texturesSize": 327680, + "verticesCount": 719 + }, + "restitution": 0.5, + "rotation": { + "w": 0.9998477101325989, + "x": -9.898545982878204e-09, + "y": 5.670873406415922e-07, + "z": 0.017452405765652657 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + } + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst", + "version": 3 + }, + "Last legends Female": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/8d823be5-6197-4418-b984-eb94160ed956-v1/LLFemale_Clothes.fst", + "version": 3 + }, + "Matthew": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst", + "version": 3 + }, + "Priscilla": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst", + "version": 3 + }, + "Woody": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/ad348528-de38-420c-82bb-054cb22163f5-v1/mannequin.fst", + "version": 3 + } +} diff --git a/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz b/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz new file mode 100644 index 0000000000..1eeeac50ac Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz differ diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin b/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin new file mode 100644 index 0000000000..645e34895e Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin differ diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/config.json b/tools/auto-tester/AppDataHighFidelity/domain-server/config.json new file mode 100644 index 0000000000..d662d89f28 --- /dev/null +++ b/tools/auto-tester/AppDataHighFidelity/domain-server/config.json @@ -0,0 +1,7 @@ +{ + "metaverse": { + "automatic_networking": "full", + "id": "17b1cb9c-08c4-45aa-9257-163ad3913529" + }, + "version": 2.2 +} diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz b/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz new file mode 100644 index 0000000000..1eeeac50ac Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz differ diff --git a/tools/auto-tester/CMakeLists.txt b/tools/auto-tester/CMakeLists.txt index 1546a35f4c..c8c0a336d2 100644 --- a/tools/auto-tester/CMakeLists.txt +++ b/tools/auto-tester/CMakeLists.txt @@ -48,4 +48,19 @@ if (WIN32) POST_BUILD COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> \"$\"" ) + + # add a custom command to copy the empty Apps/Data High Fidelity folder (i.e. - a valid folder with no entities) + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/AppDataHighFidelity" "$/AppDataHighFidelity" + ) + + # add a custom command to copy the SSL DLLs + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory "$ENV{VCPKG_ROOT}/installed/x64-windows/bin" "$" + ) + endif () \ No newline at end of file diff --git a/tools/auto-tester/Create.PNG b/tools/auto-tester/Create.PNG index d82d4873a2..85d70e59ed 100644 Binary files a/tools/auto-tester/Create.PNG and b/tools/auto-tester/Create.PNG differ diff --git a/tools/auto-tester/Evaluate.PNG b/tools/auto-tester/Evaluate.PNG index d530dec994..2f711f61a3 100644 Binary files a/tools/auto-tester/Evaluate.PNG and b/tools/auto-tester/Evaluate.PNG differ diff --git a/tools/auto-tester/README.md b/tools/auto-tester/README.md index 0924e77f8b..e029955edc 100644 --- a/tools/auto-tester/README.md +++ b/tools/auto-tester/README.md @@ -5,14 +5,16 @@ The auto-tester is a stand alone application that provides a mechanism for regre * The snapshots are compared to a 'canonical' set of images that have been produced beforehand. * The result, if any test failed, is a zipped folder describing the failure. -Auto-tester has 4 functions, separated into 4 tabs: +Auto-tester has 5 functions, separated into 4 tabs: 1. Creating tests, MD files and recursive scripts -2. Evaluating the results of running tests -3. TestRail interface -4. Windows task bar utility (Windows only) +1. Windows task bar utility (Windows only) +1. Running tests +1. Evaluating the results of running tests +1. Web interface + ## Installation ### Executable -1. Download the installer by browsing to [here](). +1. Download the installer by browsing to [here](). 2. Double click on the installer and install to a convenient location ![](./setup_7z.PNG) 3. To run the auto-tester, double click **auto-tester.exe**. @@ -25,6 +27,17 @@ Python 3 can be downloaded from: 3. Mac (**macOS 64-bit/32-bit installer** or **macOS 64-bit/32-bit installer**) After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable. +### AWS interface +#### Windows +1. Download the AWS CLI from `https://aws.amazon.com/cli/` +1. Install (installer is named `AWSCLI64PY3.msi`) +1. Open a new command prompt and run `aws configure` +1. Enter the AWS account number +1. Enter the secret key +1. Leave region name and ouput format as default [None] + +1. Install the latest release of Boto3 via pip: +>pip install boto3 # Create ![](./Create.PNG) @@ -36,6 +49,9 @@ This function is used to create/update Expected Images after a successful run of The user will be asked for the snapshot folder and then the tests root folder. All snapshots located in the snapshot folder will be used to create or update the expected images in the relevant tests. ### Details As an example - if the snapshots folder contains an image named `tests.content.entity.zone.zoneOrientation.00003.png`, then this file will be copied to `tests/contente/enity/zone/zoneOrientation/ExpectedImage0003.png`. +## Create Tests Outline +### Usage +This function creates an MD file in the (user-selected) tests root folder. The file provides links to both the tests and the MD files. ## Create MD file ### Usage This function creates a file named `test.md` from a `test.js` script. The user will be asked for the folder containing the test script: @@ -54,13 +70,20 @@ The process to produce the MD file is a simplistic parse of the test script. ### Usage This function creates all MD files recursively from the user-selected root folder. This can be any folder in the tests hierarchy (e.g. all engine\material tests). -The file provides a hierarchial list of all the tests -## Create Tests Outline +The file provides a hierarchal list of all the tests +## Create testAuto script ### Usage -This function creates an MD file in the (user-selected) tests root folder. The file provides links to both the tests and the MD files. +This function creates a script named `testAuto.js` in a user-selected test folder. +### Details +The script created runs the `test.js` script in the folder in automatic mode. The script is the same for all tests. +## Create all testAuto scripts +### Usage +This function creates all testAuto scripts recursively from the user-selected root folder. This can be any folder in the tests hierarchy (e.g. all engine\material tests). + +The file provides a hierarchical list of all the tests ## Create Recursive Script ### Usage -After the user selects a folder within the tests hierarchy, a script is created, named `testRecursive.js`. This script calls all `test.js` scripts in the subfolders. +After the user selects a folder within the tests hierarchy, a script is created, named `testRecursive.js`. This script calls all `test.js` scripts in the sub-folders. ### Details The various scripts are called in alphabetical order. @@ -92,6 +115,34 @@ autoTester.runRecursive(); In this case all recursive scripts, from the selected folder down, are created. Running this function in the tests root folder will create (or update) all the recursive scripts. +# Windows +![](./Windows.PNG) + +This tab is Windows-specific. It provides buttons to hide and show the task bar. + +The task bar should be hidden for all tests that use the primary camera. This is required to ensure that the snapshots are the right size. +# Run +![](./Run.PNG) +The run tab is used to run tests in automatic mode. The tests require the location of a folder to store files in; this folder can safely be re-used for any number of runs (the "Working Folder"). +The test script that is run is `https://github.com/highfidelity/hifi_tests/blob/master/tests/testRecursive.js`. The user can use a different branch' or even repository, if required. +Tests can be run server-less, or with the local host. In the second case, the domain-server and assignment-clients are run before starting Interface. +The default is to run the latest build. The user can select a specific build or PR if desired. +Testing can be started immediately, or run on a schedule. + +A test run is performed in a number of steps: +1. If the latest run has been selected then the `dev-builds.xml` file is downloaded to identify the latest build. +1. The installer is then downloaded. +1. After downloading the High Fidelity application is installed. This requires that UAC be disabled! +1. Any instances of the server-console, assignment-client, domain-server or Interface are killed before running the tests. +1. Interface is run with the appropriate command line parameters. +1. The expected images are then downloaded from GitHub and compared to the actual images from the tests. + +The working folder will ultimately contain the following: +1. A folder named `High Fidelity`. This is where High Fidelity is installed. +1. A folder named `snapshots`. This folder contains the zipped results folders (one for each run). It also contains both the actual images and the expected images from the last run; note that these are deleted before running tests (All PNG files in the folder are deleted. In addition - a text file named `tests_completed.txt` is created at the end of the test script - this signals that Interface did not crash during the test run. +1. The `dev-builds.xml` file, if it was downloaded. +1. The HighFidelity installer. Note that this is always named `HighFidelity-Beta-latest-dev` so as not to store too many installers over time. +1. A log file describing the runs. This file is appended to after each run. # Evaluate ![](./Evaluate.PNG) @@ -103,13 +154,13 @@ If any tests have failed, then a zipped folder will be created in the snapshots ### Usage Before starting the evaluation, make sure the GitHub user and branch are set correctly. The user should not normally be changed, but the branch may need to be set to the appropriate RC. -After setting the checkbox as required and pressing Evaluate - the user will be asked for the snapshots folder. +After setting the check-box as required and pressing Evaluate - the user will be asked for the snapshots folder. ### Details Evaluation proceeds in a number of steps: 1. A folder is created to store any failures -1. The expecetd images are download from GitHub. They are named slightly differently from the snapshots (e.g. `tests.engine.render.effect.highlight.coverage.00000.png` and `tests.engine.render.effect.highlight.coverage.00000_EI.png`). +1. The expected images are download from GitHub. They are named slightly differently from the snapshots (e.g. `tests.engine.render.effect.highlight.coverage.00000.png` and `tests.engine.render.effect.highlight.coverage.00000_EI.png`). 1. The images are then pair-wise compared, using the SSIM algorithm. A fixed threshold is used to define a mismatch. @@ -119,8 +170,10 @@ Evaluation proceeds in a number of steps: 1. If not in interactive mode, or the user has defined the results as an error, an error is written into the error folder. The error itself is a folder with the 3 images and a small text file containing details. 1. At the end of the test, the folder is zipped and the original folder is deleted. If there are no errors then the zipped folder will be empty. -# TestRail -![](./TestRail.PNG) + +# Web Interface +![](./WebInterface.PNG) +This tab has two functions: updating the TestRail cases, runs and results, and creating web page reports that are stored on AWS. Before updating TestRail, make sure the GitHub user and branch are set correctly. The user should not normally be changed, but the branch may need to be set to the appropriate RC. @@ -129,9 +182,9 @@ Any access to TestRail will require the TestRail account (default is High Fideli ![](./TestRailSelector.PNG) - The default test rail user is shown, and can be changed as needed. -- The username is usually the user's email. +- The user-name is usually the user's email. - The Project ID defaults to 14 - Interface. -- The Suite ID defaults to 1147 - Renderong. +- The Suite ID defaults to 1147 - Rendering. - The TestRail page provides 3 functions for writing to TestRail. ## Create Test Cases ### Usage @@ -188,10 +241,7 @@ A number of Python scripts are created: - `getRuns.py` reads the release names from TestRail - `addRun` is the script that writes to TestRail. -In addition - a file containing all the releases will be created - `runs.txt` -# Windows -![](./Windows.PNG) - -This tab is Windows-specific. It provides buttons to hide and show the task bar. - -The task bar should be hidden for all tests that use the primary camera. This is required to ensure that the snapshots are the right size. \ No newline at end of file +In addition - a file containing all the releases will be created - `runs.txt`. +## Create Web Page +This function requests a zipped results folder and converts it to a web page. The page is created in a user-selecetd working folder. +If the `Update AWS` checkbox is checked then the page will also be copied to AWS, and the appropriate URL will be displayed in the window below the button. diff --git a/tools/auto-tester/Run.PNG b/tools/auto-tester/Run.PNG new file mode 100644 index 0000000000..29f81299c8 Binary files /dev/null and b/tools/auto-tester/Run.PNG differ diff --git a/tools/auto-tester/TestRail.PNG b/tools/auto-tester/TestRail.PNG deleted file mode 100644 index 042d0cf1cb..0000000000 Binary files a/tools/auto-tester/TestRail.PNG and /dev/null differ diff --git a/tools/auto-tester/Web Interface.PNG b/tools/auto-tester/Web Interface.PNG new file mode 100644 index 0000000000..d7216e3c18 Binary files /dev/null and b/tools/auto-tester/Web Interface.PNG differ diff --git a/tools/auto-tester/WebInterface.PNG b/tools/auto-tester/WebInterface.PNG new file mode 100644 index 0000000000..d9d3df3fc6 Binary files /dev/null and b/tools/auto-tester/WebInterface.PNG differ diff --git a/tools/auto-tester/Windows.PNG b/tools/auto-tester/Windows.PNG index bf7b76ba02..32e3bfd53e 100644 Binary files a/tools/auto-tester/Windows.PNG and b/tools/auto-tester/Windows.PNG differ diff --git a/tools/auto-tester/src/AWSInterface.cpp b/tools/auto-tester/src/AWSInterface.cpp new file mode 100644 index 0000000000..628db5329c --- /dev/null +++ b/tools/auto-tester/src/AWSInterface.cpp @@ -0,0 +1,413 @@ +// +// AWSInterface.cpp +// +// Created by Nissim Hadar on 3 Oct 2018. +// Copyright 2013 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 "AWSInterface.h" + +#include +#include +#include + +#include +#include + +AWSInterface::AWSInterface(QObject* parent) : QObject(parent) { + _pythonInterface = new PythonInterface(); + _pythonCommand = _pythonInterface->getPythonCommand(); +} + +void AWSInterface::createWebPageFromResults(const QString& testResults, + const QString& workingDirectory, + QCheckBox* updateAWSCheckBox, + QLineEdit* urlLineEdit) { + _testResults = testResults; + _workingDirectory = workingDirectory; + + _urlLineEdit = urlLineEdit; + _urlLineEdit->setEnabled(false); + + extractTestFailuresFromZippedFolder(); + createHTMLFile(); + + if (updateAWSCheckBox->isChecked()) { + updateAWS(); + } +} + +void AWSInterface::extractTestFailuresFromZippedFolder() { + // For a test results zip file called `D:/tt/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ].zip` + // the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]` + // and, this folder will be in the workign directory + QStringList parts =_testResults.split('/'); + QString zipFolderName = _workingDirectory + "/" + parts[parts.length() - 1].split('.')[0]; + if (QDir(zipFolderName).exists()) { + QDir dir = zipFolderName; + dir.removeRecursively(); + } + + QDir().mkdir(_workingDirectory); + JlCompress::extractDir(_testResults, _workingDirectory); +} + +void AWSInterface::createHTMLFile() { + // For file named `D:/tt/snapshots/TestResults--2018-10-03_15-35-28(9433)[DESKTOP-PMKNLSQ].zip` + // - the HTML will be in a folder named `TestResults--2018-10-03_15-35-28(9433)[DESKTOP-PMKNLSQ]` + QStringList pathComponents = _testResults.split('/'); + QString filename = pathComponents[pathComponents.length() - 1]; + _resultsFolder = filename.left(filename.length() - 4); + + QString resultsPath = _workingDirectory + "/" + _resultsFolder + "/"; + QDir().mkdir(resultsPath); + _htmlFilename = resultsPath + HTML_FILENAME; + + QFile file(_htmlFilename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not create '" + _htmlFilename + "'"); + exit(-1); + } + + QTextStream stream(&file); + + startHTMLpage(stream); + writeHead(stream); + writeBody(stream); + finishHTMLpage(stream); + + file.close(); +} + +void AWSInterface::startHTMLpage(QTextStream& stream) { + stream << "\n"; + stream << "\n"; +} + +void AWSInterface::writeHead(QTextStream& stream) { + stream << "\t" << "\n"; + stream << "\t" << "\t" << "\n"; + stream << "\t" << "\n"; +} + +void AWSInterface::writeBody(QTextStream& stream) { + stream << "\t" << "\n"; + writeTitle(stream); + writeTable(stream); + stream << "\t" << "\n"; +} + +void AWSInterface::finishHTMLpage(QTextStream& stream) { + stream << "\n"; +} + +void AWSInterface::writeTitle(QTextStream& stream) { + // Separate relevant components from the results name + // The expected format is as follows: `D:/tt/snapshots/TestResults--2018-10-04_11-09-41(PR14128)[DESKTOP-PMKNLSQ].zip` + QStringList tokens = _testResults.split('/'); + + // date_buildorPR_hostName will be 2018-10-03_15-35-28(9433)[DESKTOP-PMKNLSQ] + QString date_buildorPR_hostName = tokens[tokens.length() - 1].split("--")[1].split(".")[0]; + + QString buildorPR = date_buildorPR_hostName.split('(')[1].split(')')[0]; + QString hostName = date_buildorPR_hostName.split('[')[1].split(']')[0]; + + QStringList dateList = date_buildorPR_hostName.split('(')[0].split('_')[0].split('-'); + QString year = dateList[0]; + QString month = dateList[1]; + QString day = dateList[2]; + + QStringList timeList = date_buildorPR_hostName.split('(')[0].split('_')[1].split('-'); + QString hour = timeList[0]; + QString minute = timeList[1]; + QString second = timeList[2]; + + const QString months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + stream << "\t" << "\t" << "\n"; + stream << "\t" << "\t" << "

      Failures for "; + stream << months[month.toInt() - 1] << " " << day << ", " << year << ", "; + stream << hour << ":" << minute << ":" << second << ", "; + + if (buildorPR.left(2) == "PR") { + stream << "PR " << buildorPR.right(buildorPR.length() - 2) << ", "; + } else { + stream << "build " << buildorPR << ", "; + } + + stream << "run on " << hostName << "

      \n"; +} + +void AWSInterface::writeTable(QTextStream& stream) { + QString previousTestName{ "" }; + + // Loop over all entries in directory. This is done in stages, as the names are not in the order of the tests + // The first stage reads the directory names into a list + // The second stage renames the tests by removing everything up to "--tests." + // The third stage renames the directories + // The fourth and lasts stage creates the HTML entries + // + // Note that failures are processed first, then successes + QStringList originalNamesFailures; + QStringList originalNamesSuccesses; + QDirIterator it1(_workingDirectory.toStdString().c_str()); + while (it1.hasNext()) { + QString nextDirectory = it1.next(); + + // Skip `.` and `..` directories + if (nextDirectory.right(1) == ".") { + continue; + } + + // Only process failure folders + if (!nextDirectory.contains("--tests.")) { + continue; + } + + // Look at the filename at the end of the path + QStringList parts = nextDirectory.split('/'); + QString name = parts[parts.length() - 1]; + if (name.left(7) == "Failure") { + originalNamesFailures.append(nextDirectory); + } else { + originalNamesSuccesses.append(nextDirectory); + } + } + + QStringList newNamesFailures; + for (int i = 0; i < originalNamesFailures.length(); ++i) { + newNamesFailures.append(originalNamesFailures[i].split("--tests.")[1]); + } + + QStringList newNamesSuccesses; + for (int i = 0; i < originalNamesSuccesses.length(); ++i) { + newNamesSuccesses.append(originalNamesSuccesses[i].split("--tests.")[1]); + } + + _htmlFailuresFolder = _workingDirectory + "/" + _resultsFolder + "/" + FAILURES_FOLDER; + QDir().mkdir(_htmlFailuresFolder); + + _htmlSuccessesFolder = _workingDirectory + "/" + _resultsFolder + "/" + SUCCESSES_FOLDER; + QDir().mkdir(_htmlSuccessesFolder); + + for (int i = 0; i < newNamesFailures.length(); ++i) { + QDir().rename(originalNamesFailures[i], _htmlFailuresFolder + "/" + newNamesFailures[i]); + } + + for (int i = 0; i < newNamesSuccesses.length(); ++i) { + QDir().rename(originalNamesSuccesses[i], _htmlSuccessesFolder + "/" + newNamesSuccesses[i]); + } + + QDirIterator it2((_htmlFailuresFolder).toStdString().c_str()); + while (it2.hasNext()) { + QString nextDirectory = it2.next(); + + // Skip `.` and `..` directories, as well as the HTML directory + if (nextDirectory.right(1) == "." || nextDirectory.contains(QString("/") + _resultsFolder + "/TestResults--")) { + continue; + } + + QStringList pathComponents = nextDirectory.split('/'); + QString filename = pathComponents[pathComponents.length() - 1]; + int splitIndex = filename.lastIndexOf("."); + QString testName = filename.left(splitIndex).replace(".", " / "); + QString testNumber = filename.right(filename.length() - (splitIndex + 1)); + + // The failures are ordered lexicographically, so we know that we can rely on the testName changing to create a new table + if (testName != previousTestName) { + if (!previousTestName.isEmpty()) { + closeTable(stream); + } + + previousTestName = testName; + + stream << "\t\t

      " << testName << "

      \n"; + + openTable(stream); + } + + createEntry(testNumber.toInt(), filename, stream, true); + } + + closeTable(stream); + stream << "\t" << "\t" << "\n"; + stream << "\t" << "\t" << "

      The following tests passed:

      "; + + QDirIterator it3((_htmlSuccessesFolder).toStdString().c_str()); + while (it3.hasNext()) { + QString nextDirectory = it3.next(); + + // Skip `.` and `..` directories, as well as the HTML directory + if (nextDirectory.right(1) == "." || nextDirectory.contains(QString("/") + _resultsFolder + "/TestResults--")) { + continue; + } + + QStringList pathComponents = nextDirectory.split('/'); + QString filename = pathComponents[pathComponents.length() - 1]; + int splitIndex = filename.lastIndexOf("."); + QString testName = filename.left(splitIndex).replace(".", " / "); + QString testNumber = filename.right(filename.length() - (splitIndex + 1)); + + // The failures are ordered lexicographically, so we know that we can rely on the testName changing to create a new table + if (testName != previousTestName) { + if (!previousTestName.isEmpty()) { + closeTable(stream); + } + + previousTestName = testName; + + stream << "\t\t

      " << testName << "

      \n"; + + openTable(stream); + } + + createEntry(testNumber.toInt(), filename, stream, false); + } + + closeTable(stream); +} + +void AWSInterface::openTable(QTextStream& stream) { + stream << "\t\t\n"; + stream << "\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\n"; +} + +void AWSInterface::closeTable(QTextStream& stream) { + stream << "\t\t

      Test

      Actual Image

      Expected Image

      Difference Image

      \n"; +} + +void AWSInterface::createEntry(int index, const QString& testResult, QTextStream& stream, const bool isFailure) { + stream << "\t\t\t\n"; + stream << "\t\t\t\t

      " << QString::number(index) << "

      \n"; + + // For a test named `D:/t/fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf/Failure_1--tests.engine.interaction.pick.collision.many.00000` + // we need `Failure_1--tests.engine.interaction.pick.collision.many.00000` + QStringList resultNameComponents = testResult.split('/'); + QString resultName = resultNameComponents[resultNameComponents.length() - 1]; + + QString folder; + bool differenceFileFound; + if (isFailure) { + folder = FAILURES_FOLDER; + differenceFileFound = QFile::exists(_htmlFailuresFolder + "/" + resultName + "/Difference Image.png"); + } else { + folder = SUCCESSES_FOLDER; + differenceFileFound = QFile::exists(_htmlSuccessesFolder + "/" + resultName + "/Difference Image.png"); + } + + + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + + if (differenceFileFound) { + stream << "\t\t\t\t\n"; + } else { + stream << "\t\t\t\t

      No Image Found

      \n"; + } + + stream << "\t\t\t\n"; +} + +void AWSInterface::updateAWS() { + QString filename = _workingDirectory + "/updateAWS.py"; + if (QFile::exists(filename)) { + QFile::remove(filename); + } + QFile file(filename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not create 'addTestCases.py'"); + exit(-1); + } + + QTextStream stream(&file); + + stream << "import boto3\n"; + stream << "s3 = boto3.resource('s3')\n\n"; + + QDirIterator it1(_htmlFailuresFolder.toStdString().c_str()); + while (it1.hasNext()) { + QString nextDirectory = it1.next(); + + // Skip `.` and `..` directories + if (nextDirectory.right(1) == ".") { + continue; + } + + // nextDirectory looks like `D:/t/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/failures/engine.render.effect.bloom.00000` + // We need to concatenate the last 3 components, to get `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/failures/engine.render.effect.bloom.00000` + QStringList parts = nextDirectory.split('/'); + QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1]; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Actual Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n"; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Expected Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n"; + + if (QFile::exists(_htmlFailuresFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) { + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Difference Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n"; + } + } + + QDirIterator it2(_htmlSuccessesFolder.toStdString().c_str()); + while (it2.hasNext()) { + QString nextDirectory = it2.next(); + + // Skip `.` and `..` directories + if (nextDirectory.right(1) == ".") { + continue; + } + + // nextDirectory looks like `D:/t/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/successes/engine.render.effect.bloom.00000` + // We need to concatenate the last 3 components, to get `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/successes/engine.render.effect.bloom.00000` + QStringList parts = nextDirectory.split('/'); + QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1]; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Actual Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n"; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Expected Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n"; + + if (QFile::exists(_htmlSuccessesFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) { + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Difference Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n"; + } + } + + stream << "data = open('" << _workingDirectory << "/" << _resultsFolder << "/" << HTML_FILENAME << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << _resultsFolder << "/" + << HTML_FILENAME << "', Body=data, ContentType='text/html')\n"; + + file.close(); + + // Show user the URL + _urlLineEdit->setEnabled(true); + _urlLineEdit->setText(QString("https://") + AWS_BUCKET + ".s3.amazonaws.com/" + _resultsFolder + "/" + HTML_FILENAME); + _urlLineEdit->setCursorPosition(0); + + QProcess* process = new QProcess(); + + connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); + connect(process, static_cast(&QProcess::finished), this, + [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); + + QStringList parameters = QStringList() << filename ; + process->start(_pythonCommand, parameters); +} diff --git a/tools/auto-tester/src/AWSInterface.h b/tools/auto-tester/src/AWSInterface.h new file mode 100644 index 0000000000..c5be5f35bb --- /dev/null +++ b/tools/auto-tester/src/AWSInterface.h @@ -0,0 +1,72 @@ +// +// AWSInterface.h +// +// Created by Nissim Hadar on 3 Oct 2018. +// Copyright 2013 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_AWSInterface_h +#define hifi_AWSInterface_h + +#include +#include +#include +#include + +#include "ui/BusyWindow.h" + +#include "PythonInterface.h" + +class AWSInterface : public QObject { + Q_OBJECT +public: + explicit AWSInterface(QObject* parent = 0); + + void createWebPageFromResults(const QString& testResults, + const QString& workingDirectory, + QCheckBox* updateAWSCheckBox, + QLineEdit* urlLineEdit); + + void extractTestFailuresFromZippedFolder(); + void createHTMLFile(); + + void startHTMLpage(QTextStream& stream); + void writeHead(QTextStream& stream); + void writeBody(QTextStream& stream); + void finishHTMLpage(QTextStream& stream); + + void writeTitle(QTextStream& stream); + void writeTable(QTextStream& stream); + void openTable(QTextStream& stream); + void closeTable(QTextStream& stream); + + void createEntry(int index, const QString& testResult, QTextStream& stream, const bool isFailure); + + void updateAWS(); + +private: + QString _testResults; + QString _workingDirectory; + QString _resultsFolder; + QString _htmlFailuresFolder; + QString _htmlSuccessesFolder; + QString _htmlFilename; + + const QString FAILURES_FOLDER{ "failures" }; + const QString SUCCESSES_FOLDER{ "successes" }; + const QString HTML_FILENAME{ "TestResults.html" }; + + BusyWindow _busyWindow; + + PythonInterface* _pythonInterface; + QString _pythonCommand; + + QString AWS_BUCKET{ "hifi-qa" }; + + QLineEdit* _urlLineEdit; +}; + +#endif // hifi_AWSInterface_h \ No newline at end of file diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/auto-tester/src/Downloader.cpp index 530a3b61bd..cb9863f34d 100644 --- a/tools/auto-tester/src/Downloader.cpp +++ b/tools/auto-tester/src/Downloader.cpp @@ -11,20 +11,20 @@ #include -Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) { +Downloader::Downloader(QUrl fileURL, QObject *parent) : QObject(parent) { connect( &_networkAccessManager, SIGNAL (finished(QNetworkReply*)), this, SLOT (fileDownloaded(QNetworkReply*)) ); - QNetworkRequest request(imageUrl); + QNetworkRequest request(fileURL); _networkAccessManager.get(request); } void Downloader::fileDownloaded(QNetworkReply* reply) { QNetworkReply::NetworkError error = reply->error(); if (error != QNetworkReply::NetworkError::NoError) { - QMessageBox::information(0, "Test Aborted", "Failed to download image: " + reply->errorString()); + QMessageBox::information(0, "Test Aborted", "Failed to download file: " + reply->errorString()); return; } diff --git a/tools/auto-tester/src/Downloader.h b/tools/auto-tester/src/Downloader.h index b0ad58fac5..6d1029698f 100644 --- a/tools/auto-tester/src/Downloader.h +++ b/tools/auto-tester/src/Downloader.h @@ -30,7 +30,7 @@ class Downloader : public QObject { Q_OBJECT public: - explicit Downloader(QUrl imageUrl, QObject *parent = 0); + explicit Downloader(QUrl fileURL, QObject *parent = 0); QByteArray downloadedData() const; diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/auto-tester/src/PythonInterface.cpp new file mode 100644 index 0000000000..4922b8a8df --- /dev/null +++ b/tools/auto-tester/src/PythonInterface.cpp @@ -0,0 +1,32 @@ +// +// PythonInterface.cpp +// +// Created by Nissim Hadar on Oct 6, 2018. +// Copyright 2013 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 "PythonInterface.h" + +#include +#include +#include + +PythonInterface::PythonInterface() { + if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { + QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); + if (!QFile::exists(_pythonPath + "/" + _pythonExe)) { + QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + _pythonPath); + } + _pythonCommand = _pythonPath + "/" + _pythonExe; + } else { + QMessageBox::critical(0, "PYTHON_PATH not defined", + "Please set PYTHON_PATH to directory containing the Python executable"); + exit(-1); + } +} + +QString PythonInterface::getPythonCommand() { + return _pythonCommand; +} diff --git a/tools/auto-tester/src/PythonInterface.h b/tools/auto-tester/src/PythonInterface.h new file mode 100644 index 0000000000..f32a39a644 --- /dev/null +++ b/tools/auto-tester/src/PythonInterface.h @@ -0,0 +1,26 @@ +// +// PythonInterface.h +// +// Created by Nissim Hadar on Oct 6, 2018. +// Copyright 2013 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_PythonInterface_h +#define hifi_PythonInterface_h + +#include + +class PythonInterface { +public: + PythonInterface(); + + QString getPythonCommand(); + +private: + const QString _pythonExe{ "python.exe" }; + QString _pythonCommand; +}; + +#endif // hifi_PythonInterface_h diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp index 3da789f405..582f6209af 100644 --- a/tools/auto-tester/src/Test.cpp +++ b/tools/auto-tester/src/Test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,10 @@ extern AutoTester* autoTester; #include -Test::Test() { +Test::Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode) { + _progressBar = progressBar; + _checkBoxInteractiveMode = checkBoxInteractiveMode; + _mismatchWindow.setModal(true); if (autoTester) { @@ -34,17 +38,17 @@ Test::Test() { bool Test::createTestResultsFolderPath(const QString& directory) { QDateTime now = QDateTime::currentDateTime(); - _testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT); + _testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT) + "(local)[" + QHostInfo::localHostName() + "]"; QDir testResultsFolder(_testResultsFolderPath); // Create a new test results folder return QDir().mkdir(_testResultsFolderPath); } -void Test::zipAndDeleteTestResultsFolder() { +QString Test::zipAndDeleteTestResultsFolder() { QString zippedResultsFileName { _testResultsFolderPath + ".zip" }; QFileInfo fileInfo(zippedResultsFileName); - if (!fileInfo.exists()) { + if (fileInfo.exists()) { QFile::remove(zippedResultsFileName); } @@ -55,25 +59,30 @@ void Test::zipAndDeleteTestResultsFolder() { //In all cases, for the next evaluation _testResultsFolderPath = ""; - _index = 1; + _failureIndex = 1; + _successIndex = 1; + + return zippedResultsFileName; } -bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) { - progressBar->setMinimum(0); - progressBar->setMaximum(_expectedImagesFullFilenames.length() - 1); - progressBar->setValue(0); - progressBar->setVisible(true); +int Test::compareImageLists() { + _progressBar->setMinimum(0); + _progressBar->setMaximum(_expectedImagesFullFilenames.length() - 1); + _progressBar->setValue(0); + _progressBar->setVisible(true); // Loop over both lists and compare each pair of images // Quit loop if user has aborted due to a failed test. - bool success{ true }; bool keepOn{ true }; + int numberOfFailures{ 0 }; for (int i = 0; keepOn && i < _expectedImagesFullFilenames.length(); ++i) { // First check that images are the same size QImage resultImage(_resultImagesFullFilenames[i]); QImage expectedImage(_expectedImagesFullFilenames[i]); double similarityIndex; // in [-1.0 .. 1.0], where 1.0 means images are identical + + bool isInteractiveMode = (!_isRunningFromCommandLine && _checkBoxInteractiveMode->isChecked() && !_isRunningInAutomaticTestRun); // similarityIndex is set to -100.0 to indicate images are not the same size if (isInteractiveMode && (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height())) { @@ -83,19 +92,20 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) similarityIndex = _imageComparer.compareImages(resultImage, expectedImage); } - if (similarityIndex < THRESHOLD) { - TestFailure testFailure = TestFailure{ - (float)similarityIndex, - _expectedImagesFullFilenames[i].left(_expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /) - QFileInfo(_expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image - QFileInfo(_resultImagesFullFilenames[i].toStdString().c_str()).fileName() // filename of result image - }; + TestResult testResult = TestResult{ + (float)similarityIndex, + _expectedImagesFullFilenames[i].left(_expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /) + QFileInfo(_expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image + QFileInfo(_resultImagesFullFilenames[i].toStdString().c_str()).fileName() // filename of result image + }; - _mismatchWindow.setTestFailure(testFailure); + _mismatchWindow.setTestResult(testResult); + + if (similarityIndex < THRESHOLD) { + ++numberOfFailures; if (!isInteractiveMode) { - appendTestResultsToFile(_testResultsFolderPath, testFailure, _mismatchWindow.getComparisonImage()); - success = false; + appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), true); } else { _mismatchWindow.exec(); @@ -103,41 +113,53 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) case USER_RESPONSE_PASS: break; case USE_RESPONSE_FAIL: - appendTestResultsToFile(_testResultsFolderPath, testFailure, _mismatchWindow.getComparisonImage()); - success = false; + appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), true); break; case USER_RESPONSE_ABORT: keepOn = false; - success = false; break; default: assert(false); break; } } + } else { + appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), false); } - progressBar->setValue(i); + _progressBar->setValue(i); } - progressBar->setVisible(false); - return success; + _progressBar->setVisible(false); + return numberOfFailures; } -void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) { +void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestResult testResult, QPixmap comparisonImage, bool hasFailed) { if (!QDir().exists(_testResultsFolderPath)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + _testResultsFolderPath + " not found"); exit(-1); } - QString failureFolderPath { _testResultsFolderPath + "/Failure_" + QString::number(_index) + "--" + testFailure._actualImageFilename.left(testFailure._actualImageFilename.length() - 4) }; - if (!QDir().mkdir(failureFolderPath)) { - QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath); + QString resultFolderPath; + if (hasFailed) { + resultFolderPath = _testResultsFolderPath + "/Failure_" + QString::number(_failureIndex) + "--" + + testResult._actualImageFilename.left(testResult._actualImageFilename.length() - 4); + + ++_failureIndex; + } else { + resultFolderPath = _testResultsFolderPath + "/Success_" + QString::number(_successIndex) + "--" + + testResult._actualImageFilename.left(testResult._actualImageFilename.length() - 4); + + ++_successIndex; + } + + if (!QDir().mkdir(resultFolderPath)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Failed to create folder " + resultFolderPath); exit(-1); } - ++_index; - QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME); + QFile descriptionFile(resultFolderPath + "/" + TEST_RESULTS_FILENAME); if (!descriptionFile.open(QIODevice::ReadWrite)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME); exit(-1); @@ -145,10 +167,10 @@ void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFa // Create text file describing the failure QTextStream stream(&descriptionFile); - stream << "Test failed in folder " << testFailure._pathname.left(testFailure._pathname.length() - 1) << endl; // remove trailing '/' - stream << "Expected image was " << testFailure._expectedImageFilename << endl; - stream << "Actual image was " << testFailure._actualImageFilename << endl; - stream << "Similarity _index was " << testFailure._error << endl; + stream << "Test failed in folder " << testResult._pathname.left(testResult._pathname.length() - 1) << endl; // remove trailing '/' + stream << "Expected image was " << testResult._expectedImageFilename << endl; + stream << "Actual image was " << testResult._actualImageFilename << endl; + stream << "Similarity index was " << testResult._error << endl; descriptionFile.close(); @@ -156,26 +178,34 @@ void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFa QString sourceFile; QString destinationFile; - sourceFile = testFailure._pathname + testFailure._expectedImageFilename; - destinationFile = failureFolderPath + "/" + "Expected Image.png"; + sourceFile = testResult._pathname + testResult._expectedImageFilename; + destinationFile = resultFolderPath + "/" + "Expected Image.png"; if (!QFile::copy(sourceFile, destinationFile)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile); exit(-1); } - sourceFile = testFailure._pathname + testFailure._actualImageFilename; - destinationFile = failureFolderPath + "/" + "Actual Image.png"; + sourceFile = testResult._pathname + testResult._actualImageFilename; + destinationFile = resultFolderPath + "/" + "Actual Image.png"; if (!QFile::copy(sourceFile, destinationFile)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile); exit(-1); } - comparisonImage.save(failureFolderPath + "/" + "Difference Image.png"); + comparisonImage.save(resultFolderPath + "/" + "Difference Image.png"); } -void Test::startTestsEvaluation(const QString& testFolder, const QString& branchFromCommandLine, const QString& userFromCommandLine) { - if (testFolder.isNull()) { - // Get list of JPEG images in folder, sorted by name +void Test::startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory, + const QString& branchFromCommandLine, + const QString& userFromCommandLine +) { + _isRunningFromCommandLine = isRunningFromCommandLine; + _isRunningInAutomaticTestRun = isRunningInAutomaticTestRun; + + if (snapshotDirectory.isNull()) { + // Get list of PNG images in folder, sorted by name QString previousSelection = _snapshotDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); if (!parent.isNull() && parent.right(1) != "/") { @@ -184,14 +214,14 @@ void Test::startTestsEvaluation(const QString& testFolder, const QString& branch _snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_snapshotDirectory == "") { _snapshotDirectory = previousSelection; return; } } else { - _snapshotDirectory = testFolder; - _exitWhenComplete = true; + _snapshotDirectory = snapshotDirectory; + _exitWhenComplete = (isRunningFromCommandLine && !isRunningInAutomaticTestRun); } // Quit if test results folder could not be created @@ -238,25 +268,28 @@ void Test::startTestsEvaluation(const QString& testFolder, const QString& branch } } - autoTester->downloadImages(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames); + autoTester->downloadFiles(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames, (void *)this); } - -void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) { - bool success = compareImageLists((!isRunningFromCommandline && interactiveMode), progressBar); +void Test::finishTestsEvaluation() { + int numberOfFailures = compareImageLists(); - if (interactiveMode && !isRunningFromCommandline) { - if (success) { + if (!_isRunningFromCommandLine && !_isRunningInAutomaticTestRun) { + if (numberOfFailures == 0) { QMessageBox::information(0, "Success", "All images are as expected"); } else { QMessageBox::information(0, "Failure", "One or more images are not as expected"); } } - zipAndDeleteTestResultsFolder(); + QString zippedFolderName = zipAndDeleteTestResultsFolder(); if (_exitWhenComplete) { exit(0); } + + if (_isRunningInAutomaticTestRun) { + autoTester->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); + } } bool Test::isAValidDirectory(const QString& pathname) { @@ -302,176 +335,8 @@ void Test::includeTest(QTextStream& textStream, const QString& testPathname) { textStream << "Script.include(testsRootPath + \"" << partialPathWithoutTests + "\");" << endl; } -// Creates a single script in a user-selected folder. -// This script will run all text.js scripts in every applicable sub-folder -void Test::createRecursiveScript() { - // Select folder to start recursing from - QString previousSelection = _testDirectory; - QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); - if (!parent.isNull() && parent.right(1) != "/") { - parent += "/"; - } - - _testDirectory = - QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", parent, - QFileDialog::ShowDirsOnly); - - // If user cancelled then restore previous selection and return - if (_testDirectory == "") { - _testDirectory = previousSelection; - return; - } - - createRecursiveScript(_testDirectory, true); -} - -// This method creates a `testRecursive.js` script in every sub-folder. -void Test::createAllRecursiveScripts() { - // Select folder to start recursing from - QString previousSelection = _testsRootDirectory; - QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); - if (!parent.isNull() && parent.right(1) != "/") { - parent += "/"; - } - - _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", - parent, QFileDialog::ShowDirsOnly); - - // If user cancelled then restore previous selection and return - if (_testsRootDirectory == "") { - _testsRootDirectory = previousSelection; - return; - } - - createRecursiveScript(_testsRootDirectory, false); - - QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); - while (it.hasNext()) { - QString directory = it.next(); - - // Only process directories - QDir dir; - if (!isAValidDirectory(directory)) { - continue; - } - - // Only process directories that have sub-directories - bool hasNoSubDirectories{ true }; - QDirIterator it2(directory.toStdString().c_str(), QDirIterator::Subdirectories); - while (it2.hasNext()) { - QString directory2 = it2.next(); - - // Only process directories - QDir dir; - if (isAValidDirectory(directory2)) { - hasNoSubDirectories = false; - break; - } - } - - if (!hasNoSubDirectories) { - createRecursiveScript(directory, false); - } - } - - QMessageBox::information(0, "Success", "Scripts have been created"); -} - -void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) { - const QString recursiveTestsFilename("testRecursive.js"); - QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename); - if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::critical(0, - "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), - "Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\"" - ); - - exit(-1); - } - - QTextStream textStream(&allTestsFilename); - - const QString DATE_TIME_FORMAT("MMM d yyyy, h:mm"); - textStream << "// This is an automatically generated file, created by auto-tester on " << QDateTime::currentDateTime().toString(DATE_TIME_FORMAT) << endl << endl; - - // Include 'autoTest.js' - QString branch = autoTester->getSelectedBranch(); - QString user = autoTester->getSelectedUser(); - - textStream << "PATH_TO_THE_REPO_PATH_UTILS_FILE = \"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + "/tests/utils/branchUtils.js\";" << endl; - textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl; - textStream << "var autoTester = createAutoTester(Script.resolvePath(\".\"));" << endl << endl; - - textStream << "var testsRootPath = autoTester.getTestsRootPath();" << endl << endl; - - // Wait 10 seconds before starting - textStream << "if (typeof Test !== 'undefined') {" << endl; - textStream << " Test.wait(10000);" << endl; - textStream << "};" << endl << endl; - - textStream << "autoTester.enableRecursive();" << endl; - textStream << "autoTester.enableAuto();" << endl << endl; - - // This is used to verify that the recursive test contains at least one test - bool testFound{ false }; - - // Directories are included in reverse order. The autoTester scripts use a stack mechanism, - // so this ensures that the tests run in alphabetical order (a convenience when debugging) - QStringList directories; - - // First test if top-level folder has a test.js file - const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME }; - QFileInfo fileInfo(testPathname); - if (fileInfo.exists()) { - // Current folder contains a test - directories.push_front(testPathname); - - testFound = true; - } - - QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories); - while (it.hasNext()) { - QString directory = it.next(); - - // Only process directories - QDir dir(directory); - if (!isAValidDirectory(directory)) { - continue; - } - - const QString testPathname { directory + "/" + TEST_FILENAME }; - QFileInfo fileInfo(testPathname); - if (fileInfo.exists()) { - // Current folder contains a test - directories.push_front(testPathname); - - testFound = true; - } - } - - if (interactiveMode && !testFound) { - QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found"); - allTestsFilename.close(); - return; - } - - // Now include the test scripts - for (int i = 0; i < directories.length(); ++i) { - includeTest(textStream, directories.at(i)); - } - - textStream << endl; - textStream << "autoTester.runRecursive();" << endl; - - allTestsFilename.close(); - - if (interactiveMode) { - QMessageBox::information(0, "Success", "Script has been created"); - } -} - void Test::createTests() { - // Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on + // Rename files sequentially, as ExpectedResult_00000.png, ExpectedResult_00001.png and so on // Any existing expected result images will be deleted QString previousSelection = _snapshotDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); @@ -482,7 +347,7 @@ void Test::createTests() { _snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_snapshotDirectory == "") { _snapshotDirectory = previousSelection; return; @@ -497,7 +362,7 @@ void Test::createTests() { _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select test root folder", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_testsRootDirectory == "") { _testsRootDirectory = previousSelection; return; @@ -613,9 +478,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { return relevantTextFromTest; } -// Create an MD file for a user-selected test. -// The folder selected must contain a script named "test.js", the file produced is named "test.md" -void Test::createMDFile() { +bool Test::createFileSetup() { // Folder selection QString previousSelection = _testDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); @@ -624,20 +487,18 @@ void Test::createMDFile() { } _testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", parent, - QFileDialog::ShowDirsOnly); + QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_testDirectory == "") { _testDirectory = previousSelection; - return; + return false; } - createMDFile(_testDirectory); - - QMessageBox::information(0, "Success", "MD file has been created"); + return true; } -void Test::createAllMDFiles() { +bool Test::createAllFilesSetup() { // Select folder to start recursing from QString previousSelection = _testsRootDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); @@ -645,12 +506,32 @@ void Test::createAllMDFiles() { parent += "/"; } - _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", parent, - QFileDialog::ShowDirsOnly); + _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, + QFileDialog::ShowDirsOnly); // If user cancelled then restore previous selection and return if (_testsRootDirectory == "") { _testsRootDirectory = previousSelection; + return false; + } + + return true; +} + +// Create an MD file for a user-selected test. +// The folder selected must contain a script named "test.js", the file produced is named "test.md" +void Test::createMDFile() { + if (!createFileSetup()) { + return; + } + + if (createMDFile(_testDirectory)) { + QMessageBox::information(0, "Success", "MD file has been created"); + } +} + +void Test::createAllMDFiles() { + if (!createAllFilesSetup()) { return; } @@ -681,18 +562,18 @@ void Test::createAllMDFiles() { QMessageBox::information(0, "Success", "MD files have been created"); } -void Test::createMDFile(const QString& _testDirectory) { +bool Test::createMDFile(const QString& directory) { // Verify folder contains test.js file - QString testFileName(_testDirectory + "/" + TEST_FILENAME); + QString testFileName(directory + "/" + TEST_FILENAME); QFileInfo testFileInfo(testFileName); if (!testFileInfo.exists()) { QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME); - return; + return false; } ExtractedText testScriptLines = getTestScriptLines(testFileName); - QString mdFilename(_testDirectory + "/" + "test.md"); + QString mdFilename(directory + "/" + "test.md"); QFile mdFile(mdFilename); if (!mdFile.open(QIODevice::WriteOnly)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename); @@ -705,9 +586,6 @@ void Test::createMDFile(const QString& _testDirectory) { QString testName = testScriptLines.title; stream << "# " << testName << "\n"; - // Find the relevant part of the path to the test (i.e. from "tests" down - QString partialPath = extractPathFromTestsDown(_testDirectory); - stream << "## Run this script URL: [Manual](./test.js?raw=true) [Auto](./testAuto.js?raw=true)(from menu/Edit/Open and Run scripts from URL...)." << "\n\n"; stream << "## Preconditions" << "\n"; @@ -727,6 +605,223 @@ void Test::createMDFile(const QString& _testDirectory) { } mdFile.close(); + + foreach (auto test, testScriptLines.stepList) { + delete test; + } + testScriptLines.stepList.clear(); + + return true; +} + +void Test::createTestAutoScript() { + if (!createFileSetup()) { + return; + } + + if (createTestAutoScript(_testDirectory)) { + QMessageBox::information(0, "Success", "'autoTester.js` script has been created"); + } +} + +void Test::createAllTestAutoScripts() { + if (!createAllFilesSetup()) { + return; + } + + // First test if top-level folder has a test.js file + const QString testPathname{ _testsRootDirectory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + createTestAutoScript(_testsRootDirectory); + } + + QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it.hasNext()) { + QString directory = it.next(); + + // Only process directories + QDir dir; + if (!isAValidDirectory(directory)) { + continue; + } + + const QString testPathname{ directory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + createTestAutoScript(directory); + } + } + + QMessageBox::information(0, "Success", "'autoTester.js' scripts have been created"); +} + +bool Test::createTestAutoScript(const QString& directory) { + // Verify folder contains test.js file + QString testFileName(directory + "/" + TEST_FILENAME); + QFileInfo testFileInfo(testFileName); + if (!testFileInfo.exists()) { + QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME); + return false; + } + + QString testAutoScriptFilename(directory + "/" + "testAuto.js"); + QFile testAutoScriptFile(testAutoScriptFilename); + if (!testAutoScriptFile.open(QIODevice::WriteOnly)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Failed to create file " + testAutoScriptFilename); + exit(-1); + } + + QTextStream stream(&testAutoScriptFile); + + stream << "if (typeof PATH_TO_THE_REPO_PATH_UTILS_FILE === 'undefined') PATH_TO_THE_REPO_PATH_UTILS_FILE = 'https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/utils/branchUtils.js';\n"; + stream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);\n"; + stream << "var autoTester = createAutoTester(Script.resolvePath('.'));\n\n"; + stream << "autoTester.enableAuto();\n\n"; + stream << "Script.include('./test.js?raw=true');\n"; + + testAutoScriptFile.close(); + return true; +} + +// Creates a single script in a user-selected folder. +// This script will run all text.js scripts in every applicable sub-folder +void Test::createRecursiveScript() { + if (!createFileSetup()) { + return; + } + + createRecursiveScript(_testDirectory, true); + QMessageBox::information(0, "Success", "'testRecursive.js` script has been created"); +} + +// This method creates a `testRecursive.js` script in every sub-folder. +void Test::createAllRecursiveScripts() { + if (!createAllFilesSetup()) { + return; + } + + createRecursiveScript(_testsRootDirectory, false); + + QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it.hasNext()) { + QString directory = it.next(); + + // Only process directories + QDir dir; + if (!isAValidDirectory(directory)) { + continue; + } + + // Only process directories that have sub-directories + bool hasNoSubDirectories{ true }; + QDirIterator it2(directory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it2.hasNext()) { + QString directory2 = it2.next(); + + // Only process directories + QDir dir; + if (isAValidDirectory(directory2)) { + hasNoSubDirectories = false; + break; + } + } + + if (!hasNoSubDirectories) { + createRecursiveScript(directory, false); + } + } + + QMessageBox::information(0, "Success", "Scripts have been created"); +} + +void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) { + const QString recursiveTestsFilename("testRecursive.js"); + QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename); + if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""); + + exit(-1); + } + + QTextStream textStream(&allTestsFilename); + + textStream << "// This is an automatically generated file, created by auto-tester" << endl; + + // Include 'autoTest.js' + QString branch = autoTester->getSelectedBranch(); + QString user = autoTester->getSelectedUser(); + + textStream << "PATH_TO_THE_REPO_PATH_UTILS_FILE = \"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + + "/tests/utils/branchUtils.js\";" + << endl; + textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl; + textStream << "var autoTester = createAutoTester(Script.resolvePath(\".\"));" << endl << endl; + + textStream << "var testsRootPath = autoTester.getTestsRootPath();" << endl << endl; + + // Wait 10 seconds before starting + textStream << "if (typeof Test !== 'undefined') {" << endl; + textStream << " Test.wait(10000);" << endl; + textStream << "};" << endl << endl; + + textStream << "autoTester.enableRecursive();" << endl; + textStream << "autoTester.enableAuto();" << endl << endl; + + // This is used to verify that the recursive test contains at least one test + bool testFound{ false }; + + // Directories are included in reverse order. The autoTester scripts use a stack mechanism, + // so this ensures that the tests run in alphabetical order (a convenience when debugging) + QStringList directories; + + // First test if top-level folder has a test.js file + const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + // Current folder contains a test + directories.push_front(testPathname); + + testFound = true; + } + + QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it.hasNext()) { + QString directory = it.next(); + + // Only process directories + QDir dir(directory); + if (!isAValidDirectory(directory)) { + continue; + } + + const QString testPathname{ directory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + // Current folder contains a test + directories.push_front(testPathname); + + testFound = true; + } + } + + if (interactiveMode && !testFound) { + QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found"); + allTestsFilename.close(); + return; + } + + // Now include the test scripts + for (int i = 0; i < directories.length(); ++i) { + includeTest(textStream, directories.at(i)); + } + + textStream << endl; + textStream << "autoTester.runRecursive();" << endl; + + allTestsFilename.close(); } void Test::createTestsOutline() { @@ -739,7 +834,7 @@ void Test::createTestsOutline() { _testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_testDirectory == "") { _testDirectory = previousSelection; return; @@ -949,3 +1044,19 @@ QString Test::getExpectedImagePartialSourceDirectory(const QString& filename) { void Test::setTestRailCreateMode(TestRailCreateMode testRailCreateMode) { _testRailCreateMode = testRailCreateMode; } + +void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) { + QString testResults = QFileDialog::getOpenFileName(nullptr, "Please select the zipped test results to update from", nullptr, + "Zipped Test Results (*.zip)"); + if (testResults.isNull()) { + return; + } + + QString tempDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store temporary files in", + nullptr, QFileDialog::ShowDirsOnly); + if (tempDirectory.isNull()) { + return; + } + + _awsInterface.createWebPageFromResults(testResults, tempDirectory, updateAWSCheckBox, urlLineEdit); +} \ No newline at end of file diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h index 853e9c98e2..f653a91782 100644 --- a/tools/auto-tester/src/Test.h +++ b/tools/auto-tester/src/Test.h @@ -16,6 +16,7 @@ #include #include +#include "AWSInterface.h" #include "ImageComparer.h" #include "ui/MismatchWindow.h" #include "TestRailInterface.h" @@ -41,28 +42,41 @@ enum TestRailCreateMode { class Test { public: - Test(); + Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode); - void startTestsEvaluation(const QString& testFolder = QString(), const QString& branchFromCommandLine = QString(), const QString& userFromCommandLine = QString()); - void finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar); + void startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory = QString(), + const QString& branchFromCommandLine = QString(), + const QString& userFromCommandLine = QString()); - void createRecursiveScript(); - void createAllRecursiveScripts(); - void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode); + void finishTestsEvaluation(); void createTests(); void createTestsOutline(); + bool createFileSetup(); + bool createAllFilesSetup(); + void createMDFile(); void createAllMDFiles(); - void createMDFile(const QString& topLevelDirectory); + bool createMDFile(const QString& directory); + + void createTestAutoScript(); + void createAllTestAutoScripts(); + bool createTestAutoScript(const QString& directory); void createTestRailTestCases(); void createTestRailRun(); + void updateTestRailRunResult(); - bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar); + void createRecursiveScript(); + void createAllRecursiveScripts(); + void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode); + + int compareImageLists(); QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory); @@ -70,10 +84,10 @@ public: void includeTest(QTextStream& textStream, const QString& testPathname); - void appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage); + void appendTestResultsToFile(const QString& testResultsFolderPath, TestResult testResult, QPixmap comparisonImage, bool hasFailed); bool createTestResultsFolderPath(const QString& directory); - void zipAndDeleteTestResultsFolder(); + QString zipAndDeleteTestResultsFolder(); static bool isAValidDirectory(const QString& pathname); QString extractPathFromTestsDown(const QString& fullPath); @@ -84,12 +98,20 @@ public: void setTestRailCreateMode(TestRailCreateMode testRailCreateMode); + void createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit); + private: + QProgressBar* _progressBar; + QCheckBox* _checkBoxInteractiveMode; + + bool _isRunningFromCommandLine{ false }; + bool _isRunningInAutomaticTestRun{ false }; + const QString TEST_FILENAME { "test.js" }; const QString TEST_RESULTS_FOLDER { "TestResults" }; const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; - const double THRESHOLD{ 0.96 }; + const double THRESHOLD{ 0.935 }; QDir _imageDirectory; @@ -98,7 +120,8 @@ private: ImageComparer _imageComparer; QString _testResultsFolderPath; - int _index { 1 }; + int _failureIndex{ 1 }; + int _successIndex{ 1 }; // Expected images are in the format ExpectedImage_dddd.jpg (d == decimal digit) const int NUM_DIGITS { 5 }; @@ -132,8 +155,9 @@ private: bool _exitWhenComplete{ false }; TestRailInterface _testRailInterface; - TestRailCreateMode _testRailCreateMode { PYTHON }; + + AWSInterface _awsInterface; }; #endif // hifi_test_h \ No newline at end of file diff --git a/tools/auto-tester/src/TestRailInterface.cpp b/tools/auto-tester/src/TestRailInterface.cpp index 93559490e5..f943935539 100644 --- a/tools/auto-tester/src/TestRailInterface.cpp +++ b/tools/auto-tester/src/TestRailInterface.cpp @@ -16,66 +16,40 @@ #include #include +#include #include #include TestRailInterface::TestRailInterface() { _testRailTestCasesSelectorWindow.setURL("https://highfidelity.testrail.net"); - ////_testRailTestCasesSelectorWindow.setURL("https://nissimhadar.testrail.io"); _testRailTestCasesSelectorWindow.setUser("@highfidelity.io"); - ////_testRailTestCasesSelectorWindow.setUser("nissim.hadar@gmail.com"); - _testRailTestCasesSelectorWindow.setProjectID(INTERFACE_PROJECT_ID); - ////_testRailTestCasesSelectorWindow.setProjectID(2); + _testRailTestCasesSelectorWindow.setProjectID(INTERFACE_AUTOMATION_PROJECT_ID); _testRailTestCasesSelectorWindow.setSuiteID(INTERFACE_SUITE_ID); - ////_testRailTestCasesSelectorWindow.setSuiteID(2); _testRailRunSelectorWindow.setURL("https://highfidelity.testrail.net"); - ////_testRailRunSelectorWindow.setURL("https://nissimhadar.testrail.io"); _testRailRunSelectorWindow.setUser("@highfidelity.io"); - ////_testRailRunSelectorWindow.setUser("nissim.hadar@gmail.com"); - _testRailRunSelectorWindow.setProjectID(INTERFACE_PROJECT_ID); - ////_testRailRunSelectorWindow.setProjectID(2); + _testRailRunSelectorWindow.setProjectID(INTERFACE_AUTOMATION_PROJECT_ID); _testRailRunSelectorWindow.setSuiteID(INTERFACE_SUITE_ID); - ////_testRailRunSelectorWindow.setSuiteID(2); _testRailResultsSelectorWindow.setURL("https://highfidelity.testrail.net"); - ////_testRailResultsSelectorWindow.setURL("https://nissimhadar.testrail.io"); _testRailResultsSelectorWindow.setUser("@highfidelity.io"); - ////_testRailResultsSelectorWindow.setUser("nissim.hadar@gmail.com"); - _testRailResultsSelectorWindow.setProjectID(INTERFACE_PROJECT_ID); - ////_testRailResultsSelectorWindow.setProjectID(2); + _testRailResultsSelectorWindow.setProjectID(INTERFACE_AUTOMATION_PROJECT_ID); _testRailResultsSelectorWindow.setSuiteID(INTERFACE_SUITE_ID); - ////_testRailResultsSelectorWindow.setSuiteID(2); + + _pythonInterface = new PythonInterface(); + _pythonCommand = _pythonInterface->getPythonCommand(); } QString TestRailInterface::getObject(const QString& path) { return path.right(path.length() - path.lastIndexOf("/") - 1); } - -bool TestRailInterface::setPythonCommand() { - if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { - QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); - if (!QFile::exists(_pythonPath + "/" + pythonExe)) { - QMessageBox::critical(0, pythonExe, QString("Python executable not found in ") + _pythonPath); - } - _pythonCommand = _pythonPath + "/" + pythonExe; - return true; - } else { - QMessageBox::critical(0, "PYTHON_PATH not defined", - "Please set PYTHON_PATH to directory containing the Python executable"); - return false; - } - - return false; -} - // Creates the testrail.py script // This is the file linked to from http://docs.gurock.com/testrail-api2/bindings-python void TestRailInterface::createTestRailDotPyScript() { @@ -240,7 +214,7 @@ bool TestRailInterface::requestTestRailTestCasesDataFromUser() { _url = _testRailTestCasesSelectorWindow.getURL() + "/"; _user = _testRailTestCasesSelectorWindow.getUser(); _password = _testRailTestCasesSelectorWindow.getPassword(); - ////_password = "tutKA76";//// + _projectID = QString::number(_testRailTestCasesSelectorWindow.getProjectID()); _suiteID = QString::number(_testRailTestCasesSelectorWindow.getSuiteID()); @@ -258,7 +232,7 @@ bool TestRailInterface::requestTestRailRunDataFromUser() { _url = _testRailRunSelectorWindow.getURL() + "/"; _user = _testRailRunSelectorWindow.getUser(); _password = _testRailRunSelectorWindow.getPassword(); - ////_password = "tutKA76";//// + _projectID = QString::number(_testRailRunSelectorWindow.getProjectID()); _suiteID = QString::number(_testRailRunSelectorWindow.getSuiteID()); @@ -276,7 +250,7 @@ bool TestRailInterface::requestTestRailResultsDataFromUser() { _url = _testRailResultsSelectorWindow.getURL() + "/"; _user = _testRailResultsSelectorWindow.getUser(); _password = _testRailResultsSelectorWindow.getPassword(); - ////_password = "tutKA76";//// + _projectID = QString::number(_testRailResultsSelectorWindow.getProjectID()); _suiteID = QString::number(_testRailResultsSelectorWindow.getSuiteID()); @@ -377,8 +351,9 @@ void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirect QMessageBox::Yes | QMessageBox::No).exec() ) { QProcess* process = new QProcess(); - connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); + connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); @@ -489,8 +464,8 @@ void TestRailInterface::addRun() { stream << "\tcase_ids.append(case['id'])\n\n"; // Now, we can create the run - stream << "data = { 'name': '" + _sectionNames[_testRailRunSelectorWindow.getSectionID()].replace("Section", "Run") + - "', 'suite_id': " + _suiteID + + stream << "data = { 'name': '" + _sectionNames[_testRailRunSelectorWindow.getSectionID()].replace("Section", "Run") + "[" + + QHostInfo::localHostName() + "]" + "', 'suite_id': " + _suiteID + ", 'include_all': False, 'case_ids': case_ids}\n"; stream << "run = client.send_post('add_run/" + _projectID + "', data)\n"; @@ -503,7 +478,7 @@ void TestRailInterface::addRun() { ) { QProcess* process = new QProcess(); connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); - + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); @@ -511,6 +486,7 @@ void TestRailInterface::addRun() { process->start(_pythonCommand, parameters); } } + void TestRailInterface::updateRunWithResults() { QString filename = _outputDirectory + "/updateRunWithResults.py"; if (QFile::exists(filename)) { @@ -536,17 +512,27 @@ void TestRailInterface::updateRunWithResults() { // The failed tests are read, formatted and inserted into a set // A failure named 'Failure_1--tests.content.entity.material.apply.avatars.00000' is formatted to 'content/entity/material/apply/avatars' // This is the name of the test in TestRail + // + // A success is named `Success_-tests. ... stream << "from os import listdir\n"; stream << "failed_tests = set()\n"; - stream << "for entry in listdir('" + _outputDirectory + "/" + tempName + "'):\n"; - stream << "\tparts = entry.split('--tests.')[1].split('.')\n"; - stream << "\tfailed_test = parts[0]\n"; - stream << "\tfor i in range(1, len(parts) - 1):\n"; - stream << "\t\tfailed_test = failed_test + '/' + parts[i]\n"; + QDir dir(_outputDirectory + "/" + TEMP_NAME); + if (dir.exists()) { + stream << "for entry in listdir('" + _outputDirectory + "/" + TEMP_NAME + "'):\n"; + + // skip over successes + stream << "\tif entry.split('_')[0] == 'Success':\n"; + stream << "\t\tcontinue\n"; + + stream << "\tparts = entry.split('--tests.')[1].split('.')\n"; + stream << "\tfailed_test = parts[0]\n"; + stream << "\tfor i in range(1, len(parts) - 1):\n"; + stream << "\t\tfailed_test = failed_test + '/' + parts[i]\n"; - stream << "\tfailed_tests.add(failed_test)\n\n"; + stream << "\tfailed_tests.add(failed_test)\n\n"; + } // Initialize the array of results that will be eventually used to update TestRail stream << "status_ids = []\n"; @@ -580,7 +566,13 @@ void TestRailInterface::updateRunWithResults() { stream << "\tresults.append({'case_id': case_ids[i], 'status_id': status_ids[i] })\n\n"; stream << "data = { 'results': results }\n"; - stream << "section = client.send_post('add_results_for_cases/' + str(" << runID << "), data)\n"; + stream << "client.send_post('add_results_for_cases/' + str(" << runID << "), data)\n"; + + // Also update the run + QStringList parts = _testResults.split('/'); + QString resultName = parts[parts.length() - 1].split('.')[0]; + stream << "client.send_post('update_run/' + str(" << runID << ")," + << " { 'description' : 'https://hifi-qa.s3.amazonaws.com/" << resultName << "/TestResults.html' })\n"; file.close(); @@ -590,7 +582,7 @@ void TestRailInterface::updateRunWithResults() { ) { QProcess* process = new QProcess(); connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); - + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); @@ -765,6 +757,7 @@ void TestRailInterface::getReleasesFromTestRail() { QProcess* process = new QProcess(); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { updateReleasesComboData(exitCode, exitStatus); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList parameters = QStringList() << filename; process->start(_pythonCommand, parameters); @@ -779,10 +772,6 @@ void TestRailInterface::createTestSuitePython(const QString& testDirectory, _userGitHub = userGitHub; _branchGitHub = branchGitHub; - if (!setPythonCommand()) { - return; - } - if (!requestTestRailTestCasesDataFromUser()) { return; } @@ -812,6 +801,7 @@ void TestRailInterface::createTestSuiteXML(const QString& testDirectory, QDomElement suiteName = _document.createElement("name"); suiteName.appendChild( _document.createTextNode("Test Suite - " + QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm"))); + topLevelSection.appendChild(suiteName); // This is the first call to 'process'. This is then called recursively to build the full XML tree @@ -908,7 +898,7 @@ QDomElement TestRailInterface::processTestXML(const QString& fullDirectory, ++i; QString title{ words[i] }; for (++i; i < words.length() - 1; ++i) { - title += " / " + words[i]; + title += "/" + words[i]; } QDomElement titleElement = _document.createElement("title"); @@ -1036,7 +1026,6 @@ void TestRailInterface::processTestPython(const QString& fullDirectory, QString testContent = QString("Execute instructions in [THIS TEST](") + testMDName + ")"; QString testExpected = QString("Refer to the expected result in the linked description."); - stream << "data = {\n" << "\t'title': '" << title << "',\n" << "\t'template_id': 2,\n" @@ -1087,6 +1076,7 @@ void TestRailInterface::getTestSectionsFromTestRail() { QProcess* process = new QProcess(); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { updateSectionsComboData(exitCode, exitStatus); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList parameters = QStringList() << filename; process->start(_pythonCommand, parameters); @@ -1125,6 +1115,7 @@ void TestRailInterface::getRunsFromTestRail() { QProcess* process = new QProcess(); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { updateRunsComboData(exitCode, exitStatus); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList parameters = QStringList() << filename; @@ -1134,10 +1125,6 @@ void TestRailInterface::getRunsFromTestRail() { void TestRailInterface::createTestRailRun(const QString& outputDirectory) { _outputDirectory = outputDirectory; - if (!setPythonCommand()) { - return; - } - if (!requestTestRailRunDataFromUser()) { return; } @@ -1151,10 +1138,7 @@ void TestRailInterface::createTestRailRun(const QString& outputDirectory) { void TestRailInterface::updateTestRailRunResults(const QString& testResults, const QString& tempDirectory) { _outputDirectory = tempDirectory; - - if (!setPythonCommand()) { - return; - } + _testResults = testResults; if (!requestTestRailResultsDataFromUser()) { return; @@ -1164,11 +1148,20 @@ void TestRailInterface::updateTestRailRunResults(const QString& testResults, con createTestRailDotPyScript(); // Extract test failures from zipped folder - QString tempSubDirectory = tempDirectory + "/" + tempName; + QString tempSubDirectory = tempDirectory + "/" + TEMP_NAME; QDir dir = tempSubDirectory; dir.mkdir(tempSubDirectory); JlCompress::extractDir(testResults, tempSubDirectory); // TestRail will be updated after the process initiated by getTestRunFromTestRail has completed getRunsFromTestRail(); + + dir.rmdir(tempSubDirectory); +} + +void TestRailInterface::extractTestFailuresFromZippedFolder(const QString& testResults, const QString& tempDirectory) { + QString tempSubDirectory = tempDirectory + "/" + TEMP_NAME; + QDir dir = tempSubDirectory; + dir.mkdir(tempSubDirectory); + JlCompress::extractDir(testResults, tempSubDirectory); } \ No newline at end of file diff --git a/tools/auto-tester/src/TestRailInterface.h b/tools/auto-tester/src/TestRailInterface.h index 6f250dfbba..6843ca0142 100644 --- a/tools/auto-tester/src/TestRailInterface.h +++ b/tools/auto-tester/src/TestRailInterface.h @@ -12,7 +12,6 @@ #define hifi_test_testrail_interface_h #include "ui/BusyWindow.h" - #include "ui/TestRailTestCasesSelectorWindow.h" #include "ui/TestRailRunSelectorWindow.h" #include "ui/TestRailResultsSelectorWindow.h" @@ -22,7 +21,9 @@ #include #include -class TestRailInterface : public QObject{ +#include "PythonInterface.h" + +class TestRailInterface : public QObject { Q_OBJECT public: @@ -65,9 +66,7 @@ public: bool requestTestRailRunDataFromUser(); bool requestTestRailResultsDataFromUser(); - void createAddTestCasesPythonScript(const QString& testDirectory, - const QString& userGitHub, - const QString& branchGitHub); + void createAddTestCasesPythonScript(const QString& testDirectory, const QString& userGitHub, const QString& branchGitHub); void processDirectoryPython(const QString& directory, QTextStream& stream, @@ -88,14 +87,14 @@ public: void addRun(); void updateRunWithResults(); - bool setPythonCommand(); + void extractTestFailuresFromZippedFolder(const QString& testResults, const QString& tempDirectory); private: // HighFidelity Interface project ID in TestRail - const int INTERFACE_PROJECT_ID{ 24 }; + const int INTERFACE_AUTOMATION_PROJECT_ID{ 26 }; // Rendering suite ID - const int INTERFACE_SUITE_ID{ 1147 }; + const int INTERFACE_SUITE_ID{ 1312 }; QDomDocument _document; @@ -111,13 +110,11 @@ private: QString _suiteID; QString _testDirectory; + QString _testResults; QString _outputDirectory; QString _userGitHub; QString _branchGitHub; - const QString pythonExe{ "python.exe" }; - QString _pythonCommand; - QStringList _releaseNames; QStringList _sectionNames; @@ -126,7 +123,10 @@ private: QStringList _runNames; std::vector _runIDs; - QString tempName{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" }; + QString TEMP_NAME{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" }; + + PythonInterface* _pythonInterface; + QString _pythonCommand; }; #endif \ No newline at end of file diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp new file mode 100644 index 0000000000..01ec04f254 --- /dev/null +++ b/tools/auto-tester/src/TestRunner.cpp @@ -0,0 +1,608 @@ +// +// TestRunner.cpp +// +// Created by Nissim Hadar on 1 Sept 2018. +// Copyright 2013 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 "TestRunner.h" + +#include +#include +#include + +#include "ui/AutoTester.h" +extern AutoTester* autoTester; + +#ifdef Q_OS_WIN +#include +#include +#endif + +TestRunner::TestRunner(std::vector dayCheckboxes, + std::vector timeEditCheckboxes, + std::vector timeEdits, + QLabel* workingFolderLabel, + QCheckBox* runServerless, + QCheckBox* runLatest, + QLineEdit* url, + QPushButton* runNow, + QObject* parent) : + QObject(parent) { + _dayCheckboxes = dayCheckboxes; + _timeEditCheckboxes = timeEditCheckboxes; + _timeEdits = timeEdits; + _workingFolderLabel = workingFolderLabel; + _runServerless = runServerless; + _runLatest = runLatest; + _url = url; + _runNow = runNow; + + installerThread = new QThread(); + installerWorker = new Worker(); + installerWorker->moveToThread(installerThread); + installerThread->start(); + connect(this, SIGNAL(startInstaller()), installerWorker, SLOT(runCommand())); + connect(installerWorker, SIGNAL(commandComplete()), this, SLOT(installationComplete())); + + interfaceThread = new QThread(); + interfaceWorker = new Worker(); + interfaceThread->start(); + interfaceWorker->moveToThread(interfaceThread); + connect(this, SIGNAL(startInterface()), interfaceWorker, SLOT(runCommand())); + connect(interfaceWorker, SIGNAL(commandComplete()), this, SLOT(interfaceExecutionComplete())); +} + +TestRunner::~TestRunner() { + delete installerThread; + delete interfaceThread; + + delete interfaceThread; + delete interfaceWorker; + + if (_timer) { + delete _timer; + } +} + +void TestRunner::setWorkingFolder() { + // Everything will be written to this folder + QString previousSelection = _workingFolder; + QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); + if (!parent.isNull() && parent.right(1) != "/") { + parent += "/"; + } + + _workingFolder = QFileDialog::getExistingDirectory(nullptr, "Please select a temporary folder for installation", parent, + QFileDialog::ShowDirsOnly); + + // If user canceled then restore previous selection and return + if (_workingFolder == "") { + _workingFolder = previousSelection; + return; + } + + _installationFolder = _workingFolder + "/High Fidelity"; + _logFile.setFileName(_workingFolder + "/log.txt"); + + autoTester->enableRunTabControls(); + _workingFolderLabel->setText(QDir::toNativeSeparators(_workingFolder)); + + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), this, SLOT(checkTime())); + _timer->start(30 * 1000); //time specified in ms +} + +void TestRunner::run() { + _runNow->setEnabled(false); + + _testStartDateTime = QDateTime::currentDateTime(); + _automatedTestIsRunning = true; + + // Initial setup + _branch = autoTester->getSelectedBranch(); + _user = autoTester->getSelectedUser(); + + // This will be restored at the end of the tests + saveExistingHighFidelityAppDataFolder(); + + // Download the latest High Fidelity build XML. + // Note that this is not needed for PR builds (or whenever `Run Latest` is unchecked) + // It is still downloaded, to simplify the flow + QStringList urls; + QStringList filenames; + + urls << DEV_BUILD_XML_URL; + filenames << DEV_BUILD_XML_FILENAME; + + updateStatusLabel("Downloading Build XML"); + + buildXMLDownloaded = false; + autoTester->downloadFiles(urls, _workingFolder, filenames, (void*)this); + + // `downloadComplete` will run after download has completed +} + +void TestRunner::downloadComplete() { + if (!buildXMLDownloaded) { + // Download of Build XML has completed + buildXMLDownloaded = true; + + // Download the High Fidelity installer + QStringList urls; + QStringList filenames; + if (_runLatest->isChecked()) { + parseBuildInformation(); + + _installerFilename = INSTALLER_FILENAME_LATEST; + + urls << _buildInformation.url; + filenames << _installerFilename; + } else { + QString urlText = _url->text(); + urls << urlText; + _installerFilename = getInstallerNameFromURL(urlText); + filenames << _installerFilename; + } + + updateStatusLabel("Downloading installer"); + + autoTester->downloadFiles(urls, _workingFolder, filenames, (void*)this); + + // `downloadComplete` will run again after download has completed + + } else { + // Download of Installer has completed + appendLog(QString("Tests started at ") + QString::number(_testStartDateTime.time().hour()) + ":" + + QString("%1").arg(_testStartDateTime.time().minute(), 2, 10, QChar('0')) + ", on " + + _testStartDateTime.date().toString("ddd, MMM d, yyyy")); + + updateStatusLabel("Installing"); + + // Kill any existing processes that would interfere with installation + killProcesses(); + + runInstaller(); + } +} + +void TestRunner::runInstaller() { + // Qt cannot start an installation process using QProcess::start (Qt Bug 9761) + // To allow installation, the installer is run using the `system` command + + QStringList arguments{ QStringList() << QString("/S") << QString("/D=") + QDir::toNativeSeparators(_installationFolder) }; + + QString installerFullPath = _workingFolder + "/" + _installerFilename; + + QString commandLine = + "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); + + installerWorker->setCommandLine(commandLine); + emit startInstaller(); +} + +void TestRunner::installationComplete() { + verifyInstallationSucceeded(); + + createSnapshotFolder(); + + updateStatusLabel("Running tests"); + + if (!_runServerless->isChecked()) { + startLocalServerProcesses(); + } + + runInterfaceWithTestScript(); +} + +void TestRunner::verifyInstallationSucceeded() { + // Exit if the executables are missing. + // On Windows, the reason is probably that UAC has blocked the installation. This is treated as a critical error +#ifdef Q_OS_WIN + QFileInfo interfaceExe(QDir::toNativeSeparators(_installationFolder) + "\\interface.exe"); + QFileInfo assignmentClientExe(QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe"); + QFileInfo domainServerExe(QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe"); + + if (!interfaceExe.exists() || !assignmentClientExe.exists() || !domainServerExe.exists()) { + QMessageBox::critical(0, "Installation of High Fidelity has failed", "Please verify that UAC has been disabled"); + exit(-1); + } +#endif +} + +void TestRunner::saveExistingHighFidelityAppDataFolder() { + QString dataDirectory{ "NOT FOUND" }; + +#ifdef Q_OS_WIN + dataDirectory = qgetenv("USERPROFILE") + "\\AppData\\Roaming"; +#endif + + if (_runLatest->isChecked()) { + _appDataFolder = dataDirectory + "\\High Fidelity"; + } else { + // We are running a PR build + _appDataFolder = dataDirectory + "\\High Fidelity - " + getPRNumberFromURL(_url->text()); + } + + _savedAppDataFolder = dataDirectory + "/" + UNIQUE_FOLDER_NAME; + if (_savedAppDataFolder.exists()) { + _savedAppDataFolder.removeRecursively(); + } + + if (_appDataFolder.exists()) { + // The original folder is saved in a unique name + _appDataFolder.rename(_appDataFolder.path(), _savedAppDataFolder.path()); + } + + // Copy an "empty" AppData folder (i.e. no entities) + copyFolder(QDir::currentPath() + "/AppDataHighFidelity", _appDataFolder.path()); +} + +void TestRunner::createSnapshotFolder() { + _snapshotFolder = _workingFolder + "/" + SNAPSHOT_FOLDER_NAME; + + // Just delete all PNGs from the folder if it already exists + if (QDir(_snapshotFolder).exists()) { + // Note that we cannot use just a `png` filter, as the filenames include periods + // Also, delete any `jpg` and `txt` files + // The idea is to leave only previous zipped result folders + QDirIterator it(_snapshotFolder.toStdString().c_str()); + while (it.hasNext()) { + QString filename = it.next(); + if (filename.right(4) == ".png" || filename.right(4) == ".jpg" || filename.right(4) == ".txt") { + QFile::remove(filename); + } + } + + } else { + QDir().mkdir(_snapshotFolder); + } +} + +void TestRunner::killProcesses() { +#ifdef Q_OS_WIN + try { + QStringList processesToKill = QStringList() << "interface.exe" + << "assignment-client.exe" + << "domain-server.exe" + << "server-console.exe"; + + // Loop until all pending processes to kill have actually died + QStringList pendingProcessesToKill; + do { + pendingProcessesToKill.clear(); + + // Get list of running tasks + HANDLE processSnapHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (processSnapHandle == INVALID_HANDLE_VALUE) { + throw("Process snapshot creation failure"); + } + + PROCESSENTRY32 processEntry32; + processEntry32.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(processSnapHandle, &processEntry32)) { + CloseHandle(processSnapHandle); + throw("Process32First failed"); + } + + // Kill any task in the list + do { + foreach (QString process, processesToKill) + if (QString(processEntry32.szExeFile) == process) { + QString commandLine = "taskkill /im " + process + " /f >nul"; + system(commandLine.toStdString().c_str()); + pendingProcessesToKill << process; + } + } while (Process32Next(processSnapHandle, &processEntry32)); + + QThread::sleep(2); + } while (!pendingProcessesToKill.isEmpty()); + + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +#endif +} + +void TestRunner::startLocalServerProcesses() { +#ifdef Q_OS_WIN + QString commandLine; + + commandLine = "start \"domain-server.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe\""; + system(commandLine.toStdString().c_str()); + + commandLine = + "start \"assignment-client.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe\" -n 6"; + system(commandLine.toStdString().c_str()); +#endif + // Give server processes time to stabilize + QThread::sleep(20); +} + +void TestRunner::runInterfaceWithTestScript() { + QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; + QString snapshotFolder = QString("\"") + QDir::toNativeSeparators(_snapshotFolder) + "\""; + + QString url = QString("hifi://localhost"); + if (_runServerless->isChecked()) { + // Move to an empty area + url = "file:///~serverless/tutorial.json"; + } else { + url = "hifi://localhost"; + } + + QString testScript = + QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js"; + + QString commandLine = exeFile + " --url " + url + " --no-updater --no-login-suggestion" + " --testScript " + testScript + + " quitWhenFinished --testResultsLocation " + snapshotFolder; + + interfaceWorker->setCommandLine(commandLine); + emit startInterface(); +} + +void TestRunner::interfaceExecutionComplete() { + killProcesses(); + + QFileInfo testCompleted(QDir::toNativeSeparators(_snapshotFolder) +"/tests_completed.txt"); + if (!testCompleted.exists()) { + QMessageBox::critical(0, "Tests not completed", "Interface seems to have crashed before completion of the test scripts"); + _runNow->setEnabled(true); + return; + } + + evaluateResults(); + + // The High Fidelity AppData folder will be restored after evaluation has completed +} + +void TestRunner::evaluateResults() { + updateStatusLabel("Evaluating results"); + autoTester->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user); +} + +void TestRunner::automaticTestRunEvaluationComplete(QString zippedFolder, int numberOfFailures) { + addBuildNumberToResults(zippedFolder); + restoreHighFidelityAppDataFolder(); + + updateStatusLabel("Testing complete"); + + QDateTime currentDateTime = QDateTime::currentDateTime(); + + QString completionText = QString("Tests completed at ") + QString::number(currentDateTime.time().hour()) + ":" + + QString("%1").arg(currentDateTime.time().minute(), 2, 10, QChar('0')) + ", on " + + currentDateTime.date().toString("ddd, MMM d, yyyy"); + + if (numberOfFailures == 0) { + completionText += "; no failures"; + } else if (numberOfFailures == 1) { + completionText += "; 1 failure"; + } else { + completionText += QString("; ") + QString::number(numberOfFailures) + " failures"; + } + appendLog(completionText); + + _automatedTestIsRunning = false; + + _runNow->setEnabled(true); +} + +void TestRunner::addBuildNumberToResults(QString zippedFolderName) { + QString augmentedFilename; + if (!_runLatest->isChecked()) { + augmentedFilename = zippedFolderName.replace("local", getPRNumberFromURL(_url->text())); + } else { + augmentedFilename = zippedFolderName.replace("local", _buildInformation.build); + } + QFile::rename(zippedFolderName, augmentedFilename); +} + +void TestRunner::restoreHighFidelityAppDataFolder() { + _appDataFolder.removeRecursively(); + + if (_savedAppDataFolder != QDir()) { + _appDataFolder.rename(_savedAppDataFolder.path(), _appDataFolder.path()); + } +} + +// Copies a folder recursively +void TestRunner::copyFolder(const QString& source, const QString& destination) { + try { + if (!QFileInfo(source).isDir()) { + // just a file copy + QFile::copy(source, destination); + } else { + QDir destinationDir(destination); + if (!destinationDir.cdUp()) { + throw("'source '" + source + "'seems to be a root folder"); + } + + if (!destinationDir.mkdir(QFileInfo(destination).fileName())) { + throw("Could not create destination folder '" + destination + "'"); + } + + QStringList fileNames = + QDir(source).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + + foreach (const QString& fileName, fileNames) { + copyFolder(QString(source + "/" + fileName), QString(destination + "/" + fileName)); + } + } + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +void TestRunner::checkTime() { + // No processing is done if a test is running + if (_automatedTestIsRunning) { + return; + } + + QDateTime now = QDateTime::currentDateTime(); + + // Check day of week + if (!_dayCheckboxes.at(now.date().dayOfWeek() - 1)->isChecked()) { + return; + } + + // Check the time + bool timeToRun{ false }; + + for (size_t i = 0; i < std::min(_timeEditCheckboxes.size(), _timeEdits.size()); ++i) { + if (_timeEditCheckboxes[i]->isChecked() && (_timeEdits[i]->time().hour() == now.time().hour()) && + (_timeEdits[i]->time().minute() == now.time().minute())) { + timeToRun = true; + break; + } + } + + if (timeToRun) { + run(); + } +} + +void TestRunner::updateStatusLabel(const QString& message) { + autoTester->updateStatusLabel(message); +} + +void TestRunner::appendLog(const QString& message) { + if (!_logFile.open(QIODevice::Append | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open the log file"); + exit(-1); + } + + _logFile.write(message.toStdString().c_str()); + _logFile.write("\n"); + _logFile.close(); + + autoTester->appendLogWindow(message); +} + +QString TestRunner::getInstallerNameFromURL(const QString& url) { + // An example URL: https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe + try { + QStringList urlParts = url.split("/"); + return urlParts[urlParts.size() - 1]; + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +QString TestRunner::getPRNumberFromURL(const QString& url) { + try { + QStringList urlParts = url.split("/"); + QStringList filenameParts = urlParts[urlParts.size() - 1].split("-"); + if (filenameParts.size() <= 3) { + throw "URL not in expected format, should look like `https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe`"; + } + return filenameParts[filenameParts.size() - 2]; + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +void TestRunner::parseBuildInformation() { + try { + QDomDocument domDocument; + QString filename{ _workingFolder + "/" + DEV_BUILD_XML_FILENAME }; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly) || !domDocument.setContent(&file)) { + throw QString("Could not open " + filename); + } + + QString platformOfInterest; +#ifdef Q_OS_WIN + platformOfInterest = "windows"; +#elif defined(Q_OS_MAC) + platformOfInterest = "mac"; +#endif + QDomElement element = domDocument.documentElement(); + + // Verify first element is "projects" + if (element.tagName() != "projects") { + throw("File seems to be in wrong format"); + } + + element = element.firstChild().toElement(); + if (element.tagName() != "project") { + throw("File seems to be in wrong format"); + } + + if (element.attribute("name") != "interface") { + throw("File is not from 'interface' build"); + } + + // Now loop over the platforms + while (!element.isNull()) { + element = element.firstChild().toElement(); + if (element.tagName() != "platform" || element.attribute("name") != platformOfInterest) { + continue; + } + + // Next element should be the build + element = element.firstChild().toElement(); + if (element.tagName() != "build") { + throw("File seems to be in wrong format"); + } + + // Next element should be the version + element = element.firstChild().toElement(); + if (element.tagName() != "version") { + throw("File seems to be in wrong format"); + } + + // Add the build number to the end of the filename + _buildInformation.build = element.text(); + + // First sibling should be stable_version + element = element.nextSibling().toElement(); + if (element.tagName() != "stable_version") { + throw("File seems to be in wrong format"); + } + + // Next sibling should be url + element = element.nextSibling().toElement(); + if (element.tagName() != "url") { + throw("File seems to be in wrong format"); + } + _buildInformation.url = element.text(); + } + + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +void Worker::setCommandLine(const QString& commandLine) { + _commandLine = commandLine; +} + +int Worker::runCommand() { + int result = system(_commandLine.toStdString().c_str()); + emit commandComplete(); + return result; +} \ No newline at end of file diff --git a/tools/auto-tester/src/TestRunner.h b/tools/auto-tester/src/TestRunner.h new file mode 100644 index 0000000000..e6cb7cd764 --- /dev/null +++ b/tools/auto-tester/src/TestRunner.h @@ -0,0 +1,151 @@ +// +// TestRunner.h +// +// Created by Nissim Hadar on 1 Sept 2018. +// Copyright 2013 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_testRunner_h +#define hifi_testRunner_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class BuildInformation { +public: + QString build; + QString url; +}; + +class Worker; + +class TestRunner : public QObject { + Q_OBJECT +public: + explicit TestRunner(std::vector dayCheckboxes, + std::vector timeEditCheckboxes, + std::vector timeEdits, + QLabel* workingFolderLabel, + QCheckBox* runServerless, + QCheckBox* runLatest, + QLineEdit* url, + QPushButton* runNow, + QObject* parent = 0); + + ~TestRunner(); + + void setWorkingFolder(); + + void run(); + + void downloadComplete(); + void runInstaller(); + void verifyInstallationSucceeded(); + + void saveExistingHighFidelityAppDataFolder(); + void restoreHighFidelityAppDataFolder(); + + void createSnapshotFolder(); + + void killProcesses(); + void startLocalServerProcesses(); + + void runInterfaceWithTestScript(); + + void evaluateResults(); + void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures); + void addBuildNumberToResults(QString zippedFolderName); + + void copyFolder(const QString& source, const QString& destination); + + void updateStatusLabel(const QString& message); + void appendLog(const QString& message); + + QString getInstallerNameFromURL(const QString& url); + QString getPRNumberFromURL(const QString& url); + + void parseBuildInformation(); + +private slots: + void checkTime(); + void installationComplete(); + void interfaceExecutionComplete(); + +signals: + void startInstaller(); + void startInterface(); + +private: + bool _automatedTestIsRunning{ false }; + + const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.exe" }; + + QString _installerURL; + QString _installerFilename; + const QString DEV_BUILD_XML_URL{ "https://highfidelity.com/dev-builds.xml" }; + const QString DEV_BUILD_XML_FILENAME{ "dev-builds.xml" }; + + bool buildXMLDownloaded; + + QDir _appDataFolder; + QDir _savedAppDataFolder; + + QString _workingFolder; + QString _installationFolder; + QString _snapshotFolder; + + const QString UNIQUE_FOLDER_NAME{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" }; + const QString SNAPSHOT_FOLDER_NAME{ "snapshots" }; + + QString _branch; + QString _user; + + std::vector _dayCheckboxes; + std::vector _timeEditCheckboxes; + std::vector _timeEdits; + QLabel* _workingFolderLabel; + QCheckBox* _runServerless; + QCheckBox* _runLatest; + QLineEdit* _url; + QPushButton* _runNow; + QTimer* _timer; + + QFile _logFile; + + QDateTime _testStartDateTime; + + QThread* installerThread; + QThread* interfaceThread; + Worker* installerWorker; + Worker* interfaceWorker; + + BuildInformation _buildInformation; +}; + +class Worker : public QObject { + Q_OBJECT +public: + void setCommandLine(const QString& commandLine); + +public slots: + int runCommand(); + +signals: + void commandComplete(); + void startInstaller(); + void startInterface(); + +private: + QString _commandLine; +}; +#endif // hifi_testRunner_h \ No newline at end of file diff --git a/tools/auto-tester/src/common.h b/tools/auto-tester/src/common.h index 939814df62..5df4e9c921 100644 --- a/tools/auto-tester/src/common.h +++ b/tools/auto-tester/src/common.h @@ -12,9 +12,9 @@ #include -class TestFailure { +class TestResult { public: - TestFailure(float error, QString pathname, QString expectedImageFilename, QString actualImageFilename) : + TestResult(float error, QString pathname, QString expectedImageFilename, QString actualImageFilename) : _error(error), _pathname(pathname), _expectedImageFilename(expectedImageFilename), diff --git a/tools/auto-tester/src/main.cpp b/tools/auto-tester/src/main.cpp index 03b8cf51ff..ac4b4593c5 100644 --- a/tools/auto-tester/src/main.cpp +++ b/tools/auto-tester/src/main.cpp @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { autoTester->setup(); if (!testFolder.isNull()) { - autoTester->runFromCommandLine(testFolder, branch, user); + autoTester->startTestsEvaluation(true ,false, testFolder, branch, user); } else { autoTester->show(); } diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp index 13bda4853f..32457c2224 100644 --- a/tools/auto-tester/src/ui/AutoTester.cpp +++ b/tools/auto-tester/src/ui/AutoTester.cpp @@ -28,34 +28,86 @@ AutoTester::AutoTester(QWidget* parent) : QMainWindow(parent) { connect(_ui.actionAbout, &QAction::triggered, this, &AutoTester::about); connect(_ui.actionContent, &QAction::triggered, this, &AutoTester::content); + // The second tab hides and shows the Windows task bar #ifndef Q_OS_WIN - _ui.tabWidget->setTabEnabled(3, false); + _ui.tabWidget->removeTab(1); #endif - // helpWindow.textBrowser->setText() + _ui.statusLabel->setText(""); + _ui.plainTextEdit->setReadOnly(true); + + setWindowTitle("Auto Tester - v6.7"); + + // Coming soon to an auto-tester near you... + //// _helpWindow.textBrowser->setText() +} + +AutoTester::~AutoTester() { + delete _signalMapper; + + if (_test) { + delete _test; + } + + if (_testRunner) { + delete _testRunner; + } } void AutoTester::setup() { - _test = new Test(); + if (_test) { + delete _test; + } + _test = new Test(_ui.progressBar, _ui.checkBoxInteractiveMode); + + std::vector dayCheckboxes; + dayCheckboxes.emplace_back(_ui.mondayCheckBox); + dayCheckboxes.emplace_back(_ui.tuesdayCheckBox); + dayCheckboxes.emplace_back(_ui.wednesdayCheckBox); + dayCheckboxes.emplace_back(_ui.thursdayCheckBox); + dayCheckboxes.emplace_back(_ui.fridayCheckBox); + dayCheckboxes.emplace_back(_ui.saturdayCheckBox); + dayCheckboxes.emplace_back(_ui.sundayCheckBox); + + std::vector timeEditCheckboxes; + timeEditCheckboxes.emplace_back(_ui.timeEdit1checkBox); + timeEditCheckboxes.emplace_back(_ui.timeEdit2checkBox); + timeEditCheckboxes.emplace_back(_ui.timeEdit3checkBox); + timeEditCheckboxes.emplace_back(_ui.timeEdit4checkBox); + + std::vector timeEdits; + timeEdits.emplace_back(_ui.timeEdit1); + timeEdits.emplace_back(_ui.timeEdit2); + timeEdits.emplace_back(_ui.timeEdit3); + timeEdits.emplace_back(_ui.timeEdit4); + + if (_testRunner) { + delete _testRunner; + } + _testRunner = new TestRunner(dayCheckboxes, timeEditCheckboxes, timeEdits, _ui.workingFolderLabel, _ui.checkBoxServerless, _ui.checkBoxRunLatest, _ui.urlLineEdit, _ui.runNowButton); } -void AutoTester::runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user) { - _isRunningFromCommandline = true; - _test->startTestsEvaluation(testFolder, branch, user); +void AutoTester::startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory, + const QString& branch, + const QString& user +) { + _test->startTestsEvaluation(isRunningFromCommandLine, isRunningInAutomaticTestRun, snapshotDirectory, branch, user); } void AutoTester::on_tabWidget_currentChanged(int index) { - if (index == 1 || index == 2) { - _ui.userTextEdit->setDisabled(false); - _ui.branchTextEdit->setDisabled(false); + if (index == 0 || index == 2 || index == 3) { + _ui.userLineEdit->setDisabled(false); + _ui.branchLineEdit->setDisabled(false); } else { - _ui.userTextEdit->setDisabled(true); - _ui.branchTextEdit->setDisabled(true); + _ui.userLineEdit->setDisabled(true); + _ui.branchLineEdit->setDisabled(true); } } void AutoTester::on_evaluateTestsButton_clicked() { - _test->startTestsEvaluation(); + _test->startTestsEvaluation(false, false); } void AutoTester::on_createRecursiveScriptButton_clicked() { @@ -78,6 +130,14 @@ void AutoTester::on_createAllMDFilesButton_clicked() { _test->createAllMDFiles(); } +void AutoTester::on_createTestAutoScriptButton_clicked() { + _test->createTestAutoScript(); +} + +void AutoTester::on_createAllTestAutoScriptsButton_clicked() { + _test->createAllTestAutoScripts(); +} + void AutoTester::on_createTestsOutlineButton_clicked() { _test->createTestsOutline(); } @@ -90,6 +150,28 @@ void AutoTester::on_createTestRailRunButton_clicked() { _test->createTestRailRun(); } +void AutoTester::on_setWorkingFolderButton_clicked() { + _testRunner->setWorkingFolder(); +} + +void AutoTester::enableRunTabControls() { + _ui.runNowButton->setEnabled(true); + _ui.daysGroupBox->setEnabled(true); + _ui.timesGroupBox->setEnabled(true); +} + +void AutoTester::on_runNowButton_clicked() { + _testRunner->run(); +} + +void AutoTester::on_checkBoxRunLatest_clicked() { + _ui.urlLineEdit->setEnabled(!_ui.checkBoxRunLatest->isChecked()); +} + +void AutoTester::automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures) { + _testRunner->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); +} + void AutoTester::on_updateTestRailRunResultsButton_clicked() { _test->updateTestRailRunResult(); } @@ -130,7 +212,11 @@ void AutoTester::on_createXMLScriptRadioButton_clicked() { _test->setTestRailCreateMode(XML); } -void AutoTester::downloadImage(const QUrl& url) { +void AutoTester::on_createWebPagePushButton_clicked() { + _test->createWebPage(_ui.updateAWSCheckBox, _ui.awsURLLineEdit); +} + +void AutoTester::downloadFile(const QUrl& url) { _downloaders.emplace_back(new Downloader(url, this)); connect(_downloaders[_index], SIGNAL(downloaded()), _signalMapper, SLOT(map())); @@ -139,70 +225,86 @@ void AutoTester::downloadImage(const QUrl& url) { ++_index; } -void AutoTester::downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames) { +void AutoTester::downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void *caller) { + connect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int))); + _directoryName = directoryName; _filenames = filenames; + _caller = caller; - _numberOfImagesToDownload = URLs.size(); - _numberOfImagesDownloaded = 0; + _numberOfFilesToDownload = URLs.size(); + _numberOfFilesDownloaded = 0; _index = 0; _ui.progressBar->setMinimum(0); - _ui.progressBar->setMaximum(_numberOfImagesToDownload - 1); + _ui.progressBar->setMaximum(_numberOfFilesToDownload - 1); _ui.progressBar->setValue(0); _ui.progressBar->setVisible(true); - _downloaders.clear(); - for (int i = 0; i < _numberOfImagesToDownload; ++i) { - QUrl imageURL(URLs[i]); - downloadImage(imageURL); + foreach (auto downloader, _downloaders) { + delete downloader; } - connect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveImage(int))); + _downloaders.clear(); + for (int i = 0; i < _numberOfFilesToDownload; ++i) { + downloadFile(URLs[i]); + } } -void AutoTester::saveImage(int index) { +void AutoTester::saveFile(int index) { try { QFile file(_directoryName + "/" + _filenames[index]); file.open(QIODevice::WriteOnly); file.write(_downloaders[index]->downloadedData()); file.close(); } catch (...) { - QMessageBox::information(0, "Test Aborted", "Failed to save image: " + _filenames[index]); + QMessageBox::information(0, "Test Aborted", "Failed to save file: " + _filenames[index]); _ui.progressBar->setVisible(false); return; } - ++_numberOfImagesDownloaded; + ++_numberOfFilesDownloaded; - if (_numberOfImagesDownloaded == _numberOfImagesToDownload) { - disconnect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveImage(int))); - _test->finishTestsEvaluation(_isRunningFromCommandline, _ui.checkBoxInteractiveMode->isChecked(), _ui.progressBar); + if (_numberOfFilesDownloaded == _numberOfFilesToDownload) { + disconnect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int))); + if (_caller == _test) { + _test->finishTestsEvaluation(); + } else if (_caller == _testRunner) { + _testRunner->downloadComplete(); + } } else { - _ui.progressBar->setValue(_numberOfImagesDownloaded); + _ui.progressBar->setValue(_numberOfFilesDownloaded); } } void AutoTester::about() { - QMessageBox::information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__); + QMessageBox::information(0, "About", QString("Built ") + __DATE__ + ", " + __TIME__); } void AutoTester::content() { - helpWindow.show(); + _helpWindow.show(); } void AutoTester::setUserText(const QString& user) { - _ui.userTextEdit->setText(user); + _ui.userLineEdit->setText(user); } QString AutoTester::getSelectedUser() { - return _ui.userTextEdit->toPlainText(); + return _ui.userLineEdit->text(); } void AutoTester::setBranchText(const QString& branch) { - _ui.branchTextEdit->setText(branch); + _ui.branchLineEdit->setText(branch); } QString AutoTester::getSelectedBranch() { - return _ui.branchTextEdit->toPlainText(); + return _ui.branchLineEdit->text(); } + +void AutoTester::updateStatusLabel(const QString& status) { + _ui.statusLabel->setText(status); +} + +void AutoTester::appendLogWindow(const QString& message) { + _ui.plainTextEdit->appendPlainText(message); +} \ No newline at end of file diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/auto-tester/src/ui/AutoTester.h index e29da5b716..429a8b60e1 100644 --- a/tools/auto-tester/src/ui/AutoTester.h +++ b/tools/auto-tester/src/ui/AutoTester.h @@ -19,19 +19,28 @@ #include "../Test.h" #include "HelpWindow.h" +#include "../TestRunner.h" +#include "../AWSInterface.h" class AutoTester : public QMainWindow { Q_OBJECT public: - AutoTester(QWidget *parent = Q_NULLPTR); + AutoTester(QWidget* parent = Q_NULLPTR); + ~AutoTester(); void setup(); - void runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user); + void startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory, + const QString& branch, + const QString& user); - void downloadImage(const QUrl& url); - void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames); + void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures); + + void downloadFile(const QUrl& url); + void downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void* caller); void setUserText(const QString& user); QString getSelectedUser(); @@ -39,21 +48,35 @@ public: void setBranchText(const QString& branch); QString getSelectedBranch(); + void enableRunTabControls(); + + void updateStatusLabel(const QString& status); + void appendLogWindow(const QString& message); + private slots: void on_tabWidget_currentChanged(int index); void on_evaluateTestsButton_clicked(); void on_createRecursiveScriptButton_clicked(); void on_createAllRecursiveScriptsButton_clicked(); - void on_createTestsButton_clicked(); + void on_createTestsButton_clicked(); void on_createMDFileButton_clicked(); void on_createAllMDFilesButton_clicked(); + void on_createTestAutoScriptButton_clicked(); + void on_createAllTestAutoScriptsButton_clicked(); + void on_createTestsOutlineButton_clicked(); void on_createTestRailTestCasesButton_clicked(); void on_createTestRailRunButton_clicked(); + + void on_setWorkingFolderButton_clicked(); + void on_runNowButton_clicked(); + + void on_checkBoxRunLatest_clicked(); + void on_updateTestRailRunResultsButton_clicked(); void on_hideTaskbarButton_clicked(); @@ -62,16 +85,21 @@ private slots: void on_createPythonScriptRadioButton_clicked(); void on_createXMLScriptRadioButton_clicked(); + void on_createWebPagePushButton_clicked(); + void on_closeButton_clicked(); - void saveImage(int index); + void saveFile(int index); void about(); void content(); private: Ui::AutoTesterClass _ui; - Test* _test; + Test* _test{ nullptr }; + TestRunner* _testRunner{ nullptr }; + + AWSInterface _awsInterface; std::vector _downloaders; @@ -82,13 +110,15 @@ private: // Used to enable passing a parameter to slots QSignalMapper* _signalMapper; - int _numberOfImagesToDownload { 0 }; - int _numberOfImagesDownloaded { 0 }; - int _index { 0 }; + int _numberOfFilesToDownload{ 0 }; + int _numberOfFilesDownloaded{ 0 }; + int _index{ 0 }; - bool _isRunningFromCommandline { false }; + bool _isRunningFromCommandline{ false }; - HelpWindow helpWindow; + HelpWindow _helpWindow; + + void* _caller; }; -#endif // hifi_AutoTester_h \ No newline at end of file +#endif // hifi_AutoTester_h \ No newline at end of file diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/auto-tester/src/ui/AutoTester.ui index ac8fcf5e86..b277fbdb2a 100644 --- a/tools/auto-tester/src/ui/AutoTester.ui +++ b/tools/auto-tester/src/ui/AutoTester.ui @@ -6,8 +6,8 @@ 0 0 - 432 - 734 + 720 + 870 @@ -23,8 +23,8 @@ - 166 - 610 + 470 + 750 100 40 @@ -36,10 +36,10 @@ - 12 + 45 140 - 408 - 461 + 630 + 580 @@ -52,8 +52,8 @@ - 96 - 20 + 195 + 60 220 40 @@ -65,8 +65,8 @@ - 96 - 100 + 70 + 180 220 40 @@ -78,8 +78,8 @@ - 96 - 150 + 320 + 180 220 40 @@ -91,8 +91,8 @@ - 96 - 230 + 195 + 120 220 40 @@ -104,8 +104,8 @@ - 96 - 310 + 70 + 300 220 40 @@ -117,8 +117,8 @@ - 96 - 360 + 320 + 300 220 40 @@ -127,30 +127,436 @@ Create all Recursive Scripts + + + + 70 + 240 + 220 + 40 + + + + Create testAuto script + + + + + + 320 + 240 + 220 + 40 + + + + Create all testAuto scripts + + + + + + Windows + + + + + 200 + 130 + 211 + 40 + + + + Hide Windows Taskbar + + + + + + 200 + 200 + 211 + 40 + + + + Show Windows Taskbar + + + + + + Run + + + + false + + + + 10 + 160 + 161 + 28 + + + + Run now + + + + + false + + + + 20 + 240 + 91 + 241 + + + + Days + + + + + 10 + 210 + 80 + 17 + + + + Sunday + + + + + + 10 + 90 + 80 + 17 + + + + Wednesday + + + + + + 10 + 60 + 80 + 17 + + + + Tuesday + + + + + + 10 + 120 + 80 + 17 + + + + Thursday + + + + + + 10 + 150 + 80 + 17 + + + + Friday + + + + + + 10 + 180 + 80 + 17 + + + + Saturday + + + + + + 10 + 30 + 80 + 17 + + + + Monday + + + + + + false + + + + 130 + 240 + 161 + 191 + + + + Times + + + + + 30 + 20 + 118 + 22 + + + + + + + 30 + 60 + 118 + 22 + + + + + + + 30 + 100 + 118 + 22 + + + + + + + 30 + 140 + 118 + 22 + + + + + + + 10 + 23 + 21 + 17 + + + + + + + + + + 10 + 63 + 21 + 17 + + + + + + + + + + 10 + 103 + 21 + 17 + + + + + + + + + + 10 + 143 + 21 + 17 + + + + + + + + + + + 10 + 20 + 161 + 28 + + + + Set Working Folder + + + + + + 190 + 20 + 321 + 31 + + + + (not set...) + + + + + + 300 + 210 + 311 + 331 + + + + + + + 300 + 170 + 41 + 31 + + + + Status: + + + + + + 350 + 170 + 271 + 31 + + + + ####### + + + + + + 20 + 70 + 120 + 20 + + + + <html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html> + + + Server-less + + + false + + + + + + 20 + 100 + 120 + 20 + + + + <html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html> + + + Run Latest + + + true + + + + + + 128 + 95 + 21 + 31 + + + + URL + + + + + false + + + + 160 + 100 + 451 + 21 + + + Evaluate - - - - 90 - 100 - 255 - 23 - - - - 24 - - - 90 - 50 - 131 + 200 + 180 + 120 20 @@ -164,8 +570,8 @@ - 200 - 40 + 330 + 170 101 40 @@ -177,14 +583,14 @@ - TestRail + Web Interface - 180 - 160 - 161 + 240 + 220 + 160 40 @@ -195,8 +601,8 @@ - 80 - 40 + 170 + 100 95 20 @@ -211,9 +617,9 @@ - 180 - 100 - 161 + 240 + 160 + 160 40 @@ -224,9 +630,9 @@ - 180 - 40 - 161 + 240 + 100 + 160 40 @@ -237,8 +643,8 @@ - 80 - 60 + 170 + 120 95 20 @@ -247,45 +653,86 @@ XML - - - - Windows - - + - 100 - 100 - 211 - 40 + 10 + 30 + 601 + 300 - - Hide Windows Taskbar + + TestRail - + - 100 - 170 - 211 - 40 + 10 + 350 + 601 + 151 - - Show Windows Taskbar + + Amazon Web Services + + + true + + + + 240 + 30 + 160 + 40 + + + + Create Web Page + + + + + + 150 + 42 + 81 + 17 + + + + Update AWS + + + + + + 20 + 90 + 561 + 21 + + + + groupBox + updateTestRailRunResultsButton + createPythonScriptRadioButton + createTestRailRunButton + createTestRailTestCasesButton + createXMLScriptRadioButton + groupBox_2 - 110 - 90 - 81 + 120 + 80 + 110 16 @@ -298,32 +745,12 @@ GitHub Branch - - - - 200 - 85 - 140 - 24 - - - - - - - 200 - 47 - 140 - 24 - - - - 110 - 50 - 81 + 120 + 40 + 110 16 @@ -336,13 +763,46 @@ GitHub User + + + + 80 + 760 + 255 + 23 + + + + 24 + + + + + + 220 + 40 + 161 + 21 + + + + + + + 220 + 80 + 161 + 21 + + + 0 0 - 432 + 720 21 @@ -388,6 +848,53 @@ + + userLineEdit + branchLineEdit + createTestsButton + createMDFileButton + createAllMDFilesButton + createTestsOutlineButton + createRecursiveScriptButton + createAllRecursiveScriptsButton + createTestAutoScriptButton + createAllTestAutoScriptsButton + hideTaskbarButton + showTaskbarButton + runNowButton + sundayCheckBox + wednesdayCheckBox + tuesdayCheckBox + thursdayCheckBox + fridayCheckBox + saturdayCheckBox + mondayCheckBox + timeEdit1 + timeEdit2 + timeEdit3 + timeEdit4 + timeEdit1checkBox + timeEdit2checkBox + timeEdit3checkBox + timeEdit4checkBox + setWorkingFolderButton + plainTextEdit + checkBoxServerless + checkBoxRunLatest + urlLineEdit + checkBoxInteractiveMode + evaluateTestsButton + updateTestRailRunResultsButton + createPythonScriptRadioButton + createTestRailRunButton + createTestRailTestCasesButton + createXMLScriptRadioButton + createWebPagePushButton + updateAWSCheckBox + awsURLLineEdit + closeButton + tabWidget + diff --git a/tools/auto-tester/src/ui/MismatchWindow.cpp b/tools/auto-tester/src/ui/MismatchWindow.cpp index 79d2ce9f61..58189b4795 100644 --- a/tools/auto-tester/src/ui/MismatchWindow.cpp +++ b/tools/auto-tester/src/ui/MismatchWindow.cpp @@ -22,6 +22,11 @@ MismatchWindow::MismatchWindow(QWidget *parent) : QDialog(parent) { } QPixmap MismatchWindow::computeDiffPixmap(QImage expectedImage, QImage resultImage) { + // Create an empty difference image if the images differ in size + if (expectedImage.height() != resultImage.height() || expectedImage.width() != resultImage.width()) { + return QPixmap(); + } + // This is an optimization, as QImage.setPixel() is embarrassingly slow unsigned char* buffer = new unsigned char[expectedImage.height() * expectedImage.width() * 3]; @@ -55,20 +60,20 @@ QPixmap MismatchWindow::computeDiffPixmap(QImage expectedImage, QImage resultIma return resultPixmap; } -void MismatchWindow::setTestFailure(TestFailure testFailure) { - errorLabel->setText("Similarity: " + QString::number(testFailure._error)); +void MismatchWindow::setTestResult(TestResult testResult) { + errorLabel->setText("Similarity: " + QString::number(testResult._error)); - imagePath->setText("Path to test: " + testFailure._pathname); + imagePath->setText("Path to test: " + testResult._pathname); - expectedFilename->setText(testFailure._expectedImageFilename); - resultFilename->setText(testFailure._actualImageFilename); + expectedFilename->setText(testResult._expectedImageFilename); + resultFilename->setText(testResult._actualImageFilename); - QPixmap expectedPixmap = QPixmap(testFailure._pathname + testFailure._expectedImageFilename); - QPixmap actualPixmap = QPixmap(testFailure._pathname + testFailure._actualImageFilename); + QPixmap expectedPixmap = QPixmap(testResult._pathname + testResult._expectedImageFilename); + QPixmap actualPixmap = QPixmap(testResult._pathname + testResult._actualImageFilename); _diffPixmap = computeDiffPixmap( - QImage(testFailure._pathname + testFailure._expectedImageFilename), - QImage(testFailure._pathname + testFailure._actualImageFilename) + QImage(testResult._pathname + testResult._expectedImageFilename), + QImage(testResult._pathname + testResult._actualImageFilename) ); expectedImage->setPixmap(expectedPixmap); diff --git a/tools/auto-tester/src/ui/MismatchWindow.h b/tools/auto-tester/src/ui/MismatchWindow.h index f203a2be6a..30c29076b3 100644 --- a/tools/auto-tester/src/ui/MismatchWindow.h +++ b/tools/auto-tester/src/ui/MismatchWindow.h @@ -20,7 +20,7 @@ class MismatchWindow : public QDialog, public Ui::MismatchWindow { public: MismatchWindow(QWidget *parent = Q_NULLPTR); - void setTestFailure(TestFailure testFailure); + void setTestResult(TestResult testResult); UserResponse getUserResponse() { return _userResponse; } diff --git a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp index 414e4fca79..505e04b33e 100644 --- a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp @@ -25,6 +25,7 @@ void TestRailResultsSelectorWindow::reset() { userLineEdit->setDisabled(false); passwordLineEdit->setDisabled(false); projectIDLineEdit->setDisabled(false); + suiteIDLineEdit->setDisabled(false); OKButton->setDisabled(true); @@ -37,6 +38,7 @@ void TestRailResultsSelectorWindow::on_acceptButton_clicked() { userLineEdit->setDisabled(true); passwordLineEdit->setDisabled(true); projectIDLineEdit->setDisabled(true); + suiteIDLineEdit->setDisabled(true); OKButton->setDisabled(false); diff --git a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp index 54a3985a8b..ac3419d46f 100644 --- a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp @@ -19,12 +19,12 @@ TestRailRunSelectorWindow::TestRailRunSelectorWindow(QWidget *parent) { projectIDLineEdit->setValidator(new QIntValidator(1, 999, this)); } - void TestRailRunSelectorWindow::reset() { urlLineEdit->setDisabled(false); userLineEdit->setDisabled(false); passwordLineEdit->setDisabled(false); projectIDLineEdit->setDisabled(false); + suiteIDLineEdit->setDisabled(false); OKButton->setDisabled(true); sectionsComboBox->setDisabled(true); @@ -35,6 +35,7 @@ void TestRailRunSelectorWindow::on_acceptButton_clicked() { userLineEdit->setDisabled(true); passwordLineEdit->setDisabled(true); projectIDLineEdit->setDisabled(true); + suiteIDLineEdit->setDisabled(true); OKButton->setDisabled(false); sectionsComboBox->setDisabled(false); diff --git a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp index abb873ea14..638fe71819 100644 --- a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp @@ -25,6 +25,7 @@ void TestRailTestCasesSelectorWindow::reset() { userLineEdit->setDisabled(false); passwordLineEdit->setDisabled(false); projectIDLineEdit->setDisabled(false); + suiteIDLineEdit->setDisabled(false); OKButton->setDisabled(true); @@ -37,6 +38,7 @@ void TestRailTestCasesSelectorWindow::on_acceptButton_clicked() { userLineEdit->setDisabled(true); passwordLineEdit->setDisabled(true); projectIDLineEdit->setDisabled(true); + suiteIDLineEdit->setDisabled(true); OKButton->setDisabled(false); diff --git a/tools/ktx-tool/CMakeLists.txt b/tools/ktx-tool/CMakeLists.txt index 9daf8e0a1a..6bb09e0a9e 100644 --- a/tools/ktx-tool/CMakeLists.txt +++ b/tools/ktx-tool/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME ktx-tool) setup_hifi_project(Quick Gui Concurrent) -link_hifi_libraries(shared networking image gl gpu ktx) +link_hifi_libraries(shared networking image gl shaders gpu ktx) target_gli() diff --git a/tools/scribe/CMakeLists.txt b/tools/scribe/CMakeLists.txt deleted file mode 100755 index 9c71aeec9c..0000000000 --- a/tools/scribe/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(TARGET_NAME scribe) - -# don't use the setup_hifi_project macro as we don't want Qt or GLM dependencies -file(GLOB TARGET_SRCS src/*) -add_executable(${TARGET_NAME} ${TARGET_SRCS}) -if (WIN32) - set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF") -endif() diff --git a/tools/scribe/src/TextTemplate.cpp b/tools/scribe/src/TextTemplate.cpp deleted file mode 100755 index 89937c4da6..0000000000 --- a/tools/scribe/src/TextTemplate.cpp +++ /dev/null @@ -1,1005 +0,0 @@ -// -// TextTemplate.cpp -// tools/shaderScribe/src -// -// Created by Sam Gateau on 12/15/2014. -// Copyright 2013 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 "TextTemplate.h" - -#include -#include -#include -#include - -typedef TextTemplate::Block::Pointer BlockPointer; -typedef TextTemplate::Config::Pointer ConfigPointer; -typedef TextTemplate::Pointer TextTemplatePointer; - -const std::string TextTemplate::Tag::NULL_VAR = "_SCRIBE_NULL"; - -//----------------------------------------------------------------------------- -TextTemplate::Config::Config() : - _includes(), - _funcs(), - _logStream(&std::cout), - _numErrors(0), - _includerCallback(TextTemplate::loadFile) { - _paths.push_back(""); -} - -const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) { - if (!config) { - return TextTemplatePointer(); - } - - TextTemplatePointer included = config->findInclude(include); - if (included) { - return included; - } - - // INcluded doest exist yet so let's try to create it - String includeStream; - if (config->_includerCallback(config, include, includeStream)) { - // ok, then create a new Template on the include file with this as lib - included = std::make_shared(include, config); - - std::stringstream src(includeStream); - - int nbErrors = included->parse(src); - if (nbErrors > 0) { - included->logError(included->_root, "File failed to parse, not included"); - return TextTemplatePointer(); - } - config->_includes.insert(Includes::value_type(include, included)); - return included; - } - - return TextTemplatePointer(); -} - -const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) { - Includes::iterator includeIt = _includes.find(String(include)); - if (includeIt != _includes.end()) { - return (*includeIt).second; - } else { - return TextTemplatePointer(); - } -} - -void TextTemplate::Config::addIncludePath(const char* path) { - _paths.push_back(String(path)); -} - -bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) { - String sourceFile(filename); - String fullfilename; - for (unsigned int i = 0; i < config->_paths.size(); i++) { - fullfilename = config->_paths[i] + sourceFile; - std::ifstream ifs; - ifs.open(fullfilename.c_str()); - if (ifs.is_open()) { - std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - source = str; - ifs.close(); - return (source.length() > 0); - } - } - - return false; -} - -TextTemplate::Funcs::Funcs() : - _funcs() { -} - -TextTemplate::Funcs::~Funcs() { -} - - -const BlockPointer TextTemplate::Funcs::findFunc(const char* func) { - map::iterator it = _funcs.find(String(func)); - if (it != _funcs.end()) { - return (*it).second; - } else { - return BlockPointer(); - } -} - -const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) { - BlockPointer included = findFunc(func); - if (! included) { - _funcs.insert(map::value_type(func, funcBlock)); - } - return included; -} - -void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) { - if (parent) { - parent->blocks.push_back(block); - block->parent = parent; - } -} - -const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) { - if (block && block->command.isBlockEnd()) { - return block->parent; - } else { - return block; - } -} - -void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) { - va_list argp; - va_start(argp, fmt); - - char buff[256]; - sprintf(buff, fmt, argp); - - _numErrors++; - log() << block->sourceName << " Error >>" << buff << std::endl; - - int level = 1; - displayTree(std::cerr, level); - -} - -bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { - std::stringstream dst; - while (!str->eof()) { - - std::string datatoken; - getline((*str), datatoken, Tag::BEGIN); - dst << datatoken; - - char next = str->peek(); - if (next == Tag::VAR) { - tagType = Tag::VARIABLE; - grabbed = dst.str(); - str->get(); // skip tag char - return true; - } else if (next == Tag::COM) { - tagType = Tag::COMMAND; - grabbed = dst.str(); - str->get(); // skip tag char - return true; - } else if (next == Tag::REM) { - tagType = Tag::REMARK; - grabbed = dst.str(); - str->get(); // skip tag char - return true; - } else { - if (!str->eof()) { - // false positive, just found the Tag::BEGIN without consequence - dst << Tag::BEGIN; - // keep searching - } else { - // end of the file finishing with no tag - tagType = Tag::INVALID; - grabbed = dst.str(); - return true; - } - } - } - - return false; -} - -bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { - std::stringstream dst; - - // preEnd char depends on tag type - char preEnd = Tag::COM; - if (tagType == Tag::VARIABLE) { - preEnd = Tag::VAR; - } else if (tagType == Tag::REMARK) { - preEnd = Tag::REM; - } - - while (!str->eof() && !str->fail()) { - // looking for the end of the tag means find the next preEnd - std::string dataToken; - getline((*str), dataToken, preEnd); - char end = str->peek(); - - dst << dataToken; - - // and if the next char is Tag::END then that's it - if (end == Tag::END) { - grabbed = dst.str(); - str->get(); // eat the Tag::END - return true; - } else { - // false positive, keep on searching - dst << preEnd; - } - } - grabbed = dst.str(); - return false; -} - -bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType, - Tag::Type& nextTagType) { - if (str->eof()) { - return false; - } - - if (!_steppingStarted) { - _steppingStarted = true; - return grabUntilBeginTag(str, grabbed, nextTagType); - } - - // Read from the last opening Tag::BEGIN captured to the next Tag::END - if (grabUntilEndTag(str, tag, tagType)) { - // skip trailing space and new lines only after Command or Remark tag block - if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) { - while (!str->eof() && !str->fail()) { - char c = str->peek(); - if ((c == ' ') || (c == '\t') || (c == '\n')) { - str->get(); - } else { - break; - } - } - } - - grabUntilBeginTag(str, grabbed, nextTagType); - } - - return true; //hasElement; -} - -const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag, - Tag::Type& tagType) { - switch (tagType) { - case Tag::INVALID: - block->ostr << grabbed; - return block; - break; - case Tag::VARIABLE: - return processStepVar(block, grabbed, tag); - break; - case Tag::COMMAND: - return processStepCommand(block, grabbed, tag); - break; - case Tag::REMARK: - return processStepRemark(block, grabbed, tag); - break; - } - - logError(block, "Invalid tag"); - return block; -} - -bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) { - bool goOn = true; - std::string::size_type i = 0; - while (goOn && (i < src.length())) { - char c = src[i]; - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) { - token += c; - } else { - if (!token.empty()) { - reminder = src.substr(i); - return true; - } - } - i++; - } - - return (!token.empty()); -} - -bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) { - std::stringstream str(src); - String token; - - while (!str.eof()) { - str >> token; - if (!str.fail()) { - arguments.push_back(token); - } - } - - return true; -} - -bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) { - if (src.empty()) { - return false; - } - - std::stringstream argstr(src); - std::stringstream dest; - bool inVar = false; - while (!argstr.eof()) { - // parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END - String token; - char tag; - if (!inVar) { - getline(argstr, token, Tag::BEGIN); - dest << token; - tag = argstr.peek(); - } else { - getline(argstr, token, Tag::END); - dest << token; - tag = token.back(); - } - - if (tag == Tag::VAR) { - if (!inVar) { - // real var coming: - arguments.push_back(dest.str()); - inVar = true; - } else { - // real var over - arguments.push_back(dest.str()); - inVar = false; - } - } else { - if (argstr.eof()) { - arguments.push_back(dest.str()); - } else { - // put back the tag char stolen - dest << (!inVar ? Tag::BEGIN : Tag::END); - } - } - } - - return true; -} - -bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) { - if (src.empty()) { - return false; - } - std::stringstream streamSrc(src); - String params; - getline(streamSrc, params, '('); - getline(streamSrc, params, ')'); - if (params.empty()) { - return false; - } - - std::stringstream str(params); - String token; - int nbTokens = 0; - while (!str.eof()) { - char c = str.peek(); - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) { - token += c; - } else if (c == ',') { - if (!token.empty()) { - if (token == Tag::NULL_VAR) { - arguments.push_back(Tag::NULL_VAR); - } else { - arguments.push_back(token); - } - nbTokens++; - } - token.clear(); - } - - str.get(); - } - - if (token.size()) { - arguments.push_back(token); - nbTokens++; - } - - return (nbTokens > 0); -} - -const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) { - // then look at the define - String var = tag; - String varName; - String val; - - if (grabFirstToken(var, varName, val)) { - if (!varName.empty()) { - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::VAR; - - newBlock->command.arguments.push_back(varName); - - if (!val.empty()) { - convertExpressionToFuncArguments(val, newBlock->command.arguments); - } - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; - } - } - - return block; -} - -const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) { - // Grab the command name - String command = tag; - String commandName; - - if (grabFirstToken(command, commandName, command)) { - if (commandName.compare("def") == 0) { - return processStepDef(block, grabbed, command); - } else if (commandName.compare("if") == 0) { - return processStepCommandIf(block, grabbed, command); - } else if (commandName.compare("endif") == 0) { - return processStepCommandEndIf(block, grabbed, command); - } else if (commandName.compare("else") == 0) { - return processStepCommandElse(block, grabbed, command); - } else if (commandName.compare("elif") == 0) { - return processStepCommandElif(block, grabbed, command); - } else if (commandName.compare("include") == 0) { - return processStepInclude(block, grabbed, command); - } else if (commandName.compare("func") == 0) { - return processStepFunc(block, grabbed, command); - } else if (commandName.compare("endfunc") == 0) { - return processStepEndFunc(block, grabbed, command); - } - } - - return block; -} - -const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) { - // then look at the define - String varName; - String val; - - if (!grabFirstToken(def, varName, val)) { - logError(block, "Invalid Var name in a tag"); - return block; - } - - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::DEF; - newBlock->command.arguments.push_back(varName); - if (!val.empty()) { - // loose first character which should be a white space - val = val.substr(val.find_first_not_of(' ')); - convertExpressionToDefArguments(val, newBlock->command.arguments); - } - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; -} - - -const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock depth - BlockPointer newIfBlock = std::make_shared(_root->sourceName); - newIfBlock->command.type = Command::IFBLOCK; - - Block::addNewBlock(parent, newIfBlock); - - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::IF; - convertExpressionToArguments(expression, newBlock->command.arguments); - - Block::addNewBlock(newIfBlock, newBlock); - - // dive in the new If branch - return newBlock; -} - -const BlockPointer TextTemplate::processStepCommandEndIf(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a if block ? - if ((parent->command.type == Command::IF) - || (parent->command.type == Command::ELIF) - || (parent->command.type == Command::ELSE)) { - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::ENDIF; - newBlock->command.arguments.push_back(expression); - - Block::addNewBlock(parent->parent->parent, newBlock); - - return newBlock; - } else { - logError(block, "Invalid block, not in a block"); - return block; - } - - return block; -} - -const BlockPointer TextTemplate::processStepCommandElse(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a if block ? - if ((parent->command.type == Command::IF) - || (parent->command.type == Command::ELIF)) { - // All good go back to the IfBlock - parent = parent->parent; - - // Add a new BLock depth - BlockPointer newBlock = std::make_shared(_root->sourceName); - newBlock->ostr << grabbed; - newBlock->command.type = Command::ELSE; - newBlock->command.arguments.push_back(expression); - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; - - } else if ((block)->command.type == Command::ELSE) { - logError(block, "Invalid block, in a block but after a block"); - return block; - } else { - logError(block, "Invalid block, not in a block"); - return block; - } -} - -const BlockPointer TextTemplate::processStepCommandElif(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a if block ? - if ((parent->command.type == Command::IF) - || (parent->command.type == Command::ELIF)) { - // All good go back to the IfBlock - parent = parent->parent; - - // Add a new BLock depth - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::ELIF; - convertExpressionToArguments(expression, newBlock->command.arguments); - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; - - } else if (parent->command.type == Command::ELSE) { - logError(block, "Invalid block, in a block but after a block"); - return block; - } else { - logError(block, "Invalid block, not in a block"); - return block; - } -} - -const BlockPointer TextTemplate::processStepRemark(const BlockPointer& block, String& grabbed, String& tag) { - // nothing to do :) - - // no need to think, let's just add the grabbed text - (block->ostr) << grabbed; - - return block; -} - -const BlockPointer TextTemplate::processStepInclude(const BlockPointer& block, String& grabbed, String& include) { - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::INCLUDE; - convertExpressionToArguments(include, newBlock->command.arguments); - - Block::addNewBlock(parent, newBlock); - - TextTemplatePointer inc = Config::addInclude(_config, newBlock->command.arguments.front().c_str()); - if (!inc) { - logError(newBlock, "Failed to include %s", newBlock->command.arguments.front().c_str()); - } - - // dive in the new block - return newBlock; -} - -const BlockPointer TextTemplate::processStepFunc(const BlockPointer& block, String& grabbed, String& func) { - // then look at the define - String varName; - String var; - - if (!grabFirstToken(func, varName, var)) { - logError(block, "Invalid func name <%s> in a block", func.c_str()); - return block; - } - - // DOes the func already exists ? - if (_config->_funcs.findFunc(varName.c_str())) { - logError(block, "Declaring a new func named <%s> already exists", func.c_str()); - return block; - } - - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::FUNC; - newBlock->command.arguments.push_back(varName); - convertExpressionToFuncArguments(var, newBlock->command.arguments); - - _config->_funcs.addFunc(varName.c_str(), newBlock); - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; -} - -const BlockPointer TextTemplate::processStepEndFunc(const BlockPointer& block, String& grabbed, String& tag) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a func block ? - if (parent->command.type == Command::FUNC) { - // Make sure the FUnc has been declared properly - BlockPointer funcBlock = _config->_funcs.findFunc(parent->command.arguments.front().c_str()); - if (funcBlock != parent) { - logError(block, "Mismatching blocks"); - return BlockPointer(); - } - - // Everything is cool , so let's unplugg the FUnc block from this tree and just put the EndFunc block - - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::ENDFUNC; - convertExpressionToArguments(tag, newBlock->command.arguments); - - newBlock->parent = parent->parent; - - parent->parent->blocks.back() = newBlock; - - parent->parent = 0; - - // dive in the new block - return newBlock; - } else { - logError(block, "Invalid block, not in a block"); - return BlockPointer(); - } - - return block; -} - -TextTemplate::TextTemplate(const String& name, const ConfigPointer& externalConfig) : - _config(externalConfig), - _root(new Block(name)), - _numErrors(0), - _steppingStarted(false) { -} - -TextTemplate::~TextTemplate() { -} - -int TextTemplate::scribe(std::ostream& dst, std::istream& src, Vars& vars) { - int nbErrors = parse(src); - - if (nbErrors == 0) { - return generate(dst, vars); - } - return nbErrors; -} - -int TextTemplate::generateTree(std::ostream& dst, const BlockPointer& block, Vars& vars) { - BlockPointer newCurrentBlock; - int numPasses = evalBlockGeneration(dst, block, vars, newCurrentBlock); - for (int passNum= 0; passNum < numPasses; passNum++) { - dst << newCurrentBlock->ostr.str(); - - for (auto child : newCurrentBlock->blocks) { - generateTree(dst, child, vars); - } - } - - return _numErrors; -} - -int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& block, Vars& vars, BlockPointer& branch) { - switch (block->command.type) { - case Command::BLOCK: { - branch = block; - return 1; - } - break; - case Command::VAR: { - Vars::iterator it = vars.find(block->command.arguments.front()); - if (it != vars.end()) { - dst << (*it).second; - } else { - BlockPointer funcBlock = _config->_funcs.findFunc(block->command.arguments.front().c_str()); - if (funcBlock) { - // before diving in the func tree, let's modify the vars with the local defs: - int nbParams = (int)std::min(block->command.arguments.size(), - funcBlock->command.arguments.size()); - std::vector< String > paramCache; - paramCache.push_back(""); - String val; - for (int i = 1; i < nbParams; i++) { - val = block->command.arguments[i]; - if ((val[0] == Tag::VAR) && (val[val.length()-1] == Tag::VAR)) { - val = val.substr(1, val.length()-2); - Vars::iterator it = vars.find(val); - if (it != vars.end()) { - val = (*it).second; - } else { - val = Tag::NULL_VAR; - } - } - - Vars::iterator it = vars.find(funcBlock->command.arguments[i]); - if (it != vars.end()) { - paramCache.push_back((*it).second); - (*it).second = val; - } else { - if (val != Tag::NULL_VAR) { - vars.insert(Vars::value_type(funcBlock->command.arguments[i], val)); - } - - paramCache.push_back(Tag::NULL_VAR); - } - } - - generateTree(dst, funcBlock, vars); - - for (int i = 1; i < nbParams; i++) { - if (paramCache[i] == Tag::NULL_VAR) { - vars.erase(funcBlock->command.arguments[i]); - } else { - vars[funcBlock->command.arguments[i]] = paramCache[i]; - } - } - } - } - branch = block; - return 1; - } - break; - case Command::IFBLOCK: { - // ok, go through the branches and pick the first one that goes - for (auto child: block->blocks) { - int numPasses = evalBlockGeneration(dst, child, vars, branch); - if (numPasses > 0) { - return numPasses; - } - } - } - break; - case Command::IF: - case Command::ELIF: { - if (!block->command.arguments.empty()) { - // Just one argument means check for the var beeing defined - if (block->command.arguments.size() == 1) { - Vars::iterator it = vars.find(block->command.arguments.front()); - if (it != vars.end()) { - branch = block; - return 1; - } - } else if (block->command.arguments.size() == 2) { - if (block->command.arguments[0].compare("not") == 0) { - Vars::iterator it = vars.find(block->command.arguments[1]); - if (it == vars.end()) { - branch = block; - return 1; - } - } - } else if (block->command.arguments.size() == 3) { - if (block->command.arguments[1].compare("and") == 0) { - Vars::iterator itL = vars.find(block->command.arguments[0]); - Vars::iterator itR = vars.find(block->command.arguments[2]); - if ((itL != vars.end()) && (itR != vars.end())) { - branch = block; - return 1; - } - } else if (block->command.arguments[1].compare("or") == 0) { - Vars::iterator itL = vars.find(block->command.arguments[0]); - Vars::iterator itR = vars.find(block->command.arguments[2]); - if ((itL != vars.end()) || (itR != vars.end())) { - branch = block; - return 1; - } - } else if (block->command.arguments[1].compare("==") == 0) { - Vars::iterator itL = vars.find(block->command.arguments[0]); - if (itL != vars.end()) { - if ((*itL).second.compare(block->command.arguments[2]) == 0) { - branch = block; - return 1; - } - } - } - } - - } - return 0; - } - break; - case Command::ELSE: { - branch = block; - return 1; - } - break; - case Command::ENDIF: { - branch = block; - return 1; - } - break; - case Command::DEF: { - if (block->command.arguments.size()) { - // THe actual value of the var defined sneeds to be evaluated: - String val; - for (unsigned int t = 1; t < block->command.arguments.size(); t++) { - // detect if a param is a var - auto len = block->command.arguments[t].length(); - if ((block->command.arguments[t][0] == Tag::VAR) - && (block->command.arguments[t][len - 1] == Tag::VAR)) { - String var = block->command.arguments[t].substr(1, len - 2); - Vars::iterator it = vars.find(var); - if (it != vars.end()) { - val += (*it).second; - } - } else { - val += block->command.arguments[t]; - } - } - - Vars::iterator it = vars.find(block->command.arguments.front()); - if (it == vars.end()) { - vars.insert(Vars::value_type(block->command.arguments.front(), val)); - } else { - (*it).second = val; - } - - branch = block; - return 1; - } else { - branch = block; - return 0; - } - } - break; - - case Command::INCLUDE: { - TextTemplatePointer include = _config->findInclude(block->command.arguments.front().c_str()); - if (include && !include->_root->blocks.empty()) { - if (include->_root) { - generateTree(dst, include->_root, vars); - } - } - - branch = block; - return 1; - } - break; - - case Command::FUNC: { - branch = block; - return 1; - } - break; - - case Command::ENDFUNC: { - branch = block; - return 1; - } - break; - - default: { - } - } - - return 0; -} - - -int TextTemplate::parse(std::istream& src) { - _root->command.type = Command::BLOCK; - BlockPointer currentBlock = _root; - _numErrors = 0; - - // First Parse the input file - int nbLoops = 0; - bool goOn = true; - Tag::Type tagType = Tag::INVALID; - Tag::Type nextTagType = Tag::INVALID; - while (goOn) { - std::string data; - std::string tag; - if (stepForward(&src, data, tag, tagType, nextTagType)) { - currentBlock = processStep(currentBlock, data, tag, tagType); - } else { - goOn = false; - } - - tagType = nextTagType; - nbLoops++; - } - - return _numErrors; -} - -int TextTemplate::generate(std::ostream& dst, Vars& vars) { - return generateTree(dst, _root, vars); -} - -void TextTemplate::displayTree(std::ostream& dst, int& level) const { - Block::displayTree(_root, dst, level); -} - -void TextTemplate::Block::displayTree(const BlockPointer& block, std::ostream& dst, int& level) { - String tab(level * 2, ' '); - - const String BLOCK_TYPE_NAMES[] = { - "VAR", - "BLOCK", - "FUNC", - "ENDFUNC", - "IFBLOCK", - "IF", - "ELIF", - "ELSE", - "ENDIF", - "FOR", - "ENDFOR", - "INCLUDE", - "DEF" - }; - - dst << tab << "{ " << BLOCK_TYPE_NAMES[block->command.type] << ":"; - if (!block->command.arguments.empty()) { - for (auto arg: block->command.arguments) { - dst << " " << arg; - } - } - dst << std::endl; - - level++; - for (auto sub: block->blocks) { - displayTree(sub, dst, level); - } - level--; - - dst << tab << "}" << std::endl; -} - -void TextTemplate::Config::displayTree(std::ostream& dst, int& level) const { - String tab(level * 2, ' '); - - level++; - dst << tab << "Includes:" << std::endl; - for (auto inc: _includes) { - dst << tab << tab << inc.first << std::endl; - inc.second->displayTree(dst, level); - } - dst << tab << "Funcs:" << std::endl; - for (auto func: _funcs._funcs) { - dst << tab << tab << func.first << std::endl; - TextTemplate::Block::displayTree( func.second, dst, level); - } - level--; -} diff --git a/tools/scribe/src/TextTemplate.h b/tools/scribe/src/TextTemplate.h deleted file mode 100755 index 44edc23c12..0000000000 --- a/tools/scribe/src/TextTemplate.h +++ /dev/null @@ -1,206 +0,0 @@ - -// -// TextTemplate.cpp -// tools/shaderScribe/src -// -// Created by Sam Gateau on 12/15/2014. -// Copyright 2013 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_TEXT_TEMPLATE_H -#define hifi_TEXT_TEMPLATE_H - -#include -#include -#include -#include -#include -#include -#include - -class TextTemplate { -public: - typedef std::shared_ptr< TextTemplate > Pointer; - typedef std::string String; - typedef std::vector< String > StringVector; - typedef std::map< String, String > Vars; - typedef std::map< String, TextTemplate::Pointer > Includes; - - class Tag { - public: - enum Type { - VARIABLE = 0, - COMMAND, - REMARK, - INVALID = -1, - }; - - static const char BEGIN = '<'; - static const char END = '>'; - - static const char VAR = '$'; - static const char COM = '@'; - static const char REM = '!'; - - static const std::string NULL_VAR; - }; - - class Command { - public: - typedef std::vector< Command > vector; - - enum Type { - VAR = 0, - BLOCK, - FUNC, - ENDFUNC, - IFBLOCK, - IF, - ELIF, - ELSE, - ENDIF, - FOR, - ENDFOR, - INCLUDE, - DEF, - }; - - Type type; - std::vector< String > arguments; - - bool isBlockEnd() { - switch (type) { - case ENDFUNC: - case ENDIF: - case ENDFOR: - case INCLUDE: - case DEF: - case VAR: - return true; - default: - return false; - } - } - }; - - class Block { - public: - typedef std::shared_ptr Pointer; - typedef std::vector< Block::Pointer > Vector; - - Block::Pointer parent; - Command command; - Vector blocks; - std::stringstream ostr; - - String sourceName; - - Block(const String& sourceFilename) : - sourceName(sourceFilename) {} - - static void addNewBlock(const Block::Pointer& parent, const Block::Pointer& block); - static const Block::Pointer& getCurrentBlock(const Block::Pointer& block); - - static void displayTree(const Block::Pointer& block, std::ostream& dst, int& level); - }; - - class Funcs { - public: - typedef std::map< String, Block::Pointer > map; - - Funcs(); - ~Funcs(); - - const Block::Pointer findFunc(const char* func); - const Block::Pointer addFunc(const char* func, const Block::Pointer& root); - - map _funcs; - protected: - }; - - class Config { - public: - typedef std::shared_ptr< Config > Pointer; - typedef bool (*IncluderCallback) (const Config::Pointer& config, const char* filename, String& source); - - Includes _includes; - Funcs _funcs; - std::ostream* _logStream; - int _numErrors; - IncluderCallback _includerCallback; - StringVector _paths; - - Config(); - - static const TextTemplate::Pointer addInclude(const Config::Pointer& config, const char* include); - const TextTemplate::Pointer findInclude(const char* include); - - void addIncludePath(const char* path); - - void displayTree(std::ostream& dst, int& level) const; - }; - - static bool loadFile(const Config::Pointer& config, const char* filename, String& source); - - TextTemplate(const String& name, const Config::Pointer& config = std::make_shared()); - ~TextTemplate(); - - // Scibe does all the job of parsing an inout template stream and then gneerating theresulting stream using the vars - int scribe(std::ostream& dst, std::istream& src, Vars& vars); - - int parse(std::istream& src); - int generate(std::ostream& dst, Vars& vars); - - const Config::Pointer config() { return _config; } - - void displayTree(std::ostream& dst, int& level) const; - -protected: - Config::Pointer _config; - Block::Pointer _root; - int _numErrors; - bool _steppingStarted; - - bool grabUntilBeginTag(std::istream* str, String& grabbed, Tag::Type& tagType); - bool grabUntilEndTag(std::istream* str, String& grabbed, Tag::Type& tagType); - - bool stepForward(std::istream* str, String& grabbed, String& tag, Tag::Type& tagType, Tag::Type& nextTagType); - - bool grabFirstToken(String& src, String& token, String& reminder); - bool convertExpressionToArguments(String& src, std::vector< String >& arguments); - bool convertExpressionToDefArguments(String& src, std::vector< String >& arguments); - bool convertExpressionToFuncArguments(String& src, std::vector< String >& arguments); - - // Filter between var, command or comments - const Block::Pointer processStep(const Block::Pointer& block, String& grabbed, String& tag, Tag::Type& tagType); - const Block::Pointer processStepVar(const Block::Pointer& block, String& grabbed, String& tag); - const Block::Pointer processStepCommand(const Block::Pointer& block, String& grabbed, String& tag); - const Block::Pointer processStepRemark(const Block::Pointer& block, String& grabbed, String& tag); - - // Define command - const Block::Pointer processStepDef(const Block::Pointer& block, String& grabbed, String& tag); - - // If commands - const Block::Pointer processStepCommandIf(const Block::Pointer& block, String& grabbed, String& expression); - const Block::Pointer processStepCommandEndIf(const Block::Pointer& block, String& grabbed, String& expression); - const Block::Pointer processStepCommandElif(const Block::Pointer& block, String& grabbed, String& expression); - const Block::Pointer processStepCommandElse(const Block::Pointer& block, String& grabbed, String& expression); - - // Include command - const Block::Pointer processStepInclude(const Block::Pointer& block, String& grabbed, String& tag); - - // Function command - const Block::Pointer processStepFunc(const Block::Pointer& block, String& grabbed, String& tag); - const Block::Pointer processStepEndFunc(const Block::Pointer& block, String& grabbed, String& tag); - - // Generation - int generateTree(std::ostream& dst, const Block::Pointer& block, Vars& vars); - int evalBlockGeneration(std::ostream& dst, const Block::Pointer& block, Vars& vars, Block::Pointer& branch); - - // Errors - std::ostream& log() { return (* _config->_logStream); } - void logError(const Block::Pointer& block, const char* error, ...); -}; - -#endif diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp deleted file mode 100755 index c8c540c362..0000000000 --- a/tools/scribe/src/main.cpp +++ /dev/null @@ -1,352 +0,0 @@ -// -// main.cpp -// tools/shaderScribe/src -// -// Created by Sam Gateau on 12/15/2014. -// Copyright 2013 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 "TextTemplate.h" - -#include -#include -#include -#include - -using namespace std; - -int main (int argc, char** argv) { - // process the command line arguments - std::vector< std::string > inputs; - - std::string srcFilename; - std::string destFilename; - std::string targetName; - TextTemplate::Vars vars; - - std::string lastVarName; - bool listVars = false; - bool showParseTree = false; - bool makeCPlusPlus = false; - - auto config = std::make_shared(); - - enum Mode { - READY = 0, - GRAB_OUTPUT, - GRAB_VAR_NAME, - GRAB_VAR_VALUE, - GRAB_INCLUDE_PATH, - GRAB_TARGET_NAME, - GRAB_SHADER_TYPE, - EXIT, - } mode = READY; - - enum Type { - VERTEX = 0, - FRAGMENT, - GEOMETRY, - } type = VERTEX; - static const char* shaderTypeString[] = { - "VERTEX", "PIXEL", "GEOMETRY" - }; - static const char* shaderCreateString[] = { - "Vertex", "Pixel", "Geometry" - }; - std::string shaderStage{ "vert" }; - - for (int ii = 1; (mode != EXIT) && (ii < argc); ii++) { - inputs.push_back(argv[ii]); - - switch (mode) { - case READY: { - if (inputs.back() == "-o") { - mode = GRAB_OUTPUT; - } else if (inputs.back() == "-t") { - mode = GRAB_TARGET_NAME; - } else if (inputs.back() == "-D") { - mode = GRAB_VAR_NAME; - } else if (inputs.back() == "-I") { - mode = GRAB_INCLUDE_PATH; - } else if (inputs.back() == "-listVars") { - listVars = true; - mode = READY; - } else if (inputs.back() == "-showParseTree") { - showParseTree = true; - mode = READY; - } else if (inputs.back() == "-c++") { - makeCPlusPlus = true; - mode = READY; - } else if (inputs.back() == "-T") { - mode = GRAB_SHADER_TYPE; - } else { - // just grabbed the source filename, stop parameter parsing - srcFilename = inputs.back(); - mode = EXIT; - } - } - break; - - case GRAB_OUTPUT: { - destFilename = inputs.back(); - mode = READY; - } - break; - - case GRAB_TARGET_NAME: { - targetName = inputs.back(); - mode = READY; - } - break; - - case GRAB_VAR_NAME: { - // grab first the name of the var - lastVarName = inputs.back(); - mode = GRAB_VAR_VALUE; - } - break; - case GRAB_VAR_VALUE: { - // and then the value - vars.insert(TextTemplate::Vars::value_type(lastVarName, inputs.back())); - - mode = READY; - } - break; - - case GRAB_INCLUDE_PATH: { - config->addIncludePath(inputs.back().c_str()); - mode = READY; - } - break; - - case GRAB_SHADER_TYPE: - { - if (inputs.back() == "frag") { - shaderStage = inputs.back(); - type = FRAGMENT; - } else if (inputs.back() == "geom") { - shaderStage = inputs.back(); - type = GEOMETRY; - } else if (inputs.back() == "vert") { - shaderStage = inputs.back(); - type = VERTEX; - } else { - cerr << "Unrecognized shader type. Supported is vert, frag or geom" << endl; - } - mode = READY; - } - break; - - case EXIT: { - // THis shouldn't happen - } - break; - } - } - - if (srcFilename.empty()) { - cerr << "Usage: shaderScribe [OPTION]... inputFilename" << endl; - cerr << "Where options include:" << endl; - cerr << " -o filename: Send output to filename rather than standard output." << endl; - cerr << " -t targetName: Set the targetName used, if not defined use the output filename 'name' and if not defined use the inputFilename 'name'" << endl; - cerr << " -I include_directory: Declare a directory to be added to the includes search pool." << endl; - cerr << " -D varname varvalue: Declare a var used to generate the output file." << endl; - cerr << " varname and varvalue must be made of alpha numerical characters with no spaces." << endl; - cerr << " -listVars : Will list the vars name and value in the standard output." << endl; - cerr << " -showParseTree : Draw the tree obtained while parsing the source" << endl; - cerr << " -c++ : Generate a c++ source file containing the output file stream stored as a char[] variable" << endl; - cerr << " -T vert/frag/geom : define the type of the shader. Defaults to VERTEX if not specified." << endl; - cerr << " This is necessary if the -c++ option is used." << endl; - return 0; - } - - // Define targetName: if none, get destFilename, if none get srcFilename - if (targetName.empty()) { - if (destFilename.empty()) { - targetName = srcFilename; - } else { - targetName = destFilename; - } - } - // no clean it to have just a descent c var name - if (!targetName.empty()) { - // trim anything before '/' or '\' - targetName = targetName.substr(targetName.find_last_of('/') + 1); - targetName = targetName.substr(targetName.find_last_of('\\') + 1); - - // trim anything after '.' - targetName = targetName.substr(0, targetName.find_first_of('.')); - } - - // Add built in vars - time_t endTime = chrono::system_clock::to_time_t(chrono::system_clock::now()); - std::string endTimStr(ctime(&endTime)); - vars["_SCRIBE_DATE"] = endTimStr.substr(0, endTimStr.length() - 1); - - // List vars? - if (listVars) { - cerr << "Vars:" << endl; - for (auto v : vars) { - cerr << " " << v.first << " = " << v.second << endl; - } - } - - // Open up source - std::fstream srcStream; - srcStream.open(srcFilename, std::fstream::in); - if (!srcStream.is_open()) { - cerr << "Failed to open source file <" << srcFilename << ">" << endl; - return 1; - } - - auto scribe = std::make_shared(srcFilename, config); - - // ready to parse and generate - std::stringstream destStringStream; - int numErrors = scribe->scribe(destStringStream, srcStream, vars); - if (numErrors) { - cerr << "Scribe " << srcFilename << "> failed: " << numErrors << " errors." << endl; - return 1; - }; - - - if (showParseTree) { - int level = 1; - cerr << "Config trees:" << std::endl; - config->displayTree(cerr, level); - - cerr << srcFilename << " tree:" << std::endl; - scribe->displayTree(cerr, level); - } - - if (makeCPlusPlus) { - // Because there is a maximum size for literal strings declared in source we need to partition the - // full source string stream into pages that seems to be around that value... - const int MAX_STRING_LITERAL = 10000; - std::string lineToken; - auto pageSize = lineToken.length(); - std::vector> pages(1, std::make_shared()); - while (!destStringStream.eof()) { - std::getline(destStringStream, lineToken); - auto lineSize = lineToken.length() + 1; - - if (pageSize + lineSize > MAX_STRING_LITERAL) { - pages.push_back(std::make_shared()); - // reset pageStringStream - pageSize = 0; - } - - (*pages.back()) << lineToken << std::endl; - pageSize += lineSize; - } - - std::stringstream headerStringStream; - std::stringstream sourceStringStream; - std::string headerFileName = destFilename + ".h"; - std::string sourceFileName = destFilename + ".cpp"; - - sourceStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; - - // Write header file - headerStringStream << "#ifndef " << targetName << "_h" << std::endl; - headerStringStream << "#define " << targetName << "_h\n" << std::endl; - headerStringStream << "#include \n" << std::endl; - headerStringStream << "class " << targetName << " {" << std::endl; - headerStringStream << "public:" << std::endl; - headerStringStream << "\tstatic gpu::Shader::Type getType() { return gpu::Shader::" << shaderTypeString[type] << "; }" << std::endl; - headerStringStream << "\tstatic const std::string& getSource() { return _source; }" << std::endl; - headerStringStream << "\tstatic gpu::ShaderPointer getShader();" << std::endl; - headerStringStream << "private:" << std::endl; - headerStringStream << "\tstatic const std::string _source;" << std::endl; - headerStringStream << "\tstatic gpu::ShaderPointer _shader;" << std::endl; - headerStringStream << "};\n" << std::endl; - headerStringStream << "#endif // " << targetName << "_h" << std::endl; - - bool mustOutputHeader = destFilename.empty(); - // Compare with existing file - { - std::fstream headerFile; - headerFile.open(headerFileName, std::fstream::in); - if (headerFile.is_open()) { - // Skip first line - std::string line; - std::stringstream previousHeaderStringStream; - std::getline(headerFile, line); - - previousHeaderStringStream << headerFile.rdbuf(); - mustOutputHeader = mustOutputHeader || previousHeaderStringStream.str() != headerStringStream.str(); - } else { - mustOutputHeader = true; - } - } - - if (mustOutputHeader) { - if (!destFilename.empty()) { - // File content has changed so write it - std::fstream headerFile; - headerFile.open(headerFileName, std::fstream::out); - if (headerFile.is_open()) { - // First line contains the date of modification - headerFile << sourceStringStream.str(); - headerFile << headerStringStream.str(); - } else { - cerr << "Scribe output file <" << headerFileName << "> failed to open." << endl; - return 1; - } - } else { - cerr << sourceStringStream.str(); - cerr << headerStringStream.str(); - } - } - - // Write source file - sourceStringStream << "#include \"" << targetName << ".h\"\n" << std::endl; - sourceStringStream << "gpu::ShaderPointer " << targetName << "::_shader;" << std::endl; - sourceStringStream << "const std::string " << targetName << "::_source = std::string()"; - // Write the pages content - for (auto page : pages) { - sourceStringStream << "+ std::string(R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\")\n"; - } - sourceStringStream << ";\n" << std::endl << std::endl; - - sourceStringStream << "gpu::ShaderPointer " << targetName << "::getShader() {" << std::endl; - sourceStringStream << "\tif (_shader==nullptr) {" << std::endl; - sourceStringStream << "\t\t_shader = gpu::Shader::create" << shaderCreateString[type] << "(std::string(_source));" << std::endl; - sourceStringStream << "\t}" << std::endl; - sourceStringStream << "\treturn _shader;" << std::endl; - sourceStringStream << "}\n" << std::endl; - - // Destination stream - if (!destFilename.empty()) { - std::fstream sourceFile; - sourceFile.open(sourceFileName, std::fstream::out); - if (!sourceFile.is_open()) { - cerr << "Scribe output file <" << sourceFileName << "> failed to open." << endl; - return 1; - } - sourceFile << sourceStringStream.str(); - } else { - cerr << sourceStringStream.str(); - } - } else { - // Destination stream - if (!destFilename.empty()) { - std::fstream destFileStream; - destFileStream.open(destFilename, std::fstream::out); - if (!destFileStream.is_open()) { - cerr << "Scribe output file <" << destFilename << "> failed to open." << endl; - return 1; - } - - destFileStream << destStringStream.str(); - } else { - cerr << destStringStream.str(); - } - } - - return 0; - -} diff --git a/tools/shadergen.py b/tools/shadergen.py new file mode 100644 index 0000000000..ffbe1662ec --- /dev/null +++ b/tools/shadergen.py @@ -0,0 +1,251 @@ +import re +import subprocess +import sys +import time +import os +import json +import argparse +import concurrent +from os.path import expanduser +from concurrent.futures import ThreadPoolExecutor +from argparse import ArgumentParser +from pathlib import Path +from threading import Lock + + # # Target dependant Custom rule on the SHADER_FILE + # if (ANDROID) + # set(GLPROFILE LINUX_GL) + # else() + # if (APPLE) + # set(GLPROFILE MAC_GL) + # elseif(UNIX) + # set(GLPROFILE LINUX_GL) + # else() + # set(GLPROFILE PC_GL) + # endif() + # endif() + +def getTypeForScribeFile(scribefilename): + last = scribefilename.rfind('.') + extension = scribefilename[last:] + switcher = { + '.slv': 'vert', + '.slf': 'frag', + '.slg': 'geom', + '.slc': 'comp', + } + if not extension in switcher: + raise ValueError("Unknown scribe file type for " + scribefilename) + return switcher.get(extension) + +def getCommonScribeArgs(scribefile, includeLibs): + scribeArgs = [os.path.join(args.tools_dir, 'scribe')] + # FIXME use the sys.platform to set the correct value + scribeArgs.extend(['-D', 'GLPROFILE', 'PC_GL']) + scribeArgs.extend(['-T', getTypeForScribeFile(scribefile)]) + for lib in includeLibs: + scribeArgs.extend(['-I', args.source_dir + '/libraries/' + lib + '/src/' + lib + '/']) + scribeArgs.extend(['-I', args.source_dir + '/libraries/' + lib + '/src/']) + scribeArgs.append(scribefile) + return scribeArgs + +def getDialectAndVariantHeaders(dialect, variant): + headerPath = args.source_dir + '/libraries/shaders/headers/' + variantHeader = headerPath + ('stereo.glsl' if (variant == 'stereo') else 'mono.glsl') + dialectHeader = headerPath + dialect + '/header.glsl' + return [dialectHeader, variantHeader] + +class ScribeDependenciesCache: + cache = {} + lock = Lock() + filename = '' + + def __init__(self, filename): + self.filename = filename + + def load(self): + jsonstr = '{}' + if (os.path.exists(self.filename)): + with open(self.filename) as f: + jsonstr = f.read() + self.cache = json.loads(jsonstr) + + def save(self): + with open(self.filename, "w") as f: + f.write(json.dumps(self.cache)) + + def get(self, scribefile, dialect, variant): + self.lock.acquire() + key = self.key(scribefile, dialect, variant) + try: + if key in self.cache: + return self.cache[key].copy() + finally: + self.lock.release() + return None + + def key(self, scribeFile, dialect, variant): + return ':'.join([scribeFile, dialect, variant]) + + def getOrGen(self, scribefile, includeLibs, dialect, variant): + result = self.get(scribefile, dialect, variant) + if (None == result): + result = self.gen(scribefile, includeLibs, dialect, variant) + return result + + def gen(self, scribefile, includeLibs, dialect, variant): + scribeArgs = getCommonScribeArgs(scribefile, includeLibs) + scribeArgs.extend(['-M']) + processResult = subprocess.run(scribeArgs, stdout=subprocess.PIPE) + if (0 != processResult.returncode): + raise RuntimeError("Unable to parse scribe dependencies") + result = processResult.stdout.decode("utf-8").splitlines(False) + result.append(scribefile) + result.extend(getDialectAndVariantHeaders(dialect, variant)) + key = self.key(scribefile, dialect, variant) + self.lock.acquire() + self.cache[key] = result.copy() + self.lock.release() + return result + +def getFileTimes(files): + if isinstance(files, str): + files = [files] + return list(map(lambda f: os.path.getmtime(f) if os.path.isfile(f) else -1, files)) + +def outOfDate(inputs, output): + oldestInput = max(getFileTimes(inputs)) + youngestOutput = min(getFileTimes(output)) + diff = youngestOutput - oldestInput + return oldestInput >= youngestOutput + +def executeSubprocess(processArgs): + processResult = subprocess.run(processArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if (0 != processResult.returncode): + raise RuntimeError('Call to "{}" failed.\n\narguments:\n{}\n\nstdout:\n{}\n\nstderr:\n{}'.format( + processArgs[0], + ' '.join(processArgs[1:]), + processResult.stdout.decode('utf-8'), + processResult.stderr.decode('utf-8'))) + +folderMutex = Lock() + +def processCommand(line): + global args + global scribeDepCache + glslangExec = args.tools_dir + '/glslangValidator' + spirvCrossExec = args.tools_dir + '/spirv-cross' + spirvOptExec = args.tools_dir + '/spirv-opt' + params = line.split(';') + dialect = params.pop(0) + variant = params.pop(0) + scribeFile = args.source_dir + '/' + params.pop(0) + unoptGlslFile = args.source_dir + '/' + params.pop(0) + libs = params + + upoptSpirvFile = unoptGlslFile + '.spv' + spirvFile = unoptGlslFile + '.opt.spv' + reflectionFile = unoptGlslFile + '.json' + glslFile = unoptGlslFile + '.glsl' + outputFiles = [unoptGlslFile, spirvFile, reflectionFile, glslFile] + + scribeOutputDir = os.path.abspath(os.path.join(unoptGlslFile, os.pardir)) + + # Serialize checking and creation of the output directory to avoid occasional + # crashes + global folderMutex + folderMutex.acquire() + if not os.path.exists(scribeOutputDir): + os.makedirs(scribeOutputDir) + folderMutex.release() + + scribeDeps = scribeDepCache.getOrGen(scribeFile, libs, dialect, variant) + + # if the scribe sources (slv, slf, slh, etc), or the dialect/ variant headers are out of date + # regenerate the scribe GLSL output + if args.force or outOfDate(scribeDeps, outputFiles): + print('Processing file {} dialect {} variant {}'.format(scribeFile, dialect, variant)) + if args.dry_run: + return True + + scribeDepCache.gen(scribeFile, libs, dialect, variant) + scribeArgs = getCommonScribeArgs(scribeFile, libs) + for header in getDialectAndVariantHeaders(dialect, variant): + scribeArgs.extend(['-H', header]) + scribeArgs.extend(['-o', unoptGlslFile]) + executeSubprocess(scribeArgs) + + # Generate the un-optimized output + executeSubprocess([glslangExec, '-V110', '-o', upoptSpirvFile, unoptGlslFile]) + + # Optimize the SPIRV + executeSubprocess([spirvOptExec, '-O', '-o', spirvFile, upoptSpirvFile]) + + # Generation JSON reflection + executeSubprocess([spirvCrossExec, '--reflect', 'json', '--output', reflectionFile, spirvFile]) + + # Generate the optimized GLSL output + spirvCrossDialect = dialect + # 310es causes spirv-cross to inject "#extension GL_OES_texture_buffer : require" into the output + if (dialect == '310es'): spirvCrossDialect = '320es' + spirvCrossArgs = [spirvCrossExec, '--output', glslFile, spirvFile, '--version', spirvCrossDialect] + if (dialect == '410'): spirvCrossArgs.append('--no-420pack-extension') + executeSubprocess(spirvCrossArgs) + else: + # This logic is necessary because cmake will agressively keep re-executing the shadergen + # code otherwise + Path(unoptGlslFile).touch() + Path(upoptSpirvFile).touch() + Path(spirvFile).touch() + Path(glslFile).touch() + Path(reflectionFile).touch() + return True + + + +def main(): + commands = args.commands.read().splitlines(False) + if args.debug: + for command in commands: + processCommand(command) + else: + workers = max(1, os.cpu_count() - 2) + with ThreadPoolExecutor(max_workers=workers) as executor: + for result in executor.map(processCommand, commands): + if not result: + raise RuntimeError("Failed to execute all subprocesses") + executor.shutdown() + + +parser = ArgumentParser(description='Generate shader artifacts.') +parser.add_argument('--commands', type=argparse.FileType('r'), help='list of commands to execute') +parser.add_argument('--tools-dir', type=str, help='location of the host compatible binaries') +parser.add_argument('--build-dir', type=str, help='The build directory base path') +parser.add_argument('--source-dir', type=str, help='The root directory of the git repository') +parser.add_argument('--debug', action='store_true') +parser.add_argument('--force', action='store_true', help='Ignore timestamps and force regeneration of all files') +parser.add_argument('--dry-run', action='store_true', help='Report the files that would be process, but do not output') + +args = None +if len(sys.argv) == 1: + # for debugging + sourceDir = expanduser('~/git/hifi') + toolsDir = os.path.join(expanduser('~/git/vcpkg'), 'installed', 'x64-windows', 'tools') + buildPath = sourceDir + '/build' + commandsPath = buildPath + '/libraries/shaders/shadergen.txt' + shaderDir = buildPath + '/libraries/shaders' + testArgs = '--commands {} --tools-dir {} --build-dir {} --source-dir {}'.format( + commandsPath, toolsDir, shaderDir, sourceDir + ).split() + testArgs.append('--debug') + testArgs.append('--force') + #testArgs.append('--dry-run') + args = parser.parse_args(testArgs) +else: + args = parser.parse_args() + +scribeDepCache = ScribeDependenciesCache(args.build_dir + '/shaderDeps.json') +scribeDepCache.load() +main() +scribeDepCache.save() + diff --git a/tools/shreflect/CMakeLists.txt b/tools/shreflect/CMakeLists.txt deleted file mode 100644 index 0748f59d31..0000000000 --- a/tools/shreflect/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(TARGET_NAME shreflect) - -# don't use the setup_hifi_project macro as we don't want Qt or GLM dependencies -file(GLOB TARGET_SRCS src/*) -add_executable(${TARGET_NAME} ${TARGET_SRCS}) -target_json() - -if (WIN32) - set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF") -endif() diff --git a/tools/shreflect/src/main.cpp b/tools/shreflect/src/main.cpp deleted file mode 100644 index e13f937102..0000000000 --- a/tools/shreflect/src/main.cpp +++ /dev/null @@ -1,204 +0,0 @@ -// -// Bradley Austin Davis on 2018/05/24 -// Copyright 2013 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using json = nlohmann::json; - -std::vector splitStringIntoLines(const std::string& s) { - std::stringstream ss(s); - std::vector result; - std::string line; - while (std::getline(ss, line, '\n')) { - result.push_back(line); - } - return result; -} - -std::string readFile(const std::string& file) { - std::ifstream t(file); - std::string str((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - return str; -} - -void writeFile(const std::string& file, const std::string& out) { - std::ofstream t(file, std::ios::trunc); - t << out; - t.close(); -} - -// Convert a Perl style multi-line commented regex into a C++ style regex -// All whitespace will be removed and lines with '#' comments will have the comments removed -std::string getUnformattedRegex(const std::string& formatted) { - static const std::regex WHITESPACE = std::regex("\\s+"); - static const std::string EMPTY; - std::string result; - result.reserve(formatted.size()); - auto lines = splitStringIntoLines(formatted); - for (auto line : lines) { - auto commentStart = line.find('#'); - if (std::string::npos != commentStart) { - line = line.substr(0, commentStart); - } - line = std::regex_replace(line, WHITESPACE, EMPTY); - result += line; - } - - return result; -} - -static std::string LAYOUT_REGEX_STRING{ R"REGEX( -^layout\( # BEGIN layout declaration block - (\s*std140\s*,\s*)? # Optional std140 marker - (binding|location) # binding / location - \s*=\s* - (?: - (\b\d+\b) # literal numeric binding like binding=1 - | - (\b[A-Z_0-9]+\b) # Preprocessor macro binding like binding=GPU_TEXTURE_FOO - ) -\)\s* # END layout declaration block -(?: - ( # Texture or simple uniform like `layout(binding=0) uniform sampler2D originalTexture;` - uniform\s+ - (\b\w+\b)\s+ - (\b\w+\b)\s* - (?:\[\d*\])? - (?:\s*=.*)? - \s*;.*$ - ) - | - ( # UBO or SSBO like `layout(std140, binding=GPU_STORAGE_TRANSFORM_OBJECT) buffer transformObjectBuffer {` - \b(uniform|buffer)\b\s+ - \b(\w+\b) - \s*\{.*$ - ) - | - ( # Input or output attribute like `layout(location=GPU_ATTR_POSITION) in vec4 inPosition;` - \b(in|out)\b\s+ - \b(\w+)\b\s+ - \b(\w+)\b\s*;\s*$ - ) -) -)REGEX" }; - -enum Groups { - STD140 = 1, - LOCATION_TYPE = 2, - LOCATION_LITERAL = 3, - LOCATION_DEFINE = 4, - DECL_SIMPLE = 5, - SIMPLE_TYPE = 6, - SIMPLE_NAME = 7, - DECL_STRUCT = 8, - STRUCT_TYPE = 9, - STRUCT_NAME = 10, - DECL_INOUT = 11, - INOUT_DIRECTION = 12, - INOUT_TYPE = 13, - INOUT_NAME = 14, -}; - -json reflectShader(const std::string& shaderPath) { - static const std::regex DEFINE("^#define\\s+([_A-Z0-9]+)\\s+(\\d+)\\s*$"); - static const std::regex LAYOUT_QUALIFIER{ getUnformattedRegex(LAYOUT_REGEX_STRING) }; - - - auto shaderSource = readFile(shaderPath); - std::vector lines = splitStringIntoLines(shaderSource); - using Map = std::unordered_map; - - json inputs; - json outputs; - json textures; - json textureTypes; - json uniforms; - json storageBuffers; - json uniformBuffers; - Map locationDefines; - for (const auto& line : lines) { - std::cmatch m; - if (std::regex_match(line.c_str(), m, DEFINE)) { - locationDefines[m[1].str()] = std::stoi(m[2].first); - } else if (std::regex_match(line.c_str(), m, LAYOUT_QUALIFIER)) { - int binding = -1; - if (m[LOCATION_LITERAL].matched) { - binding = std::stoi(m[LOCATION_LITERAL].str()); - } else { - binding = locationDefines[m[LOCATION_DEFINE].str()]; - } - if (m[DECL_SIMPLE].matched) { - auto name = m[SIMPLE_NAME].str(); - auto type = m[SIMPLE_TYPE].str(); - bool isTexture = 0 == type.find("sampler"); - auto& map = isTexture ? textures : uniforms; - map[name] = binding; - if (isTexture) { - textureTypes[name] = type; - } - } else if (m[DECL_STRUCT].matched) { - auto name = m[STRUCT_NAME].str(); - auto type = m[STRUCT_TYPE].str(); - auto& map = (type == "buffer") ? storageBuffers : uniformBuffers; - map[name] = binding; - } else if (m[DECL_INOUT].matched) { - auto name = m[INOUT_NAME].str(); - auto& map = (m[INOUT_DIRECTION].str() == "in") ? inputs : outputs; - map[name] = binding; - } - } - } - - json result; - if (!inputs.empty()) { - result["inputs"] = inputs; - } - if (!outputs.empty()) { - result["outputs"] = outputs; - } - if (!textures.empty()) { - result["textures"] = textures; - } - if (!textureTypes.empty()) { - result["texturesTypes"] = textureTypes; - } - if (!uniforms.empty()) { - result["uniforms"] = uniforms; - } - if (!storageBuffers.empty()) { - result["storageBuffers"] = storageBuffers; - } - if (!uniformBuffers.empty()) { - result["uniformBuffers"] = uniformBuffers; - } - - result["name"] = shaderPath; - - return result; -} - -int main (int argc, char** argv) { - auto path = std::string(argv[1]); - auto shaderReflection = reflectShader(path); - writeFile(path + ".json", shaderReflection.dump(4)); - return 0; -} diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index e9d8243e38..10b13aef36 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,8 +54,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry)); + std::unique_ptr geometry(readFBX(blob, QVariantHash())); + std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index a52e948f01..8de9c39da9 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -19,16 +19,16 @@ // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. -bool FBXGeometryLessThan(const FBXMesh& e1, const FBXMesh& e2) { +bool HFMModelLessThan(const HFMMesh& e1, const HFMMesh& e2) { return e1.meshIndex < e2.meshIndex; } -void reSortFBXGeometryMeshes(FBXGeometry& geometry) { - qSort(geometry.meshes.begin(), geometry.meshes.end(), FBXGeometryLessThan); +void reSortHFMModelMeshes(HFMModel& hfmModel) { + qSort(hfmModel.meshes.begin(), hfmModel.meshes.end(), HFMModelLessThan); } // Read all the meshes from provided FBX file -bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { +bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMModel& result) { if (_verbose) { qDebug() << "reading FBX file =" << filename << "..."; } @@ -41,19 +41,19 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } try { QByteArray fbxContents = fbx.readAll(); - FBXGeometry::Pointer geom; + HFMModel::Pointer hfmModel; if (filename.toLower().endsWith(".obj")) { bool combineParts = false; - geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); + hfmModel = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); } else if (filename.toLower().endsWith(".fbx")) { - geom.reset(readFBX(fbxContents, QVariantHash(), filename)); + hfmModel.reset(readFBX(fbxContents, QVariantHash(), filename)); } else { qWarning() << "file has unknown extension" << filename; return false; } - result = *geom; + result = *hfmModel; - reSortFBXGeometryMeshes(result); + reSortHFMModelMeshes(result); } catch (const QString& error) { qWarning() << "error reading" << filename << ":" << error; return false; @@ -63,7 +63,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } -void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& triangleIndices) { +void getTrianglesInMeshPart(const HFMMeshPart &meshPart, std::vector& triangleIndices) { // append triangle indices triangleIndices.reserve(triangleIndices.size() + (size_t)meshPart.triangleIndices.size()); for (auto index : meshPart.triangleIndices) { @@ -88,12 +88,12 @@ void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& trian } } -void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometryOffset, FBXMesh& result) const { +void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOffset, HFMMesh& result) const { // this is used to make meshes generated from a highfield collidable. each triangle // is converted into a tetrahedron and made into its own mesh-part. std::vector triangleIndices; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { getTrianglesInMeshPart(meshPart, triangleIndices); } @@ -104,7 +104,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry int indexStartOffset = result.vertices.size(); // new mesh gets the transformed points from the original - glm::mat4 totalTransform = geometryOffset * mesh.modelTransform; + glm::mat4 totalTransform = modelOffset * mesh.modelTransform; for (int i = 0; i < mesh.vertices.size(); i++) { // apply the source mesh's transform to the points glm::vec4 v = totalTransform * glm::vec4(mesh.vertices[i], 1.0f); @@ -145,7 +145,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh - FBXMeshPart newMeshPart; + HFMMeshPart newMeshPart; setMeshPartDefaults(newMeshPart, "unknown"); newMeshPart.triangleIndices << index0 << index1 << index2; newMeshPart.triangleIndices << index0 << index3 << index1; @@ -155,7 +155,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry } } -AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) { +AABox getAABoxForMeshPart(const HFMMesh& mesh, const HFMMeshPart &meshPart) { AABox aaBox; const int TRIANGLE_STRIDE = 3; for (int i = 0; i < meshPart.triangleIndices.size(); i += TRIANGLE_STRIDE) { @@ -242,7 +242,7 @@ bool isClosedManifold(const std::vector& triangleIndices) { return true; } -void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const { +void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const { // Number of hulls for this input meshPart uint32_t numHulls = convexifier->GetNConvexHulls(); if (_verbose) { @@ -256,8 +256,8 @@ void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& res VHACD::IVHACD::ConvexHull hull; convexifier->GetConvexHull(j, hull); - resultMesh.parts.append(FBXMeshPart()); - FBXMeshPart& resultMeshPart = resultMesh.parts.last(); + resultMesh.parts.append(HFMMeshPart()); + HFMMeshPart& resultMeshPart = resultMesh.parts.last(); int hullIndexStart = resultMesh.vertices.size(); resultMesh.vertices.reserve(hullIndexStart + hull.m_nPoints); @@ -288,17 +288,17 @@ float computeDt(uint64_t start) { return (float)(usecTimestampNow() - start) / (float)USECS_PER_SECOND; } -bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, +bool vhacd::VHACDUtil::computeVHACD(HFMModel& hfmModel, VHACD::IVHACD::Parameters params, - FBXGeometry& result, + HFMModel& result, float minimumMeshSize, float maximumMeshSize) { if (_verbose) { - qDebug() << "meshes =" << geometry.meshes.size(); + qDebug() << "meshes =" << hfmModel.meshes.size(); } // count the mesh-parts int numParts = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { numParts += mesh.parts.size(); } if (_verbose) { @@ -308,15 +308,15 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, VHACD::IVHACD * convexifier = VHACD::CreateVHACD(); result.meshExtents.reset(); - result.meshes.append(FBXMesh()); - FBXMesh &resultMesh = result.meshes.last(); + result.meshes.append(HFMMesh()); + HFMMesh &resultMesh = result.meshes.last(); const uint32_t POINT_STRIDE = 3; const uint32_t TRIANGLE_STRIDE = 3; int meshIndex = 0; int validPartsFound = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { // find duplicate points int numDupes = 0; @@ -337,7 +337,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, // each mesh has its own transform to move it to model-space std::vector vertices; - glm::mat4 totalTransform = geometry.offset * mesh.modelTransform; + glm::mat4 totalTransform = hfmModel.offset * mesh.modelTransform; foreach (glm::vec3 vertex, mesh.vertices) { vertices.push_back(glm::vec3(totalTransform * glm::vec4(vertex, 1.0f))); } @@ -354,7 +354,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, int partIndex = 0; std::vector triangleIndices; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { triangleIndices.clear(); getTrianglesInMeshPart(meshPart, triangleIndices); @@ -421,7 +421,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, triangleIndices.clear(); for (auto index : openParts) { - const FBXMeshPart &meshPart = mesh.parts[index]; + const HFMMeshPart &meshPart = mesh.parts[index]; getTrianglesInMeshPart(meshPart, triangleIndices); } diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 35ec3ef56b..dd8f606756 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -27,16 +27,16 @@ namespace vhacd { public: void setVerbose(bool verbose) { _verbose = verbose; } - bool loadFBX(const QString filename, FBXGeometry& result); + bool loadFBX(const QString filename, HFMModel& result); - void fattenMesh(const FBXMesh& mesh, const glm::mat4& gometryOffset, FBXMesh& result) const; + void fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOffset, HFMMesh& result) const; - bool computeVHACD(FBXGeometry& geometry, + bool computeVHACD(HFMModel& hfmModel, VHACD::IVHACD::Parameters params, - FBXGeometry& result, + HFMModel& result, float minimumMeshSize, float maximumMeshSize); - void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const; + void getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const; ~VHACDUtil(); @@ -55,6 +55,6 @@ namespace vhacd { }; } -AABox getAABoxForMeshPart(const FBXMeshPart &meshPart); +AABox getAABoxForMeshPart(const HFMMeshPart &meshPart); #endif //hifi_VHACDUtil_h diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index c263dce609..3d675f8baf 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -36,7 +36,7 @@ QString formatFloat(double n) { } -bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) { +bool VHACDUtilApp::writeOBJ(QString outFileName, HFMModel& hfmModel, bool outputCentimeters, int whichMeshPart) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "unable to write to" << outFileName; @@ -56,9 +56,9 @@ bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool out int vertexIndexOffset = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { bool verticesHaveBeenOutput = false; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { nth++; continue; @@ -297,7 +297,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } // load the mesh - FBXGeometry fbx; + HFMModel fbx; auto begin = std::chrono::high_resolution_clock::now(); if (!vUtil.loadFBX(inputFilename, fbx)){ _returnCode = VHACD_RETURN_CODE_FAILURE_TO_READ; @@ -315,8 +315,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : QVector infileExtensions = {"fbx", "obj"}; QString baseFileName = fileNameWithoutExtension(outputFilename, infileExtensions); int count = 0; - foreach (const FBXMesh& mesh, fbx.meshes) { - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMesh& mesh, fbx.meshes) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj"; writeOBJ(outputFileName, fbx, outputCentimeters, count); count++; @@ -358,7 +358,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } begin = std::chrono::high_resolution_clock::now(); - FBXGeometry result; + HFMModel result; bool success = vUtil.computeVHACD(fbx, params, result, minimumMeshSize, maximumMeshSize); end = std::chrono::high_resolution_clock::now(); @@ -377,9 +377,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : int totalVertices = 0; int totalTriangles = 0; - foreach (const FBXMesh& mesh, result.meshes) { + foreach (const HFMMesh& mesh, result.meshes) { totalVertices += mesh.vertices.size(); - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { totalTriangles += meshPart.triangleIndices.size() / 3; // each quad was made into two triangles totalTriangles += 2 * meshPart.quadIndices.size() / 4; @@ -398,17 +398,17 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } if (fattenFaces) { - FBXGeometry newFbx; - FBXMesh result; + HFMModel newFbx; + HFMMesh result; // count the mesh-parts unsigned int meshCount = 0; - foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const HFMMesh& mesh, fbx.meshes) { meshCount += mesh.parts.size(); } result.modelTransform = glm::mat4(); // Identity matrix - foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const HFMMesh& mesh, fbx.meshes) { vUtil.fattenMesh(mesh, fbx.offset, result); } diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index 0d75275802..7dadad20b3 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -28,7 +28,7 @@ public: VHACDUtilApp(int argc, char* argv[]); ~VHACDUtilApp(); - bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1); + bool writeOBJ(QString outFileName, HFMModel& hfmModel, bool outputCentimeters, int whichMeshPart = -1); int getReturnCode() const { return _returnCode; } diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 8e49589e3c..4786356fee 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -395,7 +395,7 @@ Script.clearInterval(updateTimer); Messages.messageReceived.disconnect(onMessageReceived); - Messages.subscribe(HIFI_RECORDER_CHANNEL); + Messages.unsubscribe(HIFI_RECORDER_CHANNEL); } return { diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml index 033039b87d..753771987a 100644 --- a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -16,8 +16,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls import "qrc:////qml//hifi" as Hifi